Perché utilizzare i modelli di creazione in Java?

1

Come sono utili i modelli di creazione? La maggior parte del materiale che ho incontrato spiega il caso d'uso per "risolvere problemi comuni associati alla creazione di oggetti, migliorare la flessibilità".

Posso pensare solo a due casi simili in cui questi potrebbero aiutare:

  1. Incapsulamento del codice di creazione dell'oggetto in una classe simile alla factory, in modo che tutto il codice di creazione dell'oggetto si trovi in un unico posto e qualsiasi modifica richieda modifiche solo in un unico punto.

  2. Rende dinamica la creazione di oggetti, in base alla quale il tipo di calcestruzzo richiesto può essere restituito in base ad alcuni parametri.

Non sono in grado di capire esattamente quali problemi risolvono i modelli creativi. Quali conseguenze negative potrebbero essere affrontate non utilizzando i modelli creativi?

Sarebbe bello se qualcuno potesse approfondire questo con esempi di codice reali.

    
posta bub 06.11.2014 - 14:36
fonte

4 risposte

8

Perché utilizzare i modelli di creazione (in Java)?

Il motivo principale per i pattern creativi è separare la creazione di un'istanza dal suo consumo .

Questo sembra in primo luogo piuttosto astratto, ma è facile vedere le conseguenze.

1) Si nasconde la logica creazionale dal consument di una istanza di oggetto . Come consument non ti interessa come viene creata un'istanza . Hai solo una domanda concreta (servizio, deposito, ecc.). Ogni richiesta è una dipendenza del tuo oggetto. Che porta a

2) È possibile inserire dipendenze dall'esterno, ovvero injection injection . Se sei in grado di iniettare / sostituire le dipendenze di un oggetto, puoi scrivere unit-test . Supponiamo che tu abbia bisogno del risultato di una complessa query DB o il risultato di diverse chiamate al servizio web; potresti facilmente iniettare quei risultati dai risultati esterni o stub . Inoltre puoi simulare / bloccare gli oggetti creativi stessi, che potrebbero essere usati per fornire altri mock / stub.

Un codice migliore / più verificabile porta a una copertura del codice più elevata, che porta nel migliore dei casi a una migliore e più elevata qualità del codice.

Potresti fare l'iniezione di dipendenza a mano o usare un qualche tipo di automazione, ad es. Spring-framework o simili.

Quali conseguenze negative potrebbero essere affrontate non utilizzando gli schemi creativi?

Lo svantaggio di non utilizzare pattern creativi dovrebbe essere chiaro da quanto detto sopra: rende più difficile il tuo codice a capire e test .

esempi di codice reali:

Supponiamo che tu abbia una macchina, che a sua volta ha un motore. Un modo tipico per impiantarlo è questo

public class Car {

    Engine engine;

    public void start(){
        engine.start();
    }

    public void stop(){
        engine.stop();
    }

    public Car(){
        engine=new Engine();
    }
}

public class Engine {

    public void start(){

    }

    public void stop(){

    }

}

Hai una macchina, che istanzia il suo motore. Tramite l'interfaccia della macchina, puoi avviare / fermare la macchina / il motore.

Questo non va bene in molti modi:

1) non si è in grado di cambiare il motore, l'auto funziona con. Potrebbe non sembrare un problema, dal momento che si intendeva che la macchina funzionasse solo con il motore specificato

2) ma, non potevi scambiare il motore reale per un testengine .

Devi prendere due passaggi per migliorare il design:

1) separa il consumo del motore dal suo uso 2) usa un pattern factory per incapsulare la creazione del motore

L'auto migliorata:

public class Car {

    Engine engine;

    public void start(){
        engine.start();
    }

    public void stop(){
        engine.stop();
    }

    public Car(Engine engine){
        this.engine=engine;
    }
}

I vantaggi sono:

  • possibilità di scambiare il motore per l'auto
  • possibilità di scambiare la fabbrica per il motore

Quindi l'intera creazione dell'automobile potrebbe essere sottratta con un builder :

public class CarBuilder {

    EngineFactory engineFactory;

    public Car build(){
        return new Car(engineFactory.produceEngine());
    }

    public CarBuilder(EngineFactory engineFactory){
        this.engineFactory=engineFactory;
    }

}

Quale potrebbe essere deriso / soppresso.

    
risposta data 06.11.2014 - 17:14
fonte
2

Il Singleton è classificato come un modello creativo, quindi è un uso ovvio: assicurando che tutti comunichino sempre con la stessa istanza. (I vantaggi di questo sono controversi, ma non ho detto che era un buono uso. Solo un uso piuttosto ovvio.)

I metodi di produzione consentono a un utente API di conoscere (e mantenere nel tempo) nomi di classi di helper eccessivamente specifici. Quando ordini la pizza, non ti importa se un MotorizedBicyclePizzaCourier, un WalkingPizzaCourier o un TeleportPizzaCourier ti servono: devi solo sapere che hai a che fare con un PizzaCourier. Questo lascia la pizzeria libera di introdurre nuovi metodi di consegna senza richiedere ai propri clienti di aggiornare nulla.

