Progettazione in linguaggi "misti": progettazione orientata agli oggetti o programmazione funzionale?


11

Negli ultimi anni, le lingue che mi piace usare stanno diventando sempre più "funzionali". Ora uso linguaggi che sono una sorta di "ibrido": C #, F #, Scala. Mi piace progettare la mia applicazione usando classi che corrispondono agli oggetti di dominio e utilizzare funzionalità funzionali che rendono la codifica più semplice, più coincidente e più sicura (specialmente quando si opera su raccolte o quando si passano funzioni).

Tuttavia i due mondi si "scontrano" quando si tratta di progettare modelli. L'esempio specifico che ho affrontato di recente è il modello Observer. Voglio che un produttore notifichi un altro codice (i "consumatori / osservatori", diciamo un archivio DB, un logger e così via) quando un oggetto viene creato o modificato.

Inizialmente l'ho fatto "funzionalmente" in questo modo:

producer.foo(item => { updateItemInDb(item); insertLog(item) })
// calls the function passed as argument as an item is processed

Ma ora mi chiedo se dovrei usare un approccio più "OO":

interface IItemObserver {
  onNotify(Item)
}
class DBObserver : IItemObserver ...
class LogObserver: IItemObserver ...

producer.addObserver(new DBObserver)
producer.addObserver(new LogObserver)
producer.foo() //calls observer in a loop

Quali sono i pro e i contro dei due approcci? Una volta ho sentito un guru del FP dire che i modelli di progettazione esistono solo a causa delle limitazioni del linguaggio, ed è per questo che ci sono così pochi nei linguaggi funzionali. Forse questo potrebbe esserne un esempio?

EDIT: Nel mio scenario particolare non ne ho bisogno, ma .. come implementeresti la rimozione e l'aggiunta di "osservatori" in modo funzionale? (Vale a dire come implementereste tutte le funzionalità nel modello?) Passando ad una nuova funzione, per esempio?


E gli attori?
Kiritsuku,

Dimentica le lezioni e tutte quelle cose inutili di OOPish. Faresti meglio a pensare in termini di moduli (vedi i linguaggi SML e OCaml per una fonte d'ispirazione).
SK-logic

@Antoras Se potessi fare un paragone con un approccio basato sugli attori, sarebbe più che benvenuto :)
Lorenzo Dematté

2
@ dema80, OCaml è perfettamente multi-paradigma. I moduli non sono affatto collegati alla programmazione funzionale. C'è un sistema di moduli avanzato in un Ada puramente imperativo, per esempio. E tutta la fama di OOP guadagnata dovrebbe effettivamente andare ai moduli - tutto il bene in OOP è solo varie forme di simulazione della funzionalità dei moduli. Puoi dimenticare totalmente tutte quelle classi e usare la loro sintassi per esprimere i moduli invece, pensando in termini di moduli, non di OOP. A proposito, questo è esattamente ciò che Microsoft ha fatto con il suo mscorlib - non molto OOP lì, solo moduli e spazi dei nomi.
Logica SK

1
Penso che la domanda migliore sia "C'è qualche chiarezza o organizzazione che perdi nel farlo nel modo FP?"
Djechlin,

Risposte:


0

Questo è un buon esempio di due diversi approcci che portano l'idea di eseguire un'attività al di fuori del limite di preoccupazione per l'oggetto chiamante.

Mentre in questo esempio è chiaro che dovresti optare per l'approccio funzionale, in generale dipenderà davvero dalla complessità del comportamento che l'oggetto chiamato deve esibire. Se si tratta davvero di un comportamento complesso, in cui ti ritroverai a riapplicare spesso una logica simile e i generatori di funzioni non possono essere utilizzati per esprimerla chiaramente, allora probabilmente vorrai andare per composizione di classe o eredità, dove hanno un po 'più di libertà di riutilizzo ed estensione del comportamento esistente su una base ad hoc.

Uno schema che ho osservato, tuttavia, è che di solito gli sviluppatori scelgono inizialmente un approccio funzionale e solo una volta che si presenta la domanda di un comportamento più granulare, decidono di adottare un approccio di classe. So, ad esempio, che Django è passato da viste basate su funzioni, caricatori di modelli e test runner a quelli basati su classi una volta che i vantaggi e i requisiti sono diventati ovvi, ma non prima.


Penso che questa risposta sia un po 'fuorviante. La programmazione funzionale non è la programmazione (solo con) funzioni. Anche la programmazione funzionale usa astrazioni, quindi non c'è dicotomia qui.
Doval,

"composizione o eredità, in cui avrai un po 'più di libertà di riutilizzare ed estendere il comportamento esistente" stai parlando in questo modo NON è uno dei principali vantaggi del PQ puro. la modularità, la componibilità e la reuseabilità si verificano in modo naturale quando si codifica in modo funzionale, questa non è una "cosa OOP"
sara

@ FilipDupanović Mi riferivo al riutilizzo e all'estensione del codice esistente. le funzioni pure sono probabilmente le cose più componibili in tutta la programmazione. sei scritto come se non riuscissi a gestire la complessità in un ambiente funzionale puro. gestire la complessità componendo parti semplici in parti più grandi ma ancora semplici e opache è il fulcro di FP, e molti sostengono che sia persino meglio di OOP. Non sono d'accordo con la dicotomia tra "una linea funzionale che non scala VS solido OOP che non ridimensiona nessun problema" che tu proponi.
Sara,

