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

11

Negli ultimi anni, le lingue che mi piace usare diventano 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 del dominio, e utilizzare le funzionalità funzionali dove questo rende la codifica più facile, più coincidente e più sicura (specialmente quando si opera su collezioni o quando si passano le funzioni).

Tuttavia i due mondi "si scontrano" quando arrivano a schemi di progettazione. L'esempio specifico che ho affrontato di recente è il pattern Observer. Voglio che un produttore avvisi un altro codice (i "consumatori / osservatori", ad esempio un archivio DB, un registratore e così via) quando un articolo 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 ei contro dei due approcci? Una volta ho sentito un guru di FP dire che i modelli di design ci sono solo a causa delle limitazioni del linguaggio, ed è per questo che ci sono così pochi linguaggi funzionali. Forse questo potrebbe essere un esempio di questo?

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

    
posta Lorenzo Dematté 29.03.2012 - 10:15
fonte

4 risposte

0

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

Anche se in questo esempio è chiaro che dovresti optare per l'approccio funzionale, in generale dipenderà molto dalla complessità del comportamento che l'oggetto chiamato deve esibire. Se è davvero una questione di comportamento complesso, in cui ti ritrovi a riapplicare logiche simili spesso e i generatori di funzioni non possono essere usati per esprimerlo chiaramente, allora probabilmente vorrai andare per la composizione della classe o l'ereditarietà, dove dovrai avere un po 'più libertà di riutilizzare ed estendere il comportamento esistente su una base ad hoc.

Un modello che ho osservato, tuttavia, è che di solito gli sviluppatori scelgono l'approccio funzionale e solo quando sorge la richiesta di un comportamento più granulare decidono di optare per un approccio basato sulla classe. Ad esempio, Django è passato da visualizzazioni basate su funzioni, caricatori di template e test runner a quelli basati su classi una volta che i vantaggi e i requisiti diventavano ovvi, ma non prima.

    
risposta data 29.03.2012 - 11:51
fonte
5

La versione funzionale è molto più breve, più facile da mantenere, più facile da leggere e, generalmente, di gran lunga superiore in quasi ogni aspetto immaginabile.

Molti - anche se, lontano da tutti i modelli sono per compensare la mancanza di funzionalità in OOP, come ad esempio gli osservatori. Quelli sono molto meglio modellati funzionalmente.

    
risposta data 29.03.2012 - 11:30
fonte
5

Il tuo "guru FP" ha parzialmente ragione; molti modelli OO sono hack per fare cose funzionali. (La sua affermazione che questo è il motivo per cui ci sono pochi linguaggi FP sembra alquanto dubbio.) I modelli Observer e Strategy stanno cercando di emulare funzioni di prima classe. Il pattern Visitor è un trucco per simulare la corrispondenza dei pattern. Il tuo IItemObserver è solo una funzione sotto mentite spoglie. Fingendo che sia diverso da qualsiasi altra funzione che prende un oggetto non ti compra nulla.

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

    
risposta data 05.02.2014 - 14:00
fonte
-1

Non posso davvero rispondere alla domanda perché non sono bravo nei linguaggi funzionali; ma penso che dovresti essere meno preoccupato dell'approccio finché ciò che hai funziona. Da quanto ho capito, finché non aggiungi più ascoltatori in futuro o non cambi gli ascoltatori durante l'esecuzione, puoi saltare il pattern Observer qui.

Non sono d'accordo sul fatto che gli schemi di progettazione compensino le "limitazioni" nelle lingue OO. Sono lì per fare buon uso delle funzionalità di OOP. Il polimorfismo e l'ereditarietà sono caratteristiche, non limitazioni. Gli schemi di progettazione fanno uso di queste caratteristiche 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.

    
risposta data 29.03.2012 - 10:36
fonte