Réinventer la conception du système pour Scala


35

Il y a beaucoup de lunes, j'ai effectué ma maîtrise en génie logiciel orienté objet. Je couvrais tout: initiation du projet, exigences, analyse, conception, architecture, développement, etc. Mon livre informatique préféré de tous les temps était Développer des logiciels orientés objet, une approche basée sur l'expérience (IBM-1996). Un livre créé par un groupe de vrais experts de leur temps. Il décrit une approche centrée sur le produit du travail pour les méthodes d'analyse, de conception et de développement orientées objet.

J'ai conçu et développé et étais heureux et au sommet de mon jeu, mais je commençais à me sentir un peu vieillot: les mouvements agiles sont devenus la mode du jour et remodèlent certaines approches itératives et incrémentales bien connues avec de nouveaux mots branchés. Soudain, des développeurs inexpérimentés ont commencé à froncer les sourcils quand j'ai dit «exigences» ou «architecture» comme si ces choses étaient remplacées par de la magie.

La conception et le développement ont perdu de leur intérêt et j'étais sur le point de laisser toute l'industrie informatique derrière moi.

Puis j'ai découvert Scala. Oh, comme une pluie battante sur une route poussiéreuse. Tout devenait clair, l'air redevenait doux et la petite lumière de mon disque dur s'éteignait au plus profond de la nuit, me tenant joyeusement compagnie dans mon monde de découvertes.

J'aime la conception du système. Je suis un architecte dans l'âme, plus qu'un programmeur. J'aime analyser, concevoir, penser, discuter, améliorer - j'adore les dessins simples, nets et nets.

Comment concevons-nous des solutions Scala pures?

Certes, les diagrammes de séquence, les diagrammes d’interaction, les diagrammes d’objets, etc. conventionnels pourraient tous être remplacés ou améliorés pour rendre plus sains les systèmes de fusion des systèmes impératifs et fonctionnels. Il y a sûrement une ouverture pour quelque chose de totalement différent!

Je ne recherche pas une complexité exagérée - je recherche une simplicité flexible. Tout comme Scala, c’est en fait simple et facile (bien que différent), mais il peut être étendu selon vos besoins, comme un pantalon de yoga. Il doit exister un système de conception pour ce beau langage qui débutera facilement et pourra être étendu pour répondre à vos besoins et à votre domaine. UML ne peut sûrement pas en être ainsi!

Alors, comment concevons-nous des systèmes Scala purs? Imaginez un instant que vous avez le luxe de concevoir un système complet à partir de zéro et que vous n’utiliserez que Scala - À quoi ressembleront vos modèles? Quels types de diagrammes faudrait-il pour décrire la structure et le comportement? Comment modéliseriez-vous les options, correspondances, combinaisons, objets singleton, etc. sans vous laisser entraîner par la complexité d'extension des techniques de modélisation existantes plutôt que par un ensemble d'outils innovants, légers et légers?

Une telle conception, un processus ou une solution existe -t-il ou est-il temps d'en inventer un?


+1 pour "peut être étendu à votre convenance, comme un pantalon de yoga."
Russell

FWIW, j’ai vu une question sur UML et Scala qui pourrait vous intéresser. De plus, si vous passez du côté de la fonction, alors les notations de la théorie des catégories pourraient être intéressantes. Gregory Meredith a généralement des liens vers des présentations intéressantes à ce sujet, bien que je ne sois pas sûr de ce qu’il a sur son blog.
Daniel C. Sobral le

Cela en vaut la peine ;-) Cela me donne la direction de creuser. Merci Daniel
Jack

À vérifier, l'UML "modifié" pour Scala, github.com/mnn/Dia2Scala/blob/master/other/Notation.md
WeiChing Lin

Réponses:


12

Je ne peux pas vraiment répondre à cette question, mais laissez-moi vous donner quelques réflexions.

