Reinventare la progettazione del sistema per Scala


35

Molte, molte lune fa, ho fatto i miei master in ingegneria del software orientata agli oggetti. Ho trattato di tutto: avvio del progetto, requisiti, analisi, progettazione, architettura, sviluppo, ecc. Ecc. Il mio libro IT preferito di tutti i tempi era lo sviluppo di software orientato agli oggetti, un approccio basato sull'esperienza (IBM-1996). Un libro creato da un gruppo di veri esperti del loro tempo. Descrive un approccio incentrato sul prodotto di lavoro all'analisi orientata agli oggetti, ai metodi di progettazione e sviluppo.

Ho progettato e sviluppato ed ero felice e al top del mio gioco, ma ho iniziato a sentirmi un po 'datato: i movimenti agili sono diventati la moda del giorno e hanno marchiato alcuni noti approcci iterativi e incrementali con nuove parole alla moda. Gli sviluppatori improvvisamente inesperti hanno iniziato ad aggrottare le sopracciglia quando ho detto "requisiti" o "architettura" come se queste cose fossero sostituite dalla magia.

Il design e lo sviluppo hanno perso il divertimento e stavo per lasciarmi alle spalle l'intero settore IT.

Poi ho scoperto Scala. Oh, come una leggera pioggia su una strada polverosa. Tutto è appena diventato chiaro, l'aria è tornata ad essere dolce e la poca luce del mio disco fisso si sarebbe illuminata in profondità nella notte, facendomi allegramente compagnia nel mio mondo di scoperte.

Mi piace la progettazione di sistemi. Sono un architetto nel cuore, più che un programmatore. Adoro analizzare, progettare, pensare, discutere, migliorare: adoro i disegni semplici, puliti e nitidi.

Come progettiamo soluzioni Scala pure?

Sicuramente i diagrammi di sequenza convenzionali, i diagrammi di interazione, i diagrammi di oggetti, ecc., Potrebbero tutti essere sostituiti o migliorati per la bontà di fondere sistemi imperativi e orientati agli oggetti funzionali. Sicuramente c'è un'apertura per qualcosa di totalmente diverso!

Non cerco complessità gonfia, cerco semplicità flessibile. Proprio come Scala è in realtà semplice e facile (anche se diverso), ma può essere esteso per adattarsi alle tue esigenze come un paio di pantaloni Yoga. Ci deve essere un sistema di progettazione per questo adorabile linguaggio che inizierà in modo semplice e che possa essere esteso alle tue esigenze e al tuo dominio. Sicuramente UML non può essere!

Quindi, come progettiamo sistemi Scala puri? Immagina per un momento, hai il lusso di progettare da zero un sistema completo e sai che utilizzerai solo Scala - Come appariranno i tuoi modelli? Quali tipi di diagrammi dovresti descrivere la struttura e il comportamento? Come modelleresti le opzioni, le partite, i mixin, gli oggetti singleton, ecc. Senza essere attirato dalla complessità dell'estensione delle tecniche di modellazione esistenti piuttosto che da un set di strumenti fresco, leggero e innovativo.

Esiste un tale progetto / processo / soluzione o è il momento di inventarne uno?


+1 per "può essere esteso per adattarsi alle tue esigenze come un paio di pantaloni Yoga".
Russell,

FWIW, ho visto una domanda su UML e Scala, che potrebbe interessarti. Inoltre, se vai sul lato della funzione, vale la pena esaminare le notazioni di teoria delle categorie. Gregory Meredith di solito si collega ad alcune presentazioni interessanti al riguardo, anche se non sono sicuro di ciò che ha nel suo blog - vale la pena dare un'occhiata comunque.
Daniel C. Sobral,

Vale molto ;-) Mi dà la direzione di scavare. Grazie Daniel
Jack,

Vale la pena controllare, l'UML "modificato" per Scala, github.com/mnn/Dia2Scala/blob/master/other/Notation.md
WeiChing Lin,

Risposte:


12

Non posso davvero dare una risposta a questa domanda, ma lasciatemi offrire alcuni pensieri.

