Quali sono i vantaggi dell'utilizzo di una "fabbrica di decorazioni" che decora gli oggetti?

0

In un progetto ho deciso di implementare il pattern Decorator.

Ho una classe Thing con methodA() , e una classe AbstractDecorator che eredita da Thing e che tutti i decoratori ereditano da: ConcreteDecorator1 , ConcreteDecorator2 , ecc. Tutti ovviamente override methodA() per aggiungere alcune funzionalità prima di delegare al% co_de impacchettato. Implementazione usuale di Decorator.

Ho deciso di implementare un Thing (per la mancanza di un nome migliore): riceve WrappingFactory oggetti e li avvolge con un decoratore specificato. Alcuni decoratori richiedono un parametro nel costruttore e Thing si occupa anche di questo. Eccolo:

public class WrappingFactory{

    public static void addSomeFunctionality(Thing thing){
        thing = new SomeDecorator(thing);
    }

    public static void addFunctionalityWithParameter(Thing thing, int param){
        thing = new DecoratorWithParameter(thing, param);
    }

    public static void addSomeAwesomeFunctionality(Thing thing){
        thing = new AwesomeDecorator(thing);
    }

}

L'ho fatto ma in realtà non so perché. Questo ha dei vantaggi rispetto al fatto che il cliente istanzia direttamente i decoratori?

Se questo ha dei vantaggi, ti preghiamo di spiegarmeli.

    
posta Aviv Cohn 29.05.2014 - 20:05
fonte

4 risposte

3

In primo luogo, i commenti di MichaelT sono chiari. E non c'è assolutamente nessun assolutamente motivo per creare una classe AbstractDecorator .

Detto questo, qui è un esempio di" fabbrica di decorazioni ": la factory determina, in base al nome file di input, se aggiungere o meno un decodificatore GZip in la pila di ruscelli decorati.

public static InputStream openFile(File file)
throws IOException
{
    InputStream stream = null;
    try
    {
        stream = new FileInputStream(file);
        stream = new BufferedInputStream(stream);
        if (file.getName().endsWith(".gz"))
            stream = new GZIPInputStream(stream);
        return stream;
    }
    catch (IOException ex)
    {
        closeQuietly(stream);
        throw ex;
    }
}

L'altro - principale - motivo per cui questo factory è utile è che gestisce correttamente la pulizia nel caso in cui uno dei costruttori fallisca. Il che è qualcosa che (1) molti programmatori (me compreso) non avranno diritto se fanno chiamate esplicite, e (2) eliminano il codice di stam- pa.

    
risposta data 29.05.2014 - 21:42
fonte
1

Supponendo che il tuo codice sia C #, allora il tuo codice è sbagliato. Stai creando il decoratore, ma non lo restituisci.

O, Thing parametro dovrebbe essere ref :

public static void addSomeFunctionality(ref Thing thing){
    thing = new SomeDecorator(thing);
}

Oppure return dell'istanza decoratore appena creata:

public static Thing addSomeFunctionality(Thing thing){
    return new SomeDecorator(thing);
}

Il primo caso rende difficile l'utilizzo su un'istanza appena creata, poiché devi creare una variabile temporanea su ref contro. Il secondo è praticamente inutile e penso che abbia violato YAGNI.

Ma una modifica potrebbe renderla molto più utile, e questo sta rendendo i metodi statici in metodi di estensione.

public static Thing addSomeFunctionality(this Thing thing){
    return new SomeDecorator(thing);
}

In questo modo puoi incatenarli facilmente:

var finalThing = new BasicThing().addSomeFunctionality().addFunctionalityWithParameter("param");

Ma se tutti i metodi statici fanno call al costruttore, allora andrei in modo YAGNI e non mi preoccuperei.

    
risposta data 22.09.2017 - 09:51
fonte
0

Come sempre con il modello di factory statico, ottieni il vantaggio di poter cambiare facilmente implementazione-creazione.

Ad esempio, diciamo che hai un decoratore che accetta un tipo come argomento:

public enum Type{TYPE_A,TYPE_B}

public class ThingWithType implements Thing{
    private Type type;
    private Thing thing;

    public ThingWithType(Thing thing,Type type){
        this.thing=thing;
        this.type=type;
    }
}

public class WrappingFactory{
    public Thing addType(Thing thing,Type type){
        return new ThingWithType(thing,type);
    }
}

Ora supponiamo di voler separare ThingWithType in due classi, una per TYPE_A e una per TYPE_B . Ora puoi fare:

public enum Type{TYPE_A,TYPE_B}

public class ThingWithTypeA implements Thing{
    private Thing thing;

    public ThingWithTypeA(Thing thing){
        this.type=type;
    }
}

public class ThingWithTypeB implements Thing{
    private Thing thing;

    public ThingWithTypeB(Thing thing){
        this.type=type;
    }
}

public class WrappingFactory{
    public Thing addType(Thing thing,Type type){
        switch(type){
            case TYPE_A: return new ThingWithTypeA(thing,type);
            case TYPE_B: return new ThingWithTypeB(thing,type);
            default: throw new Error(String.format("Can't handle type %s",type));
        }
    }
}

E non devi modificare i luoghi effettivi in cui chiami WrappingFactory.addType . Se non avevi WrappingFactory , dovresti cercare i luoghi nel tuo codice in cui avvolgi Thing con ThingWithType e sostituiscilo lì.

Modifica:

Ho letto il commento di MichaelT e devo essere d'accordo. I modelli di progettazione sono soluzioni e sembra che tu abbia una soluzione e ora stai cercando un problema.

    
risposta data 29.05.2014 - 20:24
fonte
-2
  1. Non dovresti dipendere da lezioni concrete. Per assicurarti di non farlo, potresti utilizzare una fabbrica di qualche tipo.

  2. Potresti voler cambiare il modo in cui decorare il tuo oggetto in un secondo momento, quindi incapsulare la parte decorativa ti manterrebbe flessibile. Penso che uno dei motivi per usare decoratori sia che potresti volerne usare più di uno. Altrimenti potresti semplicemente sottoclassi. E potresti cambiare i tuoi decoratori ad un certo punto. O impilare più decoratori in cima. Lancio qui il Principio sulla sostituzione di Liskov - potresti non voler nemmeno sapere come hai decorato il tuo oggetto. Quindi meglio non dipendere da decoratori concreti.

Quindi dico che usare una fabbrica per decorare i tuoi oggetti è una buona cosa. Forse anche un pattern Builder.

Tieni presente che ho sempre in mente i test unitari, perché le dipendenze di testabilità possono essere problematiche.

    
risposta data 22.09.2017 - 09:21
fonte

Leggi altre domande sui tag