I builder ti consentono di semplificare i dettagli sintattici di basso livello della creazione di oggetti. Ciò non è tanto importante nel mondo quanto alcune altre cose (che influenzano la correttezza o la manutenibilità), ma non è niente e spesso è una buona idea per gli oggetti con molti attributi.

Non è tutto, ma spero che tu abbia l'idea: diversi modelli di creazione hanno usi e vantaggi diversi (benché simili). Sono raggruppati in una categoria principalmente perché ora ci sono troppi modelli di design per trattarli tutti come una lista enorme.

    
risposta data 06.11.2014 - 14:43
fonte
1

Un vantaggio comune di un modello creativo è di cambiare il comportamento del programma da una corsa all'altra. Questo potrebbe essere fatto da un argomento della riga di comando, per esempio. Forse specificando un interruttore per abilitare la funzionalità si ottiene una fabbrica che crea un diverso tipo di oggetto, ad esempio creando un diverso tipo di autore di output per un file rispetto all'output standard. Forse ci sono due tipi di controller che condividono un'interfaccia comune. Uno è interattivo, uno deve essere alimentato nei suoi input e funziona senza interazione dell'utente.

Ho un esempio concreto di un modello creativo: Log4j . Il seguente snippet XML viene copiato dalla configurazione XML di esempio nel manuale di Log4j .

Il codice che legge questa configurazione creerà un appender, un layout e filtri in base ai valori specificati in questa sezione. Li collegherà insieme in un grafico completo e utilizzabile degli oggetti.

<Appender type="Console" name="STDOUT">
  <Layout type="PatternLayout" pattern="%m MDC%X%n"/>
  <Filters>
    <Filter type="MarkerFilter" marker="FLOW" onMatch="DENY" onMismatch="NEUTRAL"/>
    <Filter type="MarkerFilter" marker="EXCEPTION" onMatch="DENY" onMismatch="ACCEPT"/>
  </Filters>
</Appender>

Sebbene la maggior parte delle applicazioni non delegherà gran parte delle specifiche di creazione dell'oggetto a configurazioni esterne, questa viene utilizzata in modo piuttosto pesante in alcune applicazioni. In uno dei miei precedenti lavori c'erano un paio di fabbriche e singleton usati per personalizzare il comportamento nell'applicazione e le implementazioni erano specificate nei file delle proprietà. Quando si richiede un'istanza, il codice controllerà il nome della classe da caricare (che deve essere la classe di inclusione o una sottoclasse), consentendo le estensioni del cliente.

Questo particolare codice è un breve esempio di come costruire un factory basato su una proprietà di sistema. Sarebbe possibile impostare una proprietà di sistema per utilizzare una sottoclasse che potrebbe quindi sovrascrivere il metodo createWidget() per creare diversi widget.

public class MyFactory {

  public static MyFactory createFactory() {
    String implName = System.getProperty(MyFactory.class.getName());
    if (implName == null) {
      return new MyFactory();
    }
    else {
      try {
        Class<?> impl = Class.forName(implName);
        return impl.newInstance();
      }
      catch (Exception e) {
        throw (e instanceof RuntimeException
            ? (RuntimeException) e : new RuntimeException(e));
      }
    }
  }

  public Widget createWidget() { ... }
}
    
risposta data 09.02.2015 - 05:44
fonte
1

In Java, se DerivedFoo: BaseFoo , dicendo foo = new DerivedFoo(boz); costruirà una nuova istanza "blank" DerivedFoo e chiamerà il costruttore a DerivedFoo che può assumere boz come parametro. A sua volta, il costruttore deve chiamare uno dei costruttori di BaseFoo prima di poter eseguire gran parte di qualsiasi cosa con l'oggetto in costruzione o con il parametro passato. Quando viene restituito il costruttore di BaseFoo , BaseFoo non avrà alcun ulteriore controllo sull'oggetto in costruzione.

Si noti che ci sono molti casi in cui il codice potrebbe sapere che ha bisogno di qualcosa che deriva da DerivedFoo , ma potrebbe non aver bisogno di un oggetto appena creato di quel tipo esatto. Tra le altre cose:

  • In alcuni casi, spesso identificabili in base ai parametri passati, qualche altro derivato di DerivedFoo potrebbe essere migliore.

  • In alcuni casi, specialmente con tipi immutabili, il client può essere meglio servito da un oggetto preesistente che da uno nuovo.

  • Alcuni tipi di oggetti devono notificare oggetti esterni quando sono creati e pronti per l'uso, ma la sequenza chiamante per i costruttori non notifica l'oggetto della classe base quando il costruttore più derivato ha terminato l'esecuzione, e doesn 'anche fornire un modo terribilmente conveniente per un costruttore per sapere se altri costruttori più derivati devono ancora eseguire.

I metodi factory possono facilmente superare tutti questi problemi, dal momento che possono restituire oggetti nuovi o preesistenti, e possono invocare il metodo% id_de% della classe base dopo che il costruttore più derivato è finito.

    
risposta data 09.02.2015 - 03:59
fonte

Leggi altre domande sui tag