Sono uno studente di ingegneria informatica in un'università e lo scorso semestre io e un gruppo abbiamo realizzato un grande progetto software in cui la nostra lingua principale era Scala. Abbiamo fatto il nostro design nel modo tradizionale: casi d'uso, un modello di dominio, diagrammi di sequenza, diagrammi di classe UML; il solito carico. E, come già sapete, sono inadeguati. Ecco alcuni motivi.

Innanzitutto, considera i diagrammi di sequenza. La sensazione di un diagramma di sequenza è di alcuni attori stabili, di lunga durata, semi-autonomi che parlano tra loro. Alla Scala, gli oggetti tendono ad essere creati per un breve periodo. Inoltre le classi di casi incoraggiano oggetti "stupidi", con solo dati e nessun codice, quindi il concetto di passaggio dei messaggi è fuori posto. Ma la sfida più difficile per i diagrammi di sequenza è che non hanno un buon modo per incorporare funzioni di prima classe; in un progetto OO usando solo un callback qui o là può essere fatto funzionare, ma se questo è il tuo mezzo principale per trasferire il controllo troverai che il diagramma di sequenza riflette poco del tuo codice reale.

I diagrammi di classe UML sono più suscettibili a Scala; sì, i mixin non arrivano bene, ma è qualcosa che potrebbe essere "ottimizzato" piuttosto che revisionato. Ma penso che potrebbe mancare il punto.

Considera di nuovo perché Scala ha "oggetti stupidi". Se scrivi una classe di casi senza metodi:

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

non hai bisogno di alcun metodo, perché i tipi di membri promettono qualcosa su cosa puoi fare con loro. In realtà, promettono tutto . E, in ogni ambito all'interno del programma, le variabili nell'ambito e i loro tipi promettono qualcosa su dove il codice può andare in quel punto.

Quindi, forse, se facciamo un passo indietro e, invece di pensare minuziosamente ai tipi in sé, progettiamo il nostro programma in termini di contesti in cui vivrà il nostro codice e di cosa è promesso al programmatore (e, infine, all'utente ) in ciascuno di questi contesti, troveremo una descrizione più appropriata per Scala. Tuttavia, sto solo indovinando qui e penso che tu abbia ragione che molto altro deve essere esplorato in questo settore.


"Ciò che è promesso al programmatore" - che ha un suono delizioso ;-)
Jack

12

UML è emerso dalle "Bubble Wars" della fine degli anni '80 e dei primi anni '90. È sempre stato un compromesso per la notazione , non un metodo di progettazione. Molte delle calunnie attribuite a UML sono segretamente il risultato di un metodo di progettazione che dice "Passaggio 1: disegnare un modello di classe".

Suggerisco che non abbiamo bisogno di nuovi metodi di progettazione tanto quanto dovremmo rivitalizzare alcuni vecchi. Inizia con la definizione e l'allocazione delle responsabilità, poi passa alla comprensione delle loro implementazioni e segui con buone notazioni e rappresentazioni per comunicare quelle idee.

responsabilità

CRC funziona anche per Scala come per qualsiasi altro linguaggio OO, vale a dire che funziona molto bene! Modellare l'assegnazione delle responsabilità da parte degli attori antropomorfici e comprendere le loro collaborazioni funziona ancora bene.

Su larga scala, dovresti comunque progettare con oggetti. I confini degli oggetti sono confini di incapsulamento. Mantieni lo stato mutevole e i dettagli di implementazione all'interno di tali confini degli oggetti. Le classi di casi creano buoni oggetti di interfaccia, così come le raccolte di vaniglia e le strutture di dati.

Comprensione delle implementazioni

Passa queste strutture di dati oltre i confini degli oggetti piuttosto che altri oggetti e troverai a) è più facile tenere nascoste le viscere di un oggetto, e b) le implementazioni funzionali dei comportamenti degli oggetti hanno molto senso.