Je suis un étudiant en ingénierie informatique dans une université et le semestre dernier, un groupe de projet a réalisé un grand projet informatique dans lequel notre langue principale était Scala. Nous avons conçu nos méthodes de manière traditionnelle: cas d'utilisation, modèle de domaine, diagrammes de séquence, diagrammes de classe UML; la charge habituelle. Et, comme vous le savez déjà, ils conviennent mal. Voici quelques raisons.

Premièrement, considérons les diagrammes de séquence. La sensation d'un diagramme de séquence est celle de quelques acteurs stables, de longue durée et semi-autonomes qui se parlent. Dans Scala, les objets ont tendance à être créés pendant une courte période. En outre, les classes de cas encouragent les objets "stupides", avec uniquement des données et aucun code, de sorte que le concept de transmission de messages est déplacé. Mais le défi le plus difficile à relever pour les diagrammes de séquence est qu’ils n’ont aucun moyen d’intégrer des fonctions de premier ordre; dans une conception OO utilisant uniquement un rappel ici ou là, cela peut fonctionner, mais si c'est votre principal moyen de transférer le contrôle, vous constaterez que le diagramme de séquence ne reflète que très peu de votre code réel.

Les diagrammes de classes UML sont plus sensibles à Scala; oui, les mixins ne passent pas bien, mais c'est quelque chose qui pourrait être "peaufiné" plutôt que révisé. Mais je pense que cela manque peut-être.

Réfléchissez à nouveau pourquoi Scala a des "objets stupides". Si vous écrivez une classe de cas sans méthode:

case class NewInvite(user: User, league: League)

vous n'avez besoin d' aucune méthode, car les types de membres promettent quelque chose sur ce que vous pouvez en faire. En fait, ils promettent tout . Et, dans tous les domaines du programme, les variables et leurs types promettent quelque chose sur la direction que peut prendre le code à ce stade.

Donc, peut-être, si nous reculons un peu et, au lieu de penser minutieusement aux types en tant que tels, concevons notre programme en fonction des contextes dans lesquels notre code vivra et de ce qui est promis au programmeur (et, éventuellement, à l'utilisateur). ) Dans chacun de ces contextes, nous trouverons une description plus appropriée à Scala. Cependant, je ne fais que deviner, et je pense que vous avez raison, il reste encore beaucoup à faire dans ce domaine.


"Ce qui est promis au programmeur" - ça sonne bien ;-)
Jack

12

UML est sorti des «guerres à bulles» de la fin des années 80 et du début des années 90. C'était toujours un compromis pour la notation , pas une méthode de conception. Beaucoup des calomnies attribuées à UML sont secrètement le résultat d'une méthode de conception qui dit "Étape 1: dessinez un modèle de classe".

Je suggère que nous n’avions pas besoin de nouvelles méthodes de conception autant que de revitaliser certaines anciennes. Commencez par définir et attribuer les responsabilités, puis comprenez leurs applications et suivez-les avec de bonnes notations et représentations pour communiquer ces idées.

Responsabilités

CRC fonctionne aussi bien pour Scala que pour n’importe quel autre langage OO, c’est-à-dire que cela fonctionne très bien! Modéliser la répartition des responsabilités en anthropomorphisant les acteurs et en comprenant leurs collaborations fonctionne toujours bien.

À grande échelle, vous devriez toujours concevoir avec des objets. Les limites d'objets sont des limites d'encapsulation. Conservez les détails d'état et d'implémentation mutables à l'intérieur de ces limites d'objet. Les classes de cas font de bons objets d'interface, tout comme les collections et les structures de données vanilla.

Comprendre les implémentations

Passez ces structures de données à travers les limites des objets plutôt que d’autres objets. Vous constaterez a) qu’il est plus facile de cacher les entrailles d’un objet et b) que les implémentations fonctionnelles des comportements d’objet ont beaucoup de sens.

Vous ne voudrez pas traiter la structure à grande échelle de votre programme comme "un vaste pool de petites fonctions". Au lieu de cela, vous vous dirigerez naturellement vers des interfaces étroites entre des parties isolées du système. De toute façon, ces objets ressemblent beaucoup à des objets, alors n'hésitez plus et implémentez-les en tant qu'objets!

