La programmazione funzionale sostituisce i modelli di progettazione GoF?


1047

Da quando ho iniziato a studiare F # e OCaml l'anno scorso, ho letto un numero enorme di articoli che insistono sul fatto che i modelli di progettazione (specialmente in Java) sono soluzioni alternative per le funzionalità mancanti nei linguaggi imperativi. Un articolo che ho trovato fa un'affermazione abbastanza forte :

Molte persone che ho incontrato hanno letto il libro Design Patterns di Gang of Four (GoF). Qualsiasi programmatore che si rispetti ti dirà che il libro è indipendente dalla lingua e che i modelli si applicano all'ingegneria del software in generale, indipendentemente dalla lingua che usi. Questa è una pretesa nobile. Purtroppo è lontano dalla verità.

I linguaggi funzionali sono estremamente espressivi. In un linguaggio funzionale uno non ha bisogno di schemi di progettazione perché il linguaggio è probabilmente di così alto livello, si finisce per programmare in concetti che eliminano gli schemi di progettazione tutti insieme.

Le caratteristiche principali della programmazione funzionale (FP) includono funzioni come valori di prima classe, curry, valori immutabili, ecc. Non mi sembra ovvio che i modelli di progettazione OO stiano approssimando una di queste caratteristiche.

Inoltre, nei linguaggi funzionali che supportano OOP (come F # e OCaml), mi sembra ovvio che i programmatori che usano questi linguaggi utilizzino gli stessi schemi di progettazione disponibili per tutti gli altri linguaggi OOP. In effetti, in questo momento uso F # e OCaml ogni giorno, e non ci sono differenze notevoli tra i pattern che uso in questi linguaggi rispetto ai pattern che uso quando scrivo in Java.

C'è qualche verità nell'affermazione che la programmazione funzionale elimina la necessità di modelli di progettazione OOP? In tal caso, potresti pubblicare o collegare un esempio di un tipico modello di progettazione OOP e il suo equivalente funzionale?


18
Potresti guardare l'articolo di Steve Yegge ( steve-yegge.blogspot.com/2006/03/… )
Ralph

27
"il libro è indipendente dal linguaggio e gli schemi si applicano all'ingegneria del software in generale" - va notato che il libro non è d'accordo con questa affermazione, nel senso che alcune lingue non hanno bisogno di esprimere certe cose come gli schemi di progettazione: "I nostri schemi assumere funzionalità linguistiche di livello Smalltalk / C ++ e tale scelta determina cosa può e non può essere implementato facilmente [...] CLOS ha multi-metodi, ad esempio, che riducono la necessità di un modello come Visitatore (Pagina 331). " (pagina 4)
Guildenstern,

6
Inoltre, tieni presente che molti modelli di progettazione non sono nemmeno necessari in linguaggi imperativi di livello sufficientemente elevato.
R. Barzell,

3
@ R.Barzell Che cosa sono quelle "lingue imperative di livello sufficientemente alto"? Grazie.
cibercitizen1,

5
@ cibercitizen1 linguaggi di tipo duck con supporto per funzioni di ordine superiore e funzioni anonime. Queste caratteristiche forniscono gran parte della potenza che molti modelli di design erano destinati a fornire.
R. Barzell,

Risposte:


1077

Il post sul blog che hai citato sopravvaluta un po 'la sua affermazione. FP non elimina la necessità di modelli di progettazione. Il termine "modelli di progettazione" non è ampiamente utilizzato per descrivere la stessa cosa nei linguaggi FP. Ma esistono. I linguaggi funzionali hanno molte regole di buone pratiche del modulo "quando incontri il problema X, usa un codice che assomiglia a Y", che è fondamentalmente quello che è un modello di progettazione.

Tuttavia, è corretto che la maggior parte dei modelli di progettazione specifici di OOP siano praticamente irrilevanti nei linguaggi funzionali.

Non penso che dovrebbe essere particolarmente controverso affermare che i modelli di progettazione in generale esistono solo per rimediare alle carenze del linguaggio. E se un'altra lingua può risolvere lo stesso problema in modo banale, quell'altra lingua non avrà bisogno di un modello di progettazione per questo. Gli utenti di quella lingua potrebbero non essere nemmeno consapevoli che il problema esiste , perché, beh, non è un problema in quella lingua.

Ecco cosa ha da dire la banda di quattro su questo problema:

La scelta del linguaggio di programmazione è importante perché influenza il proprio punto di vista. I nostri modelli assumono funzionalità linguistiche di livello Smalltalk / C ++ e tale scelta determina cosa può e non può essere implementato facilmente. Se avessimo assunto linguaggi procedurali, avremmo potuto includere modelli di progettazione chiamati "Ereditarietà", "Incapsulamento" e "Polimorfismo". Allo stesso modo, alcuni dei nostri modelli sono supportati direttamente dai linguaggi orientati agli oggetti meno comuni. CLOS ha diversi metodi, ad esempio, che riducono la necessità di un modello come Visitatore. In effetti, ci sono abbastanza differenze tra Smalltalk e C ++ per indicare che alcuni schemi possono essere espressi più facilmente in una lingua rispetto all'altra. (Vedi Iterator per esempio.)

(Quanto sopra è una citazione dall'Introduzione al libro Patterns Design, pagina 4, paragrafo 3)

Le caratteristiche principali della programmazione funzionale includono funzioni come valori di prima classe, curry, valori immutabili, ecc. Non mi sembra ovvio che i modelli di progettazione OO stiano approssimando una di queste caratteristiche.

Qual è il modello di comando, se non un'approssimazione di funzioni di prima classe? :) In un linguaggio FP, passeresti semplicemente una funzione come argomento a un'altra funzione. In un linguaggio OOP, devi racchiudere la funzione in una classe, che puoi creare un'istanza e quindi passare quell'oggetto all'altra funzione. L'effetto è lo stesso, ma in OOP si chiama modello di progettazione e richiede molto più codice. E qual è il modello astratto di fabbrica, se non il curry? Passa i parametri a una funzione un po 'alla volta, per configurare il tipo di valore che sputa quando finalmente la chiami.

Quindi sì, molti modelli di progettazione GoF sono resi ridondanti nei linguaggi FP, perché esistono alternative più potenti e più facili da usare.

Ma ovviamente ci sono ancora modelli di progettazione che non sono risolti dai linguaggi FP. Qual è l'equivalente in FP di un singleton? (Trascurando per un momento che i singoli sono generalmente un modello terribile da usare.)

E funziona anche in entrambi i modi. Come ho detto, FP ha anche i suoi schemi di progettazione; la gente di solito non la pensa come tale.

Ma potresti esserti imbattuto in monadi. Cosa sono, se non un modello di progettazione per "trattare con lo stato globale"? Questo è un problema così semplice nei linguaggi OOP che non esiste un modello di progettazione equivalente.

Non abbiamo bisogno di un modello di progettazione per "incrementare una variabile statica" o "leggere da quel socket", perché è proprio quello che fai .

Dire che una monade è uno schema di progettazione è assurdo come dire gli Integer con le loro solite operazioni e l'elemento zero è uno schema di progettazione. No, una monade è un modello matematico , non un modello di progettazione.

In linguaggi (puri) funzionali, gli effetti collaterali e lo stato mutevole sono impossibili, a meno che non ci si aggiri con il "modello di progettazione" della monade o con uno qualsiasi degli altri metodi per consentire la stessa cosa.

Inoltre, nei linguaggi funzionali che supportano OOP (come F # e OCaml), mi sembra ovvio che i programmatori che usano questi linguaggi utilizzino gli stessi schemi di progettazione disponibili per tutti gli altri linguaggi OOP. In effetti, in questo momento uso F # e OCaml ogni giorno, e non ci sono differenze sorprendenti tra i pattern che uso in questi linguaggi rispetto ai pattern che uso quando scrivo in Java.

Forse perché stai ancora pensando imperativamente? Molte persone, dopo aver affrontato le lingue imperative per tutta la vita, hanno difficoltà a rinunciare a quell'abitudine quando provano un linguaggio funzionale. (Ho visto alcuni tentativi piuttosto divertenti in F #, dove letteralmente ogni funzione era solo una serie di istruzioni 'let', praticamente come se avessi preso un programma C e sostituito tutti i punti e virgola con 'let'. :))

Ma un'altra possibilità potrebbe essere che non ti sei reso conto che stai risolvendo banalmente problemi che richiederebbero schemi di progettazione in un linguaggio OOP.

Quando usi il curry o passi una funzione come argomento a un'altra, fermati e pensa a come lo faresti in una lingua OOP.

C'è qualche verità nell'affermazione che la programmazione funzionale elimina la necessità di modelli di progettazione OOP?

Sì. :) Quando lavori in un linguaggio FP, non hai più bisogno dei modelli di progettazione specifici di OOP. Ma hai ancora bisogno di alcuni schemi di progettazione generali, come MVC o altri elementi non OOP specifici, e invece hai bisogno di un paio di nuovi "schemi di progettazione" specifici per FP. Tutte le lingue hanno i loro difetti e i modelli di progettazione sono di solito il modo in cui lavoriamo intorno a loro.

Ad ogni modo, potresti trovare interessante cimentarti in linguaggi FP "più puliti", come ML (il mio preferito, almeno a fini di apprendimento) o Haskell , dove non hai la stampella OOP su cui ripiegarti quando hai di fronte qualcosa di nuovo.


Come previsto, alcune persone hanno obiettato alla mia definizione di modelli di design come "correggendo le carenze in una lingua", quindi ecco la mia giustificazione:

Come già detto, la maggior parte dei modelli di progettazione sono specifici di un paradigma di programmazione, o talvolta anche di un linguaggio specifico. Spesso risolvono problemi che esistono solo in quel paradigma (vedi monadi per FP o fabbriche astratte per OOP).

Perché il modello astratto di fabbrica non esiste in FP? Perché il problema che tenta di risolvere non esiste qui.

Quindi, se esiste un problema nelle lingue OOP, che non esiste nelle lingue FP, è evidente che si tratta di un difetto delle lingue OOP. Il problema può essere risolto, ma la tua lingua non lo fa, ma richiede un sacco di codice boilerplate per aggirare il problema. Idealmente, vorremmo che il nostro linguaggio di programmazione magicamente facesse scomparire tutti i problemi. Qualunque problema ancora presente è in linea di principio un difetto della lingua. ;)


73
I modelli di progettazione descrivono soluzioni generali ai problemi di base. Ma è anche ciò che fanno i linguaggi e le piattaforme di programmazione. Quindi usi modelli di progettazione quando le lingue e le piattaforme che stai utilizzando non sono sufficienti.
yfeldblum,

135
S.Lott: descrivono soluzioni a problemi che esistono in una determinata lingua, sì. Non esiste un modello di progettazione dei comandi nei linguaggi FP, perché il problema che tenta di risolvere non esiste. Ciò significa che risolvono problemi che la lingua stessa non può risolvere. Cioè, carenze nella lingua
jalf

38
La monade è un concetto matematico e lo stai allungando con la tua classificazione. Certo, puoi vedere funzioni, monoidi, monadi, matrici o altri concetti matematici come schemi di progettazione, ma quelli sono più simili a algoritmi e strutture di dati ... concetti fondamentali, indipendenti dal linguaggio.
Alexandru Nedelcu,

41
Certo, le monadi sono un concetto matematico, ma sono anche un modello. Il "modello FP" delle monadi è in qualche modo distinto dal concetto matematico di monadi. Il primo è un modello utilizzato per aggirare alcune "limitazioni" in linguaggi FP puri. Quest'ultimo è un concetto matematico universale.
jalf

69
Nota che le monadi in Haskell sono usate per cose diverse dallo stato mutabile, ad esempio per eccezioni, continuazioni, comprensione delle liste, analisi, programmazione asincrona e così via. Ma tutte queste applicazioni di monadi potrebbero probabilmente essere chiamate schemi.
JacquesB,

152

C'è qualche verità nell'affermazione che la programmazione funzionale elimina la necessità di modelli di progettazione OOP?

La programmazione funzionale non è la stessa della programmazione orientata agli oggetti. I modelli di progettazione orientati agli oggetti non si applicano alla programmazione funzionale. Invece, hai modelli di progettazione di programmazione funzionale.

Per la programmazione funzionale, non leggere i libri di schemi di progettazione OO; leggerai altri libri sui modelli di progettazione FP.

linguaggio agnostico

Non del tutto. Solo lingua-agnostico rispetto alle lingue OO. I modelli di progettazione non si applicano affatto ai linguaggi procedurali. Hanno appena senso in un contesto di progettazione di database relazionali. Non si applicano quando si progetta un foglio di calcolo.

un tipico modello di design OOP e il suo equivalente funzionale?

Quanto sopra non dovrebbe esistere. È come chiedere un pezzo di codice procedurale riscritto come codice OO. Ummm ... Se traduco l'originale Fortran (o C) in Java, non ho fatto altro che tradurlo. Se lo riscrivo totalmente in un paradigma OO, non assomiglierà più al Fortran o alla C originale: sarà irriconoscibile.

Non esiste una semplice mappatura dal design OO al design funzionale. Sono modi molto diversi di vedere il problema.

La programmazione funzionale (come tutti gli stili di programmazione) ha schemi di progettazione. I database relazionali hanno schemi di progettazione, OO ha schemi di progettazione e la programmazione procedurale ha schemi di progettazione. Tutto ha motivi di design, persino l'architettura degli edifici.

I modelli di progettazione - come concetto - sono un modo senza tempo di costruire, indipendentemente dalla tecnologia o dal dominio del problema. Tuttavia, specifici modelli di progettazione si applicano a specifici domini e tecnologie problematiche.

Chiunque pensi a ciò che sta facendo scoprirà i modelli di progettazione.


12
MVC non è design OO. È il design architettonico - quel modello si applica abbastanza ampiamente.
S. Lott,

1
@Princess: la programmazione funzionale non è necessariamente più semplice. Nel tuo esempio, sì. Per altre cose, la giuria è ancora fuori. Ma hai scartato un modello di progettazione OO Java e adottato un modello di progettazione FP.
S. Lott,

1
+1: preferisco questa risposta alla risposta di Jalf sopra. Sebbene alcuni modelli di progettazione risolvano le carenze della lingua, non tutti lo fanno. Ad esempio, direi a malapena che il modello di progettazione "sciogliendo il nodo ricorsivo" affronta una carenza del linguaggio, è solo un linguaggio utile per allentare le dipendenze.
Jon Harrop,

9
Java 8 includerà chiusure alias funzioni anonime alias lambda. Ciò renderà il modello di progettazione dei comandi obsoleto per Java. Questo è un esempio di carenza linguistica, no? Hanno aggiunto una funzione mancante e ora non è necessario il modello di progettazione.
Todd Chaffee,

2
+1 per la frase di chiusura. I modelli di progettazione hanno lo scopo di semplificare la programmazione e rendere i programmi risultanti più efficienti, a quello che sono destinati a fare.
Sorter

46

I commenti di Brian sullo stretto legame tra linguaggio e modello sono al punto,

La parte mancante di questa discussione è il concetto di idioma. Il libro di James O. Coplien, "Advanced C ++" ha avuto un'enorme influenza qui. Molto prima di scoprire Christopher Alexander e la colonna senza nome (e non si può parlare in modo sensato di schemi senza nemmeno leggere Alexander), ha parlato dell'importanza di padroneggiare i linguaggi nell'apprendimento di una lingua. Ha usato la copia con stringa in C come esempio, while(*from++ = *to++);puoi vederlo come un cerotto per una funzione del linguaggio mancante (o funzione della libreria), ma ciò che conta davvero è che è un'unità di pensiero o di espressione più grande di qualsiasi le sue parti.

Questo è ciò che i modelli e le lingue stanno cercando di fare per permetterci di esprimere le nostre intenzioni in modo più succinto. Più ricche sono le unità di pensiero, più complessi sono i pensieri che puoi esprimere. Avere un vocabolario ricco e condiviso su una vasta gamma di scale - dall'architettura di sistema fino al bit twiddling - ci consente di avere conversazioni più intelligenti e pensieri su cosa dovremmo fare.

Possiamo anche, come individui, imparare. Qual è l'intero punto dell'esercizio. Ognuno di noi può capire e usare cose che non saremmo mai in grado di pensare a noi stessi. Lingue, quadri, biblioteche, schemi, modi di dire e così via hanno tutti il ​​loro posto nel condividere la ricchezza intellettuale.


8
Grazie! questo è ciò che riguarda gli schemi: "chunking concettuale" per ridurre il carico cognitivo.
Randall Schulz,

E le Monadi funzionali appartengono sicuramente a questa discussione.
Greg,

@RandallSchulz: le caratteristiche del linguaggio (e il loro uso idiomatico, ovviamente) si adatterebbero bene anche alla categoria del "blocco concettuale per ridurre il carico cognitivo".
Roy Tinker,

39

Il libro GoF si lega esplicitamente a OOP - il titolo è Design Patterns - Elements of Reusable Object-Oriented Software (sottolineatura mia).



26

Ecco un altro link che discute questo argomento: http://blog.ezyang.com/2010/05/design-patterns-in-haskel/

Nel suo post sul blog, Edward descrive tutti e 23 i modelli GoF originali in termini di Haskell.


4
L'articolo non sembra mostrare realmente i modelli di design in Haskell, ma mostra come Haskell affronti queste esigenze senza tali modelli.
Fresheyeball,

3
@Fresheyball: dipende dalla tua definizione di modelli. La mappatura di una funzione su un elenco è una variante del modello Visitatore? In genere ho pensato che la risposta fosse "sì". I pattern dovrebbero andare oltre una particolare sintassi. La funzione applicata potrebbe essere racchiusa come oggetto o passata come puntatore a funzione, ma il concetto è lo stesso per me. Non sei d'accordo?
srm,

20

Quando provi a guardare questo a livello di "modelli di progettazione" (in generale) e "FP contro OOP", le risposte che troverai saranno alquanto oscure.

Passa a un livello più profondo su entrambi gli assi, tuttavia, e considera modelli di progettazione specifici e caratteristiche linguistiche specifiche e le cose diventano più chiare.

Quindi, ad esempio, alcuni schemi specifici, come Visitatore , Strategia , Comando e Osservatore, cambiano o scompaiono definitivamente quando si usa una lingua con tipi di dati algebrici e corrispondenza di schemi , chiusure , funzioni di prima classe , ecc. Alcuni altri schemi del libro GoF ancora "restate in giro", però.

In generale, direi che, nel tempo, i modelli specifici vengono eliminati da nuove (o semplicemente in aumento popolarità) funzionalità linguistiche. Questo è il corso naturale di progettazione del linguaggio; man mano che le lingue diventano di alto livello, le astrazioni che in precedenza potevano essere richiamate in un libro solo usando esempi ora diventano applicazioni di una particolare funzione o libreria linguistica.

(A parte: ecco un recente blog che ho scritto, che ha altri collegamenti a ulteriori discussioni su FP e modelli di progettazione.)


Come puoi dire che il modello di visitatore "scompare"? Non si trasforma semplicemente da "crea un'interfaccia Visitatore con un sacco di metodi Visit" in "usa tipi di unione e pattern matching"?
Gabe,

22
Sì, ma quello è cambiato da a modello che è un'idea di design che leggi in un libro e che applichi al tuo codice, in "solo codice". Cioè, "usa i tipi di unione e la corrispondenza dei modelli" è il modo in cui normalmente codice cose in un tale linguaggio. (Analogia: se nessuna lingua aveva dei forloop e tutti avevano solo dei whileloop, allora "For" potrebbe essere uno schema di iterazione. Ma quando forè solo un costrutto supportato dalla lingua e dal modo in cui le persone codificano normalmente, allora non è uno schema - non si ' Ho bisogno di uno schema, è solo un codice, amico.)
Brian,