Non vorrai trattare la struttura su larga scala del tuo programma come "un vasto pool di piccole funzioni". Invece, graviterai naturalmente verso interfacce strette tra parti isolate del sistema. Questi sembrano e somigliano molto agli oggetti, quindi vai avanti e implementali come oggetti!

Rappresentazione e notazione

Quindi, all'interno di questa struttura di design, devi ancora toglierti i pensieri dalla testa e condividerli con qualcun altro.

Suggerisco che UML possa ancora essere utile per descrivere la struttura su larga scala del sistema. Tuttavia non aiuterà ai livelli più bassi di dettaglio.

Alla fine dei dettagli, prova tecniche di programmazione letteraria.

Mi piace anche usare minidocs. Un minidoc è un documento di una o due pagine che descrive alcuni aspetti specifici del comportamento del sistema. Dovrebbe trovarsi vicino al codice in modo da essere aggiornato e dovrebbe essere abbastanza breve da consentire l'apprendimento just-in-time. Ci vuole un po 'di pratica per raggiungere il giusto livello di astrazione: deve essere abbastanza specifico per essere utile ma non così specifico da essere invalidato dalla successiva modifica del codice.


9

Come progettiamo soluzioni Scala pure?

Penso che stai prendendo l'angolazione sbagliata con questa domanda. Il tuo progetto complessivo di soluzioni non dovrebbe essere legato a un linguaggio particolare; piuttosto, dovrebbe corrispondere al problema attuale. Il bello di Scala è che è abbastanza flessibile da non ostacolarti quando arriva il momento di implementare il tuo design. Java, d'altra parte, impone alcune decisioni di progettazione (principalmente, la mentalità di ogni classe) che la farà sentire un po 'goffa.

Durante la progettazione, provare a modellare lo stato in modo esplicito . Dati immutabili e stato esplicito portano a progetti che sono molto più facili da ragionare. Il più delle volte, questo si tradurrà piacevolmente in una soluzione di programmazione funzionale, anche se a volte potresti scoprire che è più efficiente o più semplice usare la mutazione e uno stile imperativo; Scala accoglie entrambi abbastanza bene.

Un altro motivo per cui Scala è così brava a togliersi di mezzo è la sua capacità di creare DSL adatti alle tue esigenze . Puoi creare il tuo piccolo linguaggio che risolva un certo problema nel modo "normale", e quindi semplicemente implementarlo in Scala.

In sintesi, il mio punto principale è che non dovresti provare a "progettare soluzioni Scala pure". Dovresti progettare soluzioni nel modo più naturale e comprensibile possibile, e così succede che Scala è uno strumento incredibilmente flessibile che può aiutarti a implementare tali soluzioni in vari modi. Quando tutto ciò che sai è un martello, ogni problema inizia a sembrare un chiodo, quindi assicurati di esplorare i vari approcci disponibili. Non provare a chiudere il processo di progettazione ai passaggi X, Y e Z, per non perdere le possibilità dalla A alla W.


4

Hai ragione che OOD è limitato e obsoleto. Il suo principale vantaggio è che è così ben specificato e ritualizzato, con nomi fantasiosi per ogni modello banale e trucco di design. Non troverai alcun sistema comparabile per gli approcci più moderni e razionali alla progettazione dei sistemi.

Posso solo elencare un paio di cose: un approccio semantico alla progettazione, un sottoinsieme del quale si chiama Programmazione orientata al linguaggio (ed è ampiamente utilizzato dalla comunità Scala) e Domain-Driven Design. Quest'ultima è una visione semplificata e OOPised di un approccio di progettazione semantica più generico, potrai godertelo solo se ti piace OOD.

Consiglierei anche di imparare uno o due scherzi dalle altre comunità di programmazione funzionale - ad esempio Haskell e Scheme, non tutto il loro know-how progettuale era già stato trasferito a Scala.


3

La domanda era: come progettiamo soluzioni Scala pure?

Risposte come,

  1. Usiamo XYZ perché ...
  2. Usiamo ABC ma così ....
  3. Abbiamo iniziato a usare XYZ, ma poi abbiamo scoperto che ABC era migliore perché ...

