In che modo un altro linguaggio popolare evita di dover utilizzare lo schema di fabbrica gestendo una complessità simile a quella di Java / Java EE?

21

Il pattern di fabbrica (o almeno l'uso di FactoryFactory.. ) è il bersaglio di molte battute, come qui .

Oltre ad avere nomi dettagliati e "creativi" come RequestProcessorFactoryFactory.RequestProcessorFactory , c'è qualcosa di fondamentalmente sbagliato nel modello factory se devi programmare in Java / C ++ e c'è un caso per Abstract_factory_pattern ?

Come sarebbe un altro linguaggio popolare (ad esempio, Ruby o Scala ) evita di doverlo utilizzare mentre gestisci una complessità simile?

La ragione per cui la sto chiedendo è che vedo soprattutto le critiche alle fabbriche menzionate nel contesto di Java / Java EE ecosistema, ma non spiegano mai come altri linguaggi / framework li risolvano.

    
posta senseiwu 16.04.2014 - 12:26
fonte

5 risposte

26

La tua domanda è contrassegnata con "Java", non sorprende che ti chiedi perché viene deriso il modello Factory: Java stesso viene fornito con abusi ben confezionati di quel modello.

Ad esempio, prova a caricare un documento XML da un file ed esegui una query XPath su di esso. Hai bisogno di qualcosa come 10 linee di codice solo per configurare Fabbriche e Costruttori:

DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = builderFactory.newDocumentBuilder(); 

Document xmlDocument = builder.parse(new FileInputStream("c:\employees.xml"));
XPath xPath =  XPathFactory.newInstance().newXPath();

xPath.compile(expression).evaluate(xmlDocument);

Mi chiedo se i ragazzi che progettano questa API abbiano mai lavorato come sviluppatori o semplicemente leggendo libri e buttando le cose in giro. Capisco che non volevano scrivere un parser da soli e hanno lasciato il compito agli altri, ma è comunque una brutta implementazione.

Dato che stai chiedendo qual è l'alternativa, qui viene caricato il file XML in C #:

XDocument xml = XDocument.Load("c:\employees.xml");
var nodes = xml.XPathSelectElements(expression);

Sospetto che gli sviluppatori Java appena schiusi vedano la follia Factory e pensino che sia giusto farlo, se i geni che hanno costruito Java stessi li hanno usati così tanto.

Fabbrica e qualsiasi altro modello sono strumenti, ciascuno adatto per un particolare lavoro. Se li applichi a lavori che non sono adatti per te, è destinato ad avere un codice brutto.

    
risposta data 16.04.2014 - 15:54
fonte
36

Come spesso accade, la gente fraintende cosa sta succedendo (e questo include molte delle risate).
Non è il modello di fabbrica in sé che è cattivo tanto quanto lo usano molti (forse anche la maggior parte).

E questo senza dubbio deriva dal modo in cui viene insegnata la programmazione (e i modelli). Gli scolari (che spesso si chiamano "studenti") vengono istruiti a "creare X usando il modello Y" e dopo alcune iterazioni di quello pensano che sia il modo di affrontare qualsiasi problema di programmazione.
Quindi iniziano ad applicare uno schema specifico a loro piace a scuola contro qualsiasi cosa, indipendentemente dal fatto che sia appropriato o meno.

E questo include professori universitari che scrivono libri sul design del software, purtroppo.
Il culmine di questo era un sistema che provavo il dispiacere di dover mantenere quello che era stato creato da uno di questi (l'uomo aveva anche diversi libri sul design orientato agli oggetti al suo nome, e una posizione di insegnamento nel dipartimento CS di una grande università ).
Era basato su un modello a 3 livelli, con ogni livello stesso un sistema a 3 livelli (necessario disaccoppiare ...). Sull'interfaccia tra ogni gruppo di livelli, su entrambi i lati, c'era una fabbrica per produrre gli oggetti per trasferire i dati sull'altro livello e una fabbrica per produrre l'oggetto per tradurre l'oggetto ricevuto su uno appartenente al livello ricevente. > Per ogni fabbrica c'era una fabbrica astratta (chissà, la fabbrica potrebbe dover cambiare e quindi non vuoi che il codice chiamante debba cambiare ...).
E tutto quel casino era ovviamente completamente non documentato.

Il sistema aveva un database normalizzato in 5a forma normale (non scherzo).

Un sistema che era essenzialmente poco più di una rubrica che poteva essere usata per registrare e tracciare i documenti in entrata e in uscita, aveva un codice di 100 MB in C ++ e oltre 50 tabelle di database. La stampa di 500 lettere formali richiederebbe fino a 72 ore utilizzando una stampante collegata direttamente al computer che esegue il software.

È per questo che le persone imbrogliano i modelli, e in particolare le persone si concentrano in modo univoco su un singolo modello specifico.

    
risposta data 16.04.2014 - 12:52
fonte
16

Le fabbriche hanno molti vantaggi che consentono l'elegante design delle applicazioni in alcune situazioni. Uno è che è possibile impostare le proprietà degli oggetti che in seguito si desidera creare in un unico posto creando una fabbrica, quindi consegnare quella fabbrica. Ma spesso non hai davvero bisogno di farlo. In tal caso, l'utilizzo di una Factory aggiunge ulteriore complessità senza in realtà dare nulla in cambio. Prendiamo questo stabilimento, ad esempio:

WidgetFactory redWidgetFactory = new ColoredWidgetFactory(COLOR_RED);
Widget widget = redWidgetFactory.create();

Un'alternativa al modello Factory è il modello Builder molto simile. La differenza principale è che le proprietà degli oggetti creati da una Factory vengono impostate quando la Factory viene inizializzata, mentre un Builder viene inizializzato con uno stato predefinito e tutte le proprietà vengono impostate in seguito.

WidgetBuilder widgetBuilder = new WidgetBuilder();
widgetBuilder.setColor(COLOR_RED);
Widget widget = widgetBuilder.create();

Ma quando il tuo problema è l'overengineering, la sostituzione di una Factory con un Builder non è probabilmente un miglioramento.

La sostituzione più semplice per entrambi i pattern è ovviamente la creazione di istanze di oggetti con un semplice costruttore con l'operatore new :

Widget widget = new ColoredWidget(COLOR_RED);

I costruttori, tuttavia, hanno uno svantaggio cruciale nella maggior parte dei linguaggi orientati agli oggetti: devono restituire un oggetto di quella classe esatta e non possono restituire un sottotipo.

Quando è necessario scegliere il sottotipo in fase di esecuzione ma non si vuole ricorrere alla creazione di un Builder o di una classe Factory completamente nuovi, è possibile utilizzare invece un metodo di produzione. Questo è un metodo statico di una classe che restituisce una nuova istanza di quella classe o una delle sue sottoclassi. Una fabbrica che non mantiene nessuno stato interno può essere spesso sostituita con un metodo di produzione simile:

 Widget widget = Widget.createColoredWidget(COLOR_RED); // returns an object of class RedColoredWidget

Una nuova funzionalità in Java 8 sono riferimenti al metodo che consentono di passare metodi in giro, proprio come faresti con una fabbrica senza stato. Convenientemente, tutto ciò che accetta un riferimento al metodo accetta anche qualsiasi oggetto che implementa la stessa interfaccia funzionale, che può anche essere una fabbrica a pieno titolo con stato interno, quindi è possibile introdurre facilmente le fabbriche in un secondo momento, quando si vede una ragione per farlo. / p>     

risposta data 16.04.2014 - 13:31
fonte
3

Le fabbriche di un tipo o dell'altro si trovano praticamente in qualsiasi linguaggio orientato agli oggetti in circostanze appropriate. A volte hai semplicemente bisogno di un modo per selezionare il tipo di oggetto da creare basato su un parametro semplice come una stringa.

Alcuni lo prendono troppo lontano e cercano di architettare il loro codice per non aver mai bisogno di chiamare un costruttore se non all'interno di una fabbrica. Le cose cominciano a diventare ridicole quando hai fabbriche di fabbrica.

Ciò che mi ha colpito quando ho imparato Scala, e non conosco Ruby, ma credo che sia più o meno allo stesso modo, è che il linguaggio è abbastanza espressivo che i programmatori non cercano sempre di spingere il lavoro "idraulico" a file di configurazione esterni. Piuttosto che essere tentati di creare factory per collegare le tue classi insieme in diverse configurazioni, in Scala si usano oggetti con tratti mescolati. È anche relativamente facile creare semplici DSL in-language per attività che sono spesso troppo ingegnerizzate in Java.

Inoltre, come altri commenti e risposte hanno sottolineato, chiusure e funzioni di prima classe eliminano la necessità di molti modelli. Per questo motivo, credo che molti degli anti-pattern cominceranno a scomparire con l'adozione diffusa di C ++ 11 e Java 8.

    
risposta data 16.04.2014 - 14:47
fonte
2

In generale, c'è questa tendenza che i programmi Java sono orrendamente sovra-progettati [citazione necessaria]. Avere molte fabbriche è uno dei sintomi più comuni di sovraingegneria; è per questo che le persone si prendono gioco di quelle.

In particolare, il problema che Java ha con le fabbriche è che in Java a) i costruttori non sono funzioni eb) le funzioni non sono cittadini di prima classe.

Immagina di poter scrivere qualcosa del genere (lascia che Function sia un'interfaccia ben nota di JRE )

// Framework code
public Node buildTree(Function<BranchNode, Node, Node> branchNodeFactory) {
    Node current = nextNode();
    while (hasNextNode()) {
        current = branchNodeFactory.call(current, nextNode());
    }
    return current
}

// MyDataNode.java
public class MyBranchNode implements BranchNode {

    public MyBranchNode(Node left, Node right) { ... }
}

// Client code
Node root = buildTree(MyBranchNode::new);

Vedi, nessuna interfaccia o classe factory. Molti linguaggi dinamici hanno funzioni di prima classe. Purtroppo, questo non è possibile con Java 7 o versioni precedenti (implementare lo stesso con le fabbriche è lasciato come esercizio al lettore).

Quindi l'alternativa non è tanto "usa un modello diverso", ma più "usa un linguaggio con un modello di oggetto migliore" o "non sovrastimare i problemi semplici".

    
risposta data 16.04.2014 - 13:19
fonte

Leggi altre domande sui tag