4
Detto in altro modo, una cartina di tornasole forse non male per "è uno schema" è: il codice attuale scritto in questo modo a uno studente universitario del secondo anno che si specializza in CS con un anno di esperienza di programmazione nella tua lingua. Se mostri loro il codice e vanno "è un design intelligente", allora è un modello. Se mostri loro il codice e vanno "bene, duh!", Allora non è uno schema. (E se mostri questo "Visitatore" a chiunque abbia fatto ML / F # / Haskell per un anno, andranno "bene, duh!")
Brian,

1
Brian: Penso che abbiamo solo diverse definizioni di un "modello". Considero qualsiasi astrazione progettuale identificabile come un modello , mentre tu consideri solo le astrazioni non ovvie come un modello . Solo perché C # ha foreache Haskell mapMnon significa che non hanno il modello Iterator. Non vedo alcun problema nel dire che il modello Iterator è implementato come interfaccia generica IEnumerable<T>in C # e la typeclass Traversablein Haskell.
Gabe

È possibile che schemi software non ovvi siano utili agli ingegneri del software, ma che tutti i modelli siano utili ai progettisti linguistici. Vale a dire "Se stai creando una nuova lingua, assicurati di includere un modo chiaro per esprimere il modello iteratore." Anche gli ovvi schemi sono interessanti quando iniziamo a porre la domanda "Esiste una sintassi migliore per esprimere questa idea?" Dopotutto, questo è ciò che porta qualcuno a creare foreach.
srm,

16

La presentazione di Norvig allude a un'analisi che hanno fatto di tutti i modelli GoF e affermano che 16 dei 23 modelli avevano implementazioni più semplici nei linguaggi funzionali o erano semplicemente parte del linguaggio. Quindi presumibilmente almeno sette di loro erano a) ugualmente complicati oppure b) non presenti nella lingua. Sfortunatamente per noi, non sono elencati!

Penso che sia chiaro che la maggior parte dei modelli "creativi" o "strutturali" in GoF sono semplicemente trucchi per ottenere i sistemi di tipo primitivo in Java o C ++ per fare quello che vuoi. Ma il resto è degno di considerazione, indipendentemente dalla lingua in cui programmi.

Uno potrebbe essere prototipo; mentre è una nozione fondamentale di JavaScript, deve essere implementata da zero in altre lingue.

Uno dei miei modelli preferiti è il modello Null Object: rappresenta l'assenza di qualcosa come un oggetto che non fa un tipo appropriato di nulla. Questo potrebbe essere più semplice da modellare in un linguaggio funzionale. Tuttavia, il vero risultato è il cambiamento di prospettiva.


2
Che strana analisi da fare poiché i modelli GoF sono stati progettati specificamente per i linguaggi OOP basati su classi. Sembra un po 'come analizzare se le chiavi a tubo sono buone per fare lavori elettrici.
munificente

@munifico: non proprio. L'orientamento agli oggetti fornisce polimorfismo; la programmazione funzionale generalmente fornisce polimorfismo.
Marcin

@Marcin un programmatore OO significa qualcosa di molto diverso dal polimorfismo rispetto a un programmatore funzionale.
AndrewC,

@AndrewC Non sono d'accordo. Il programmatore OO potrebbe pensare che significano qualcosa di diverso, ma non lo fanno.
Marcin,

3
@Marcin Nella mia esperienza, un programmatore OO si riferisce in genere al polimorfismo del sottotipo (spesso solo usando l'Oggetto), usando i cast per ottenerlo o il polimorfismo ad hoc (sovraccarico, ecc.). Quando un programmatore funzionale dice polimorfismo, intendono polimorfismo parametrico (vale a dire che funziona per qualsiasi tipo di dati - Int, funzione, elenco), che forse è più simile alla programmazione generica di OO di quanto non sia come qualsiasi programmatore di OO di solito chiama polimorfismo.
AndrewC,

15

Direi che quando hai una lingua come Lisp con il suo supporto per le macro, puoi costruire le tue astrazioni specifiche del dominio, astrazioni che spesso sono molto meglio delle soluzioni di linguaggio generale.


Sono completamente perso. Preparare qualcosa con le astrazioni ... Cosa significa?
tuinstoel

2
È possibile creare astrazioni specifiche del dominio (anche incorporate) senza macro. Le macro ti consentono di abbellirle semplicemente aggiungendo una sintassi personalizzata.
Jon Harrop,

2
Puoi pensare a Lisp come a un set di Lego per la costruzione di linguaggi di programmazione: è un linguaggio ma è anche un metalinguaggio. Ciò significa che per qualsiasi dominio problematico, è possibile progettare in modo personalizzato un linguaggio che non presenta evidenti carenze. Richiederà un po 'di pratica e Kurt Gödel potrebbe non essere d'accordo, ma vale la pena passare un po' di tempo con Lisp per vedere cosa porta in tavola (suggerimento, macro).
Greg,

9

E anche le soluzioni del modello di progettazione OO sono specifiche della lingua.

I modelli di progettazione sono soluzioni a problemi comuni che il tuo linguaggio di programmazione non risolve per te. In Java, il modello Singleton risolve il problema one-of-qualcosa (semplificato).

In Scala, hai un costrutto di livello superiore chiamato Object oltre a Class. È pigramente istanziato e ce n'è solo uno. Non è necessario utilizzare il modello Singleton per ottenere un Singleton. Fa parte della lingua.


8

I modelli sono modi per risolvere problemi simili che vengono visti più volte, quindi descritti e documentati. Quindi no, FP non sostituirà i modelli; tuttavia, FP potrebbe creare nuovi schemi e rendere "obsoleti" alcuni schemi attuali di "buone pratiche".


4
I modelli GoP sono modi per risolvere il problema dei limiti di un particolare tipo di linguaggio di programmazione che si frappongono. Ad esempio "Voglio indirizzare sulle classi e dire loro di creare oggetti" -> "Non puoi, ma puoi creare oggetti simili a metaclasse chiamati Fabbrica". "Voglio invio multiplo" -> "Non puoi, ma c'è un labirinto che puoi implementare chiamato Pattern Visitatore". Ecc. Nessuno degli schemi ha senso se non sei in un linguaggio OOP con limitazioni specifiche.
Kaz

1
Non so che "nessuno" abbia senso in altre lingue, ma concorderò sul fatto che molti di loro non hanno senso in altre lingue. Adapter e Bridge sembrano avere più possibilità multilingue, diminuendo un po 'per il visitatore e forse un po' meno per l'ascoltatore. Tuttavia, i modelli attraverso le lingue soffriranno sempre di "come fare l'operazione della lingua X nella lingua Y" che sostiene i confini naturali della lingua. Un esempio perfetto è stato il modello Singleton, che è fondamentalmente, come posso ottenere i globuli C in OOP? (a cui risponderò, non dovresti).
Edwin Buck,

1
Secondo Kaz: I pattern non sono "il modo di risolvere problemi simili che vengono visti ancora e ancora" ma "il modo di risolvere problemi simili che vengono visti ancora e ancora E devono essere riscritti ancora e ancora perché il linguaggio non consente di scrivilo solo una volta ". In altre parole, se il linguaggio ha permesso di estrarre / sottrarre il modello in libreria / classe / modulo ecc., Smette di essere un modello ma diventa una libreria / classe / modulo. In FP, è molto più facile estrarre / sottrarre bit di codici a una funzione, quindi i "pattern" vengono convertiti più facilmente in codice riutilizzabile rendendoli non un pattern.
mb14

1
La tua interpretazione è benvenuta, ma il libro GoF è stato chiaro per definire uno schema e, se leggi i capitoli introduttivi, non dice nulla sulle lingue o sui punti deboli della lingua. Certamente alcune lingue hanno aree che le faranno sfruttare alcuni schemi più spesso, ma che tu lo scriva dieci volte (taglia e incolla) o lo implementi una volta con dieci realizzazioni (sottoclasse) o abbia un framework configurato per farlo leggermente dieci modi diversi, è solo un dettaglio di implementazione del modello esposto.
Edwin Buck,

1
Tornando indietro in questa conversazione dopo anni, penso che molte persone associno Patterns con un linguaggio di programmazione specifico o un paradigma di programmazione specifico. Possono essere utilizzati in tale contesto, ma esistevano prima della programmazione. "Un modo intramontabile di costruzione" discute gli schemi nella costruzione di architettura e pianificazione della comunità. Ciò implica che le tecniche orientate al modello possono essere utilizzate al di fuori dei "limiti di un linguaggio" a meno che non si voglia chiamare l'edilizia un linguaggio di programmazione :)
Edwin Buck

8

Come altri hanno già detto, esistono schemi specifici per la programmazione funzionale. Penso che il problema di sbarazzarsi dei modelli di progettazione non sia tanto una questione di passaggio a funzionalità, ma una questione di caratteristiche del linguaggio .

Dai un'occhiata a come Scala elimina il "modello singleton": dichiari semplicemente un oggetto anziché una classe. Un'altra caratteristica, la corrispondenza dei motivi, aiuta a evitare il disordine del motivo dei visitatori. Vedi il confronto qui: Scala's Pattern Matching = Visitor Pattern on Steroids

E Scala, come F #, è una fusione di OO-funzionale. Non so di F #, ma probabilmente ha questo tipo di funzionalità.

Le chiusure sono presenti in un linguaggio funzionale, ma non devono essere limitate a loro. Aiutano con il modello del delegatore.

Un'altra osservazione. Questo pezzo di codice implementa un modello: è così classico ed è così elementare che di solito non lo consideriamo come un "modello", ma sicuramente è:

for(int i = 0; i < myList.size(); i++) { doWhatever(myList.get(i)); }

Linguaggi imperativi come Java e C # hanno adottato quello che è essenzialmente un costrutto funzionale per affrontare questo: "foreach".


Direi che Scala include un supporto di prima classe per il modello singleton. Il modello è ancora lì, ma il codice del boilerplate necessario per applicare il modello è notevolmente ridotto rispetto a Java.
JacquesB,

Se le opinioni fossero come un *******, beh ... Guarda il resto delle risposte. "Dichiarare semplicemente un oggetto invece di una classe" è così vero, lo definirei esplicitamente un oggetto letterale (es. var singleton = {};). Mi piace anche la menzione del modello foreach. Sfortunatamente, sembra che la maggior parte delle persone che hanno risposto / commentato questa domanda non capiscano la programmazione funzionale e giustificherebbero l'uso dei modelli di progettazione OOP. +1 per fornire esempi concreti, darei di più se potessi.
Evan Plaice,

@JacquesB Non posso commentare Scala / Haskell ma in JavaScript (es. Ibrido funzionale / imperativo) non c'è assolutamente nessuna piastra di comando, basta regolare il modo in cui si dichiarano gli oggetti usando combinazioni di sintassi letterale di oggetti, funzioni anonime, passando le funzioni come prima membri della classe e consentendo l'ereditarietà multipla (eliminando la necessità di contratti di interfaccia).
Evan Plaice,

8

GoF Design Patterns sta codificando ricette alternative per i linguaggi OO che discendono da Simula 67 , come Java e C ++.

La maggior parte dei "mali" trattati dai modelli di progettazione sono causati da:

  • classi tipizzate staticamente, che specificano oggetti, ma non sono essi stessi oggetti;
  • restrizione al singolo invio (per selezionare un metodo viene utilizzato solo l'argomento più a sinistra, gli argomenti rimanenti vengono considerati solo come tipi statici: se hanno tipi dinamici, sta al metodo risolverlo con approcci ad hoc);
  • distinzione tra chiamate di funzione regolari e chiamate di funzione orientate agli oggetti, nel senso che le funzioni orientate agli oggetti non possono essere passate come argomenti funzionali in cui sono previste funzioni regolari e viceversa; e
  • distinzione tra "tipi di base" e "tipi di classe".

Non esiste uno solo di questi modelli di progettazione che non scompaia nel Common Lisp Object System, anche se la soluzione è strutturata essenzialmente nello stesso modo del modello di progettazione corrispondente. (Inoltre, quel sistema di oggetti precede il libro GoF di oltre un decennio. Common Lisp è diventato uno standard ANSI lo stesso anno in cui quel libro è stato pubblicato per la prima volta.)

Per quanto riguarda la programmazione funzionale, se gli schemi si applicano o meno ad esso dipende dal fatto che il linguaggio di programmazione funzionale dato abbia un qualche tipo di sistema di oggetti e se sia modellato sui sistemi di oggetti che beneficiano degli schemi. Questo tipo di orientamento agli oggetti non si combina bene con la programmazione funzionale, poiché la mutazione dello stato è nella parte anteriore e centrale.

La costruzione e l'accesso non mutante sono compatibili con la programmazione funzionale, quindi potrebbero essere applicabili schemi che hanno a che fare con l'astrazione dell'accesso o della costruzione: schemi come Fabbrica, Facciata, Proxy, Decoratore e Visitatore.

D'altra parte, i modelli comportamentali come Stato e Strategia probabilmente non si applicano direttamente nella OOP funzionale perché la mutazione dello stato è al centro. Questo non significa che non si applicano; forse si applicano in qualche modo in combinazione con qualsiasi trucco disponibile per simulare lo stato mutabile.


2
"I modelli di progettazione GoF codificano le soluzioni alternative" è semplicemente un'affermazione falsa.
John Peters,

7

Vorrei collegare un paio di articoli eccellenti ma piuttosto densi di Jeremy Gibbons: "Progettare modelli come programmi generici di tipo di dati di ordine superiore" e "L'essenza del modello Iterator" (entrambi disponibili qui: http: // www. comlab.ox.ac.uk/jeremy.gibbons/publications/ ).

Entrambi descrivono in che modo costrutti idiomatici funzionali coprono il terreno che è coperto da specifici schemi di progettazione in altre impostazioni (orientate agli oggetti).


6

Non puoi avere questa discussione senza far apparire sistemi di tipo.

Le caratteristiche principali della programmazione funzionale includono funzioni come valori di prima classe, curry, valori immutabili, ecc. Non mi sembra ovvio che i modelli di progettazione OO stiano approssimando una di queste caratteristiche.

Questo perché queste funzionalità non affrontano gli stessi problemi di OOP ... sono alternative alla programmazione imperativa. La risposta di FP a OOP risiede nei sistemi di tipi di ML e Haskell ... nello specifico sommano tipi, tipi di dati astratti, moduli ML e caratteri tipografici Haskell.

Ma ovviamente ci sono ancora modelli di progettazione che non sono risolti dai linguaggi FP. Qual è l'equivalente FP di un singleton? (Trascurando per un momento che i singoli sono generalmente un modello terribile da usare)

La prima cosa che fanno le macchine da scrivere è eliminare la necessità di singoli.

Potresti consultare l'elenco dei 23 ed eliminarne altri, ma non ho tempo di farlo adesso.


6
In che modo i typeclass (l'equivalente FP delle interfacce OOP) eliminano la necessità di singoli (l'equivalente FP dello stato globale)?
Gabe,

4

Penso che solo due GoF Design Patterns siano progettati per introdurre la logica di programmazione funzionale nel linguaggio OO naturale. Penso a strategia e comando. Alcuni degli altri modelli di progettazione GoF possono essere modificati mediante la programmazione funzionale per semplificare la progettazione e mantenere lo scopo.


4
Il fatto è che il punto principale di molti modelli è quello di sfruttare il polimorfismo per fare cose che il supporto decente ai concetti di FP potrebbe consentire automaticamente. (La maggior parte delle incarnazioni che ho visto su Builder, ad esempio, sono solo curry a metà.) Una volta che puoi facilmente trattare le funzioni come valori, i modelli spesso semplificano al punto di essere banali. Diventano "pass callback" o "hanno un dizionario di callbacks" e diverse classi di builder, ad esempio, possono quasi scomparire. IMO uno schema smette di essere uno schema una volta che è abbastanza banale da essere proprio come funzionano le cose , piuttosto che qualcosa che devi implementare.
cHao,


3

La programmazione funzionale non sostituisce i modelli di progettazione. I motivi di progettazione non possono essere sostituiti.

I modelli esistono semplicemente; sono emersi nel tempo. Il libro GoF ne ha formalizzato alcuni. Se nuovi modelli stanno venendo alla luce mentre gli sviluppatori usano linguaggi di programmazione funzionali che sono cose interessanti, e forse ci saranno anche libri scritti su di loro.


1
I modelli di progettazione non possono essere sostituiti? Penso sia un po 'chiuso. Probabilmente possiamo essere tutti d'accordo sul fatto che i modelli di progettazione sono pensati per risolvere i problemi di programmazione e almeno vorrei sperare che un giorno potremmo risolvere quei problemi senza schemi di progettazione.
Metropolis,

3
Qualsiasi modello particolare potrebbe essere sostituibile, ma il concetto di modelli non può essere sostituito . Ricorda che il termine "modello" è sorto nel campo dell'architettura .
Frank Shearar,

1
I pattern non sono pensati per risolvere problemi di programmazione. Gli schemi sono modi in cui programmiamo. La documentazione dei modelli ha lo scopo di aiutare a risolvere i problemi di programmazione.
Torbjørn,

3
@ Torbjørn: i pattern sono modi in cui programmiamo quando la lingua si mette in mezzo . Esistono a causa di una discrepanza tra il comportamento desiderato del programma e le abilità incorporate del linguaggio, in cui i requisiti e le abilità non si mappano bene o si mappano in modo ambiguo. Se non fosse per quello, non ci sarebbe alcun modello; avresti un'implementazione che è proprio il modo in cui le cose vengono fatte e altre implementazioni non valgono la pena prendere in considerazione.
cHao,

1
Solo che i modelli esistono davvero solo per facilitare la comunicazione. Non c'è altro scopo. E in tutti gli incontri di design a cui ho partecipato nel corso degli anni, una discussione sull'algoritmo è ciò che era importante, non lo schema. Lo schema raramente spiegava cosa stava realmente succedendo in alcun senso significativo. Spiega precisamente gli impatti O (n) vs O (n Log (n))? No. Spiega quanto facilmente si adatterà all'architettura esistente? No. Discussioni su algoritmi in scala reale. Non sto sostenendo che i modelli dovrebbero essere ritirati di per sé, ma se lo fossero, quasi nulla ne risentirebbe di conseguenza.

3

Nel nuovo libro del 2013 intitolato "Schemi di programmazione funzionale - in Scala e Clojure" l'autore Michael.B. Linn svolge un lavoro dignitoso confrontando e fornendo sostituzioni in molti casi per i modelli GoF e discute anche i nuovi modelli funzionali come "ricorsione della coda", "memoizzazione", "sequenza pigra", ecc.

Questo libro è disponibile su Amazon. L'ho trovato molto istruttivo e incoraggiante quando provenivo da un background OO di un paio di decenni.


3

I modelli OOP e GoF si occupano degli stati. OOP modella la realtà per mantenere la base di codice il più vicino possibile alle specifiche esigenze della realtà. I modelli di progettazione GoF sono modelli identificati per risolvere i problemi atomici del mondo reale. Gestiscono il problema dello stato in modo semantico.

Come nella vera programmazione funzionale non esiste uno stato, non ha senso applicare i modelli GoF. Non ci sono modelli di progettazione funzionali allo stesso modo di quelli di progettazione GoF. Ogni modello di design funzionale è artificiale in contrasto con la realtà poiché le funzioni sono costrutti della matematica e non della realtà.

Le funzioni mancano del concetto di tempo in quanto restituiscono sempre lo stesso valore qualunque sia l'ora corrente, a meno che il tempo non faccia parte dei parametri della funzione, il che rende veramente difficile elaborare "richieste future". I linguaggi ibridi mescolano questi concetti rendono i linguaggi non veri e propri linguaggi di programmazione funzionale.

I linguaggi funzionali stanno aumentando solo per una cosa: le attuali restrizioni naturali della fisica. I processori di oggi sono limitati nella loro velocità di elaborazione delle istruzioni a causa delle leggi fisiche. Si vede una stagnazione nella frequenza di clock ma un'espansione nell'elaborazione dei core. Ecco perché il parallelismo delle istruzioni diventa sempre più importante per aumentare la velocità delle applicazioni moderne. Poiché la programmazione funzionale per definizione non ha uno stato e quindi non ha effetti collaterali, è sicuro elaborare le funzioni in modo sicuro in parallelo.

I modelli GoF non sono obsoleti. Sono almeno necessari per modellare i requisiti del mondo reale. Ma se usi un linguaggio di programmazione funzionale devi trasformarli nei loro equivalenti ibridi. Infine, non hai alcuna possibilità di creare solo programmi funzionali se usi la persistenza. Per gli elementi ibridi del tuo programma rimane la necessità di utilizzare i modelli GoF. Per qualsiasi altro elemento puramente funzionale non è necessario utilizzare modelli GoF perché non esiste uno stato.

Poiché i modelli GoF non sono necessari per una vera programmazione funzionale, ciò non significa che i principi SOLID non debbano essere applicati. I principi SOLID sono al di là di qualsiasi paradigma linguistico.


2
FP può avere uno stato - semplicemente nessuno stato globale, condiviso o mutabile.
vt5491,

2

Nella programmazione funzionale, i modelli di progettazione hanno un significato diverso. In effetti, la maggior parte dei modelli di progettazione OOP non sono necessari nella programmazione funzionale a causa del livello più elevato di astrazione e HOF utilizzati come elementi costitutivi.

Il principio di un HOF significa che le funzioni possono essere passate come argomenti ad altre funzioni. e le funzioni possono restituire valori.


1

Come ha affermato la risposta accettata, OOP e FP hanno tutti i loro schemi specifici.

Tuttavia, ci sono alcuni schemi che sono così comuni che dovrebbero avere tutte le piattaforme di programmazione a cui riesco a pensare. Ecco un elenco (incompleto):

  • Adattatore. Riesco a malapena a pensare a un'utile piattaforma di programmazione che sia così completa (e autorealizzata) da non dover parlare con il mondo. Se lo farà, è sicuramente necessario un adattatore.

  • Facciata. Qualsiasi piattaforma di programmazione in grado di gestire un grande codice sorgente dovrebbe essere in grado di modulare. Se dovessi creare un modulo per altre parti del programma, vorrai nascondere le parti "sporche" del codice e dargli una bella interfaccia.

  • Interprete. In generale, qualsiasi programma sta facendo solo due cose: analizzare input e stampare output. È necessario analizzare gli input del mouse e stampare i widget delle finestre. Pertanto, avere un interprete incorporato dà al programma ulteriore potere di personalizzare le cose.

Inoltre, ho notato in un tipico linguaggio FP, Haskell, che c'è qualcosa di simile ai modelli GoF, ma con nomi diversi. A mio avviso ciò suggerisce che erano lì perché ci sono alcuni problemi comuni da risolvere in entrambi i linguaggi FP e OOP.

  • Trasformatore e decoratore di monade. Il primo usato per aggiungere ulteriore abilità in una monade esistente, il secondo aggiunge ulteriore capacità a un oggetto esistente.

1

Penso che ogni paradigma abbia uno scopo diverso e come tale non possa essere confrontato in questo modo.

Non ho sentito che i modelli di progettazione GoF sono applicabili a tutte le lingue. Ho sentito che sono applicabili a tutte le lingue OOP . Se usi la programmazione funzionale, il dominio dei problemi che risolvi è diverso dai linguaggi OO.

Non userei il linguaggio funzionale per scrivere un'interfaccia utente, ma uno dei linguaggi OO come C # o Java faciliterebbe questo lavoro. Se stessi scrivendo un linguaggio funzionale, non prenderei in considerazione l'utilizzo di modelli di progettazione OO.


1

OOP e FP hanno obiettivi diversi. OOP mira a incapsulare le complessità / parti mobili dei componenti software e FP mira a ridurre al minimo la complessità e le dipendenze dei componenti software.

Tuttavia, questi due paradigmi non sono necessariamente in contraddizione al 100% e potrebbero essere applicati insieme per ottenere il beneficio da entrambi i mondi.

Anche con un linguaggio che non supporta in modo nativo la programmazione funzionale come C #, è possibile scrivere un codice funzionale se si comprendono i principi FP. Allo stesso modo potresti applicare i principi OOP usando F # se comprendi i principi, i modelli e le migliori pratiche OOP. Faresti la scelta giusta in base alla situazione e al problema che provi a risolvere, indipendentemente dal linguaggio di programmazione che usi.


1

Alcuni schemi sono più facili da implementare in un linguaggio che supporta FP. Ad esempio, la strategia può essere implementata usando piacevolmente le chiusure. Tuttavia, a seconda del contesto, potresti preferire implementare la strategia usando un approccio basato sulla classe, dire dove le strategie stesse sono piuttosto complicate e / o condividere la struttura che vuoi modellare usando il Metodo modello.

Nella mia esperienza di sviluppo in un linguaggio multi-paradigma (Ruby), l'implementazione di FP funziona bene in casi semplici, ma dove il contesto è più complicato l'approccio basato su GoF OOP si adatta meglio.

L'approccio FP non sostituisce l'approccio OOP, lo integra.


0

La caratteristica fondamentale della programmazione funzionale, IMHO, è che stai programmando con nient'altro che espressioni - espressioni all'interno di espressioni all'interno di espressioni che tutte valutano fino all'ultima espressione finale che "riscalda la macchina quando valutata".

Caratteristica fondamentale della programmazione orientata agli oggetti, IMHO è la programmazione con oggetti con stato interno. Non puoi avere uno stato interno nelle funzioni pure: i linguaggi di programmazione orientati agli oggetti hanno bisogno di istruzioni per far accadere le cose. (Non ci sono dichiarazioni nella programmazione funzionale.)

Stai confrontando le mele con le arance. I modelli di programmazione orientata agli oggetti non si applicano alla programmazione delle funzioni, poiché la programmazione funzionale è la programmazione con espressioni e la programmazione orientata agli oggetti è la programmazione con stato interno.


Hmm, avrei dovuto notare che la domanda aveva undici anni prima di rispondere. :-)
Jim Flood,

0

Preparati.

Farò arrabbiare molti nel sentirmi affermare di aver sostituito i modelli di progettazione e di aver smontato il SOLID e il DRY. Io non sono nessuno. Ciononostante, ho modellato correttamente l'architettura collaborativa (manifatturiera) e pubblicato le regole per la creazione di processi online insieme al codice e alla scienza alla base del mio sito Web http://www.powersemantics.com/ .

La mia tesi è che i modelli di progettazione tentano di ottenere ciò che la produzione chiama "personalizzazione di massa", una forma di processo in cui ogni fase può essere rimodellata, ricomposta ed estesa. Potresti pensare a tali processi come script non compilati. Non ripeterò qui il mio argomento (online). In breve, la mia architettura di personalizzazione di massa sostituisce i modelli di progettazione ottenendo tale flessibilità senza alcuna semantica disordinata. Sono rimasto sorpreso dal fatto che il mio modello abbia funzionato così bene, ma il modo in cui i programmatori scrivono codice semplicemente non tiene in considerazione il modo in cui la produzione organizza il lavoro collaborativo.

  • Produzione = ogni fase interagisce con un prodotto
  • OOP = ogni passaggio interagisce con se stesso e altri moduli, facendo passare il prodotto da un punto all'altro come inutili impiegati

Questa architettura non ha mai bisogno di refactoring. Esistono anche regole relative alla centralizzazione e alla distribuzione che incidono sulla complessità. Ma per rispondere alla tua domanda, la programmazione funzionale è un'altra serie di semantiche di elaborazione, non un'architettura per processi personalizzati di massa in cui 1) il routing di origine esiste come documento (script) che il possessore può riscrivere prima dell'attivazione e 2) i moduli possono essere facilmente e aggiunto o rimosso dinamicamente.

Potremmo dire che OOP è il paradigma del "processo hardcoded" e che i modelli di progettazione sono modi per evitare quel paradigma. Ma questo è tutto ciò che riguarda la personalizzazione di massa. I modelli di progettazione incorporano processi dinamici come hardcode disordinato. Non ha senso. Il fatto che F # permetta di passare le funzioni come parametro significa che sia i linguaggi funzionali sia quelli OOP tentano di realizzare la personalizzazione di massa.

Quanto è confuso per il lettore, hardcode che rappresenta lo script? Niente affatto se pensi che i consumatori del tuo compilatore paghino per tali funzionalità, ma per me tali funzionalità sono uno spreco semantico. Sono inutili, perché il punto della personalizzazione di massa è rendere dinamici i processi stessi , non solo dinamici con il programmatore che utilizza Visual Studio.


0

Lo fa, in quanto un PL funzionale di alto livello (come OCaml, con classi, moduli, ecc.) Certamente sostituisce i linguaggi imperativi OOP nella versatilità dei tipi e nel potere espressivo. Le astrazioni non perdono, puoi esprimere la maggior parte delle tue idee direttamente nel programma. Pertanto, sì, sostituisce i modelli di progettazione, la maggior parte dei quali sono ridicolmente semplicistici rispetto ai modelli funzionali comunque.

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.