hai detto che FP era inferiore quando si trattava di comporre e riutilizzare il codice e che quando le applicazioni diventano più complesse, OOP sarà più o meno sempre "migliore". Sostengo che questo sia semplicemente falso e fuorviante, e quindi ho pensato che giustificasse un downvote. È davvero "zelante purista"? Non ne discuterò ulteriormente qui, poiché i commenti non sono per discussioni estese come queste.
Sara,

5

La versione funzionale è molto più breve, più facile da mantenere, più facile da leggere e, in generale, notevolmente superiore in quasi ogni aspetto immaginabile.

Molti-sebbene, lontano dal all- modelli sono per compensare la mancanza di caratteristiche in OOP, come osservatori. Questi sono molto meglio modellati funzionalmente.


Sono d'accordo e ho la stessa sensazione.
Lorenzo Dematté,

Sono d'accordo e ho la stessa sensazione. Ma ero una specie di "imbroglione" con il mio codice funzionale: nel mio caso è tutto ciò di cui ho bisogno, ma cosa succede se devo aggiungere e rimuovere "osservatori"? Ho modificato la mia domanda
Lorenzo Dematté il

5

Il tuo "FP guru" ha in parte ragione; molti modelli OO sono hack per fare cose funzionali. (La sua affermazione che questa è la ragione per cui pochi linguaggi FP sembrano al meglio discutibili.) I modelli Observer e Strategia stanno cercando di emulare funzioni di prima classe. Il modello visitatore è un trucco per simulare la corrispondenza dei modelli. La tua IItemObserverè solo una funzione mascherata. Fingere che sia diverso da qualsiasi altra funzione che accetta un oggetto non ti compra nulla.

Gli oggetti sono solo un tipo di astrazione dei dati. Questo documento aiuterà a far luce su questo argomento. Gli oggetti possono essere utili, ma è importante riconoscere che non sono adatti a tutto. Non c'è dicotomia; semplicemente questione di scegliere lo strumento giusto per il lavoro giusto e la programmazione funzionale non richiede in alcun modo di rinunciare agli oggetti. A parte questo, la programmazione funzionale è molto più che usare semplicemente le funzioni. Si tratta anche di ridurre al minimo gli effetti collaterali e la mutazione.


+1 per (IMO) una buona risposta. Inoltre, grazie per il link al documento: l'avevo letto qualche tempo fa e poi l'ho perso. Ora l'ho trovato di nuovo.
Giorgio,

-1

Non posso davvero rispondere alla domanda perché non sono bravo nei linguaggi funzionali; ma penso che dovresti essere meno preoccupato dell'approccio fintanto che ciò che hai funziona. Da quello che ho capito, purché non si aggiungano più ascoltatori in futuro o non si cambino gli ascoltatori durante l'esecuzione, è possibile saltare qui il modello di Observer.

Non sono d'accordo sul fatto che i modelli di progettazione compensino le "limitazioni" nei linguaggi OO. Sono lì per fare buon uso delle funzionalità OOP. Il polimorfismo e l'ereditarietà sono caratteristiche, non limitazioni. I modelli di progettazione fanno uso di queste funzionalità per promuovere un design flessibile. Puoi creare un programma assolutamente non OO in un OO. Puoi, con molta cura, scrivere un intero programma contenente oggetti che non hanno stato, imitando FP.


1
"Puoi, con molta cura, scrivere un intero programma contenente oggetti che non hanno stato, imitando FP", naturalmente, e attraverso la disciplina puoi fare lo stesso anche in linguaggi imperativi. :) In generale, non penso agli schemi di progettazione come qualcosa per compensare le limitazioni, ma considero il caso del modello Visitatore ..
Lorenzo Dematté,

Inoltre, non mi occupo del codice: tuttavia, vorrei confrontarmi con gli altri programmatori sull'approccio (per quanto ne so, questo è ciò che programmers.se è, altrimenti avrei pubblicato la mia Q su SO)
Lorenzo Dematté

Ogni volta che stai individuando uno schema ripetitivo, non è altro che un'indicazione di una grave limitazione del tuo linguaggio e del tuo modello di modellazione. Non ci saranno affatto schemi in un mondo ideale.
SK-logic

@ SK-logic: purtroppo il mondo non è l'ideale e il mondo reale ha schemi. Questo è il motivo per cui le lingue OO hanno ereditarietà, per implementare il codice ripetibile senza doverle riscrivere di nuovo. L'oggetto mondo reale ha uno stato, ecco perché esiste un modello di stato
DPD

1
@ SK-logic: sono ben consapevole che alcuni linguaggi sono programmabili e alcuni consentono di applicare effetti anche dal sistema dei tipi. Il mio punto è che, come "OOP", "modello" è un termine tristemente frainteso. Uno schema è qualcosa che continua a riapparire in forme simili ma diverse , che non ammette una soluzione uniforme . A volte è possibile far scomparire quelle quasirepetitions - i multimetodi rendono ridondanti il ​​visitatore / doppia spedizione. Ciò non implica "tutti i modelli sono segni di una carenza", perché ciò significa che il mondo reale è carente. I modelli sono nati nell'architettura , non nel software.
Frank Shearar,
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.