avrebbe indicato una certa maturità, o l'esistenza generalmente accettata, di una tale serie di strumenti o soluzioni.

I commenti relativi all'opportunità di considerare un set di strumenti di progettazione specifico di Scala sono una questione diversa. La mia opinione su questo è che Scala è rivoluzionaria nel modo in cui combina programmazione imperativa e funzionale. Scala cattura un'incredibile quantità di pratiche, concetti e principi di sviluppo, e quindi, nel trovare una soluzione che si adatta perfettamente a Scala, probabilmente scopriremo una soluzione per la quale un sottoinsieme servirà molto bene anche molte altre lingue.

È sicuro quindi dire che conosciamo la risposta alla domanda di fallback:

"... è ora di inventarne uno?"

Potrebbe essere ? (Laddove " " non prescrive i mezzi per la soluzione. Potrebbe essere raggiunto estendendo una soluzione esistente o creandone una da zero. Ammette tuttavia che vi sono carenze significative nelle opzioni di progettazione esistenti)

Se non sei d'accordo, puoi votare la mia risposta. Lo prenderò come un uomo.


Rivoluzionario? Perché? Lisp era una combinazione perfetta di approcci imperativi e funzionali per decenni. Scala è interessante per il suo sistema di tipi.
SK-logic,

3
Ci scusiamo se "rivoluzionario" è una parola forte e forte. Non sto insinuando che Scala abbia reinventato la ruota, ma sicuramente ha rimodellato la gomma. In effetti, Scala è un delizioso mix di tutta la bontà di molti linguaggi di programmazione. Sono sicuro che prende in prestito pesantemente anche da Lisp. Per me, e non sto rivendicando gli altri, il linguaggio Scala sembra rivoluzionario in quanto sto pensando al codice in un modo che viene dannatamente naturale. Un linguaggio di progettazione / modellazione che fa lo stesso sarebbe sicuramente gratuito. Quello era il mio unico punto: nessuna offesa per Lisp e tutte le altre grandi lingue.
Jack

2