Représentation et notation

Ainsi, dans cette structure de conception, vous devez toujours vous débarrasser de vos pensées et les partager avec quelqu'un d'autre.

Je suggère qu'UML peut encore être utile pour décrire la structure à grande échelle de votre système. Cela n’aidera pas aux niveaux de détail inférieurs, cependant.

Au grain de détail, essayez des techniques de programmation alphabétisées.

J'aime aussi utiliser les minidocs. Un minidoc est un document d'une ou deux pages décrivant une facette spécifique du comportement du système. Il devrait être placé à proximité du code pour qu'il soit mis à jour et suffisamment court pour permettre un apprentissage juste à temps. Il faut un peu de pratique pour atteindre le bon niveau d'abstraction: il doit être suffisamment spécifique pour être utile, mais pas si spécifique qu'il soit invalidé par le prochain changement de code.


9

Comment concevons-nous des solutions Scala pures?

Je pense que vous prenez le mauvais angle avec cette question. Votre conception globale des solutions ne doit pas être liée à une langue particulière; cela devrait plutôt correspondre au problème à résoudre. L'avantage de Scala est qu'il est suffisamment flexible pour ne pas vous gêner lorsque vient le temps d'implémenter votre conception. Java, en revanche, impose certaines décisions de conception (principalement la mentalité de tout-est-une-classe) qui le rendra un peu maladroit.

Lors de la conception, essayez de modéliser explicitement l'état . Les données immuables et les états explicites mènent à des conceptions beaucoup plus faciles à raisonner. La plupart du temps, cela se traduira par une solution de programmation fonctionnelle, même si vous constaterez parfois qu’il est plus efficace ou plus simple d’utiliser la mutation et un style impératif; Scala accueille très bien les deux.

Une autre raison pour laquelle Scala est si doué pour vous sortir de votre chemin est sa capacité à créer des DSL qui répondent à vos besoins . Vous pouvez créer votre propre petit langage qui résout un problème de manière "normale", puis l’appliquer simplement dans Scala.

En résumé, mon point principal est qu'il ne faut pas essayer de "concevoir des solutions Scala pures". Vous devez concevoir des solutions de la manière la plus naturelle et la plus compréhensible possible. En effet, Scala est un outil extrêmement flexible qui peut vous aider à mettre en œuvre ces solutions de différentes manières. Lorsque tout ce que vous savez sur un marteau, chaque problème commence à ressembler à un clou, alors assurez-vous d'explorer les différentes approches qui s'offrent à vous. N'essayez pas de fermer le processus de conception aux étapes X, Y et Z, de peur de manquer les possibilités de A à W.


4

Vous avez raison, OOD est limité et obsolète. Son principal avantage est qu’il est si bien spécifié et ritualisé, avec des noms fantaisistes pour chaque motif trivial et chaque astuce de conception. Vous ne trouverez pas de système comparable pour les approches plus modernes et rationnelles de la conception de systèmes.

Je ne peux énumérer que quelques éléments: une approche sémantique de la conception, dont un sous-ensemble s'appelle Programmation orientée vers le langage (et largement utilisée par la communauté Scala) et Conception orientée domaine. Ce dernier est une vue simplifiée et optimisée d'une approche plus générique de la conception sémantique, vous ne pourrez en profiter que si vous aimez OOD.

Je recommanderais également d'apprendre quelques astuces des autres communautés de programmation fonctionnelles - par exemple, Haskell et Scheme, tout leur savoir-faire en matière de conception n'avait pas déjà été transféré à Scala.


3

La question était: comment concevons-nous des solutions Scala pures?

Des réponses comme

  1. Nous utilisons XYZ parce que ....
  2. Nous utilisons ABC mais comme alors ....
  3. Nous avons commencé à utiliser XYZ, mais nous avons trouvé ABC meilleur car ...

aurait indiqué une certaine maturité ou l'existence généralement acceptée d'un tel ensemble d'outils ou d'une telle solution.

