Pattern di progettazione sconosciuto

2

Attualmente sto cercando di ridefinire un po 'di codice e uno dei problemi che ho riscontrato è che i costruttori avevano troppi parametri (15 in effetti) e sono stati inizializzati da un altro oggetto che aveva la stessa quantità di proprietà.

Ho intenzione di ridurre la quantità di classi che abbiamo quindi creando una serie di classi che rappresentavano le diverse parti dei parametri, sembrava sciocco quindi ho iniziato a cercare e la risposta migliore che ho trovato è stata da questa domanda su stackoverflow .

public class DoSomeActionParameters
{
    readonly string _a;
    readonly int _b;

    public string A { get { return _a; } }
    public int B{ get { return _b; } }

    DoSomeActionParameters(Initializer data)
    {
        _a = data.A;
        _b = data.B;
    }

    public class Initializer
    {
        public Initializer()
        {
            A = "(unknown)";
            B = 88;
        }

        public string A { get; set; }
        public int B { get; set; }
    }

    public static DoSomeActionParameters Create(Action<Initializer> assign)
    {
        var i = new Initializer();
        assign(i)

        return new DoSomeActionParameters(i);
    }
}

che può essere chiamato così usando un lambda

DoSomeAction(
    DoSomeActionParameters.Create(
        i => {
            i.A = "Hello";
        })
    );

Mi è piaciuto molto questo metodo anche se non risolve direttamente il problema di inizializzare effettivamente la classe, vuol dire che posso avere un costruttore invece di 3. Inoltre non richiede che la mia classe sappia oggetti a cui non importa.

Il mio problema è comunque, non so come viene chiamato questo schema, quindi non posso fare ulteriori ricerche su di esso per scoprire se è ancora valido, è sostituito da un altro modello o se ha gravi problemi di prestazioni. Dalla mia ricerca, il modello più vicino che ho trovato è il modello di builder, ma sembra essere più un'estensione su questo.

Come punto bonus, sarebbe bello se tu potessi darmi una mano con come rendere certi parametri obbligatori dato che abbiamo circa 10 parametri dal database e altri 5 che sono solo bandiere che non abbiamo davvero bisogno di sapere.

    
posta Keithin8a 20.07.2015 - 14:08
fonte

3 risposte

1

Sto creando la mia risposta (dopo una discussione sulla meta) perché ritengo che la risposta corretta sia in realtà una combinazione di tutto finora.

In primo luogo, come ha detto DavidArno, lo schema che ho chiesto è più di un anti-modello perché nasconde il problema da qualche altra parte. Tuttavia, credo che il suggerimento di utilizzare il modello di builder dalla risposta di Carl Manaster risolva il problema che ho perché non riesco a suddividere l'elenco dei parametri senza sovrastimare il Principio di Responsabilità Unica.

Il problema che avevo con il modello di builder era che non c'era modo di rendere più facile per uno sviluppatore che lo utilizza conoscere i campi obbligatori, il che significa che ci saranno un sacco di tentativi ed errori che porteranno a copia e incolla l'inizializzazione ovunque che porterà a più errori. Fuhrmanator tuttavia collegato al seguente blog che dimostra come avere un obbligo campi con il modello di builder (dopo aver guardato altrove credo che questo sia chiamato Step Builder).

In sostanza, il principio di Step Builder è di guidare uno sviluppatore attraverso l'inizializzazione di tutti i campi obbligatori. Il tuo costruttore implementa varie interfacce che fungono da passaggi per creare l'oggetto. Ognuno di loro ha un metodo che ha un tipo di ritorno del prossimo passo. Ciò aiuta l'intellisense più del patter standard del builder, perché riduce la quantità di metodi che puoi chiamare. Guarda il seguente esempio dal blog.

public class Address {
private String protocol;
private String url;
private int port;
private String path;
private String description;

// only builder should be able to create an instance
private Address(Builder builder) {
    this.protocol = builder.protocol;
    this.url = builder.url;
    this.port = builder.port;
    this.path = builder.path;
    this.description = builder.description;
}

public static Url builder() {
    return new Builder();
}

public static class Builder implements Url, Port, Build{
    private String protocol;
    private String url;
    private int port;
    private String path;
    private String description;

    /** Mandatory, must be followed by {@link Port#port(int)}  */
    public Port url(String url) {
        this.url = url;
        return this;
    }

    /** Mandatory, must be followed by methods in {@link Build}  */
    public Build port(int port) {
        this.port = port;
        return this;
    }

    /** Non-mandatory, must be followed by methods in {@link Build}  */
    public Build protocol(String protocol) {
        this.protocol = protocol;
        return this;
    }

    /** Non-mandatory, must be followed by methods in {@link Build}  */
    public Build path(String path) {
        this.path = path;
        return this;
    }

    /** Non-mandatory, must be followed by methods in {@link Build}  */
    public Build description(String description) {
        this.description = description;
        return this;
    }

    /** Creates an instance of {@link Address} */
    public Address build() {
        return new Address(this);
    }
}

interface Url {
    public Port url(String url);
}

interface Port {
    public Build port(int port);
}

interface Build {
    public Build protocol(String protocol);
    public Build path(String path);
    public Build description(String description);
    public Address build();
}

Hai la collezione di parametri obbligatori che restituiscono ciascuna l'istruzione successiva, ma anche quello che penso sia ottimo per questo è che puoi avere anche dei parametri facoltativi, rendendo i metodi restituire l'interfaccia di compilazione.

Questo risolve il mio problema perché avevo parametri obbligatori mescolati con quelli opzionali che rendevano difficile l'espansione in quanto c'era un sacco di copia e incolla e potenziali problemi. Con Step Builder posso controllare in che modo uno sviluppatore crea il mio oggetto in modo tale che io abbia fatti da usare in futuro e concedo loro la flessibilità di creare l'oggetto in tutti i modi possibili.

Inoltre lo rende molto leggibile!

    
risposta data 21.07.2015 - 16:32
fonte
13

Lo definirei il pattern "Elephant in the Room" (anti). Ti stai concentrando sulle minuzie mentre ignori il problema più grande. Se hai una classe che richiede 15 parametri di costruzione, allora questo è un segnale di avvertimento che la classe sta facendo troppo e quindi ha bisogno di troppa configurazione.

Il "motivo" di cui hai bisogno qui è quindi il principio di responsabilità singola . Esaminare la classe, calcolare quante (probabilmente abbastanza distinte) cose sta facendo e iniziare a scomporle in classi più piccole e più mirate. Ciascuno avrà quindi bisogno solo di alcuni parametri del costruttore.

Per cominciare, questo creerà più classi; ma saranno delle buone lezioni. Quando applichi questo al pasticcio mutato di classi simili, sarai in grado di sostituire grandi parti di quelle con le tue nuove classi e quello che resterà sarà di nuovo una qualità migliore, classi più mirate.

    
risposta data 20.07.2015 - 14:56
fonte
3

Dai un'occhiata al Pattern Builder . Risolve esattamente il problema che si sta verificando: troppi parametri in un costruttore. Si finisce con un costruttore vuoto (tipicamente in Java questo sarà un metodo privato, se si usa una classe interna come Builder) e i metodi setter (che possono anche rimanere privati), quindi una classe Builder con metodi concatenati che chiamano i setter e un metodo build () che costruisce l'oggetto e popola i suoi campi.

    
risposta data 20.07.2015 - 16:00
fonte

Leggi altre domande sui tag