Non farei distinzione tra le lingue, lo farei allo stesso modo in Java. Comunque vengo dalla scuola OO piuttosto che da quella funzionale (algebrica Haskell o lisp). Il design dell'app Haskell è davvero diverso da Scala o Clojure. Anche il design di un'app Scala.js è diverso a causa del DOM con stato: è difficile combattere una parte con stato dell'app che è obbligatoria. Nel tempo mi sono abituato a farlo nel modo OO, mentre cercavo di eliminare il più possibile lo stato e la mutabilità. Se fossi un fan di typelevel o scalaz, le mie app probabilmente apparirebbero molto diverse.

  1. Decidere lo stack tecnologico e le principali decisioni di progettazione come responsabilità client / server, natura distribuita: abbiamo davvero bisogno della persistenza dei dati? Cosa si adatta meglio alla nostra applicazione? (certamente non ancora librerie o framework)

  2. A partire da un solo App.scalafile in cui definisco i tipi di chiave (tratti e classi di casi) e ruoli - molta contemplazione e pensiero pur essendo AFK. È così facile giocarci mentre è in un file. Anche il prototipo potrebbe nascere se fosse piuttosto una semplice applicazione. Dedico molto tempo a questa fase perché nulla è più importante che avere il prototipo più corretto e principalmente trasparente e sensibile possibile. Tutto aiuta a rendere più accurati i nostri ulteriori studi e aumenta le probabilità di successo.

  3. Ora, quando abbiamo dimostrato la correttezza della nostra soluzione, abbiamo una visione chiara e vengono svelati tutti i potenziali problemi, è tempo di pensare a che tipo di librerie o metodologie di terze parti introdurre. Abbiamo bisogno di framework di gioco o solo alcune librerie? Realizzeremo questi attori di stato, abbiamo davvero bisogno della resilienza di Akka, ..., .. o forse no? Usiamo Rx per questi flussi? Cosa utilizziamo per l'interfaccia utente in modo da non impazzire dopo che ha 10 funzionalità interattive?

  4. dividere il App.scalafile in più file perché nuovi tratti e classi sono apparsi e sono diventati più grandi. La loro interfaccia dovrebbe raccontare i loro ruoli. Ora è anche il momento di pensare all'utilizzo di classi di tipi e polimorfismo ad hoc, ove possibile. In modo che chiunque chiami la tua interfaccia, deve essere flessibile per loro. Il punto è implementare solo funzionalità generali, lasciando i dettagli al chiamante. A volte ciò che stai cercando di implementare potrebbe essere simile a Turing-complete, quindi dovresti fornire un modo ai chiamanti di implementare le specifiche da soli invece di accumulare richieste di funzionalità su GitHub. Questa roba è difficile da aggiungere in seguito. Deve essere pensato all'inizio. Oltre a progettare una DSL.

  5. riflettere sulla progettazione perché l'applicazione crescerà e deve essere pronta a resistere a nuove funzionalità, ottimizzazioni, persistenza, interfaccia utente, remoting, ecc. - roba che non è stata ancora implementata o è stata in qualche modo derisa o sottratta. Ricorda che mentre è tutto in un unico file è abbastanza facile cambiare le cose. Il cervello umano inizia a perderlo dopo che si è diffuso su oltre 10 file. Ed è come un tumore che cresce, è così che inizia l'ingegneria genetica. Ho notato che le mie applicazioni finiscono con alcune lunghe funzioni parziali che ne formano una sorta di spina dorsale. Trovo facile lavorare con. Immagino di essere stato infettato dagli attori di Akka.

  6. Ora che esistono le prime funzionalità effettive, non solo il funzionamento principale, è tempo di iniziare a scrivere test. Perchè così tardi? Perché durante questo periodo probabilmente hai passato importanti riscritture e avresti perso tempo a mantenere questi test. Non si dovrebbero scrivere test a meno che non si sia veramente sicuri che non ci saranno riprogettazioni importanti. Preferisco i test di integrazione che resistono facilmente al refactoring continuo e non passerò più tempo a testare lo sviluppo di cose. Mi è successo un paio di volte che ho passato troppo tempo a mantenere i test. La correzione di potenziali bug avrebbe sicuramente ripagato più dei test negativi. Test errati o test scritti fin dal primo momento possono causare forti dolori. Si noti che non sono certamente un fan di TDD - IMHO è una cazzata.

  7. Rifattorizzare, rendere e mantenere leggibile il design dell'applicazione, ruoli chiari dei componenti. Man mano che l'applicazione cresce non c'è modo che rimanga tale a meno che tu non sia un programmatore geniale e se leggi questa risposta probabilmente non lo sei :-) Nemmeno io. Di solito esistono alcune cose di stato perché non sapevi come evitare quello - che potrebbe causare problemi, cerca di eliminarli. Assicurati anche di eliminare gli odori di codice che ti hanno disturbato per un po 'di tempo. A questo punto il tuo project manager (se ne hai uno) probabilmente inizia a scuotere la testa. Digli che pagherà.

  8. Fatto, l'app è ben progettata? Dipende: hanno componenti ruoli ben definiti che hanno effettivamente senso anche al di fuori della tua testa? Potresti prenderne alcuni e open source senza troppi sforzi? Esiste una chiara separazione tra le preoccupazioni? Puoi aggiungere una nuova funzionalità senza preoccuparti che andrà in crash altrove (segno di sapere esattamente cosa stai facendo)? In generale, dovrebbe essere leggibile come un libro con semplici soluzioni pragmatiche ai problemi. Qual è l'IMHO il principale segno di un buon design.

Tutto sommato, è solo un duro lavoro e tempo che probabilmente non otterrai dai tuoi manager. Tutto ciò che ho descritto qui richiede molto tempo, fatica, caffè, tè e soprattutto pensando AFK. Più lo dai, migliore è il design. Non esiste una ricetta generale per progettare applicazioni, buon senso, pragmatismo, KISS, duro lavoro, esperienza significa buon design.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.