Les commentaires relatifs à l'opportunité de considérer un ensemble d'outils de conception spécifiques à Scala sont une autre affaire. Mon opinion à ce sujet est que Scala est révolutionnaire dans la façon dont elle combine la programmation impérative et fonctionnelle. Scala capte un souffle incroyable de pratiques, de concepts et de principes de développement. Ainsi, en trouvant une solution parfaitement adaptée à Scala, nous découvrirons probablement une solution pour laquelle un sous-ensemble servira également très bien de nombreuses autres langues.

Est-il prudent de dire alors que nous connaissons la réponse à la question de repli:

"... est-il temps d'en inventer un?"

Pourrait-il être oui ? (Où « oui » ne prescrit pas le moyen de la solution. Cela peut être réalisé soit en étendant une solution existante, soit en en créant une à partir de la base. Cela admet cependant que les options de conception existantes présentent des lacunes importantes.)

Vous êtes invités à voter ma réponse si vous êtes en désaccord. Je vais le prendre comme un homme.


Révolutionnaire? Pourquoi? Lisp était une combinaison parfaite d’approches impératives et fonctionnelles depuis des décennies. Scala est intéressant en raison de son système de type.
SK-logic

3
Excuses si «révolutionnaire» est un mot fort fort. Je ne veux pas dire que Scala a réinventé la roue - mais elle a certainement remodelé le caoutchouc. En fait, Scala est un beau mélange de tous les avantages de nombreux langages de programmation. Je suis sûr que cela emprunte également beaucoup à Lisp. Pour moi, et je ne revendique rien pour les autres, le langage Scala est révolutionnaire en ce sens que je pense au code d’une manière qui vient naturellement. Un langage de conception / modélisation qui fait la même chose serait certainement complémentaire. C'était mon seul point - ne pas offenser Lisp et toutes les autres grandes langues.
Jack

2

Je ne ferais pas la différence entre les langues, je le ferais de la même manière en Java. Cependant, je suis de l’école OO plutôt que de la fonctionnelle (algèbre de Haskell ou lisp). La conception de l'application Haskell est très différente de Scala ou de Clojure. De plus, la conception d'une application Scala.js est différente en raison du DOM stateful - il est difficile de lutter contre une partie stateful de votre application qui est obligatoire. Au fil du temps, je me suis habitué à le faire de manière OO, tout en essayant d'éliminer autant que possible l'état et la mutabilité. Si j'étais un fan de typelevel ou de scalaz, mes applications seraient probablement très différentes.

  1. Décider de la pile technologique et des principales décisions de conception telles que la responsabilité client / serveur, la nature distribuée - avons-nous vraiment besoin de la persistance des données? Qu'est-ce qui convient le mieux à notre application? (certainement pas encore les bibliothèques ou les frameworks)

  2. En commençant avec un seul App.scalafichier dans lequel je définis les types de clés (traits et classes de cas) et les rôles - beaucoup de réflexion et de réflexion en étant AFK. Il est si facile de jouer avec cela quand il est dans un fichier. Même le prototype pourrait voir le jour s'il s'agit plutôt d'une application simple. Je consacre beaucoup de temps à cette phase car rien n'est plus important que de disposer du prototype le plus correct, le plus transparent et le plus sensible possible. Tout cela contribue à rendre notre réflexion plus précise plus précise et augmente les chances de succès.

  3. Maintenant que nous avons prouvé que notre solution est correcte, que nous avons une vision claire et que tous les problèmes potentiels sont révélés, il est temps de réfléchir au type de bibliothèques ou de méthodologies tierces à importer. Avons-nous besoin d'un framework de jeu ou de quelques bibliothèques seulement? Est-ce que nous allons faire de ces acteurs, avons-nous vraiment besoin de la résilience d'Akka, .., .. ou peut-être pas? Utilisons-nous Rx pour ces flux? Que utilisons-nous pour l'interface utilisateur afin de ne pas devenir fous une fois que celle-ci aura 10 fonctions interactives?

  4. divisez le App.scalafichier en plusieurs fichiers car de nouveaux traits et classes sont apparus et ont grossi. Leur interface devrait parler de leurs rôles. Maintenant, il est également temps de penser à utiliser des classes de types et un polymorphisme ad hoc si possible. Pour que quiconque appelle votre interface, il doit être flexible pour eux. Le but est d'implémenter uniquement les fonctionnalités générales, en laissant les détails à l'appelant. Parfois, ce que vous essayez d'implémenter peut ressembler à Turing-complete. Vous devez donc fournir aux appelants un moyen d'implémenter les spécificités eux-mêmes au lieu d'empiler les demandes de fonctionnalités sur GitHub. Ce truc est difficile à ajouter plus tard. Il faut y penser au tout début. Ainsi que la conception d'un DSL.

  5. en pensant au design parce que l'application va croître et qu'elle doit être prête à supporter de nouvelles fonctionnalités, optimisations, persistance, interface utilisateur, communication à distance, etc. Rappelez-vous que même si tout est dans un fichier, il est assez facile de changer les choses. Le cerveau humain commence à le perdre après avoir été réparti sur plus de 10 fichiers. Et c'est comme une tumeur qui se développe, c'est comme ça que commence l'ingénierie. J'ai remarqué que mes applications se retrouvent avec quelques longues fonctions partielles qui en forment une sorte de colonne vertébrale. Je trouve facile de travailler avec. Je suppose que je suis devenu infecté par des acteurs Akka.

  6. Maintenant que les premières fonctionnalités existent, et pas seulement le fonctionnement de base, il est temps de commencer à écrire des tests. Pourquoi si tard? Parce que pendant ce temps, vous avez probablement fait des réécritures majeures et vous auriez perdu du temps à maintenir ces tests. On ne devrait pas écrire de tests à moins d'être vraiment sûr qu'il n'y aura pas de refonte majeure. Je préfère les tests d'intégration qui résistent facilement à la refactorisation continue et je ne passerai pas plus de temps à tester ce genre de choses en développement. Il m'est arrivé à plusieurs reprises de passer beaucoup trop de temps à effectuer des tests. Corriger les bugs potentiels serait certainement plus rentable que de mauvais tests. De mauvais tests ou des tests écrits dès le premier moment peuvent causer une douleur majeure. Notez que je ne suis certainement pas un fan de TDD - IMHO, c'est des conneries.

  7. Refactoriser, rendre et garder la conception de l'application lisible, les rôles des composants clairement définis. Au fur et à mesure que l'application grandit, elle ne le restera pas sauf si vous êtes un programmeur de génie et si vous lisez cette réponse, vous ne serez probablement pas :-) Moi non plus. cela - cela pourrait causer des problèmes, essayez de les éliminer. Veillez également à éliminer les odeurs de code qui vous gênent depuis un certain temps. À ce stade, votre chef de projet (si vous en avez un) commence probablement à secouer la tête. Dis-lui que ça va rapporter.

  8. Fait, l'application est-elle bien conçue? Cela dépend: les composants ont-ils des rôles bien définis qui ont un sens, même en dehors de votre tête? Pourriez-vous en prendre quelques-unes et les ouvrir en source sans trop d'effort? Y a-t-il une séparation claire des préoccupations entre eux? Pouvez-vous ajouter une nouvelle fonctionnalité sans craindre qu'il se bloque ailleurs (pour indiquer que vous savez exactement ce que vous faites)? De manière générale, il devrait être lisible comme un livre avec des solutions pragmatiques simples aux problèmes. Ce qui est à mon humble avis le signe majeur d’un bon design.

Dans l’ensemble, c’est un travail ardu et du temps que vos gestionnaires n’auront probablement pas. Tout ce que je décris ici prend beaucoup de temps, d’efforts, de café, de thé et surtout de réflexion à AFK. Plus vous en donnez, meilleur sera le design. Il n’existe pas de recette générale pour la conception d’applications, de bon sens, de pragmatisme, de KISS, de travail acharné, l’expression signifie une bonne conception.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.