È antipattern a introdurre la complessità in un costruttore?

2

Ho esaminato varie definizioni del modello di builder e, sebbene esistano definizioni diverse, tendono a concentrarsi sull'ampia definizione di costruzione incrementale. Tuttavia, sembra che la maggior parte degli esempi di builder che ho mai visto essenzialmente costruisca solo tipi "value" con il concatenamento del metodo.

Ho trovato, tuttavia, che a volte può essere più utile utilizzare un tipo di modello completamente diverso nel builder per svolgere attività aggiuntive. Quindi, oltre a quello che la gente si aspetta in genere da un costruttore, questo costruttore:

  1. Può nascondere i dettagli del tipo che si sta creando
  2. Incorporare la logica di compilazione complessa nel builder
  3. Uso dello stesso builder per creare più tipi simili

Ho messo insieme un semplice esempio di una variante di builder che ho creato in Java e che speriamo possa dimostrare meglio le idee. (Nota: 1 non è molto ben dimostrato poiché richiederebbe molto più codice per illustrare)

class FamiliesBuilder {
    final Map<String, List<String>> familyMap = new HashMap<>();
    String lastName;
    List<String> familyList;

    FamiliesBuilder newFamily(String lastName) {
        this.familyList = this.familyMap.getOrDefault(lastName, new LinkedList<>());
        this.familyMap.put(lastName, this.familyList);
        this.lastName = lastName;
        return this;
    }

    FamiliesBuilder addFamilyMember(String firstName) {
        this.familyList.add(firstName);
        return this;
    }

    Map<String, List<Person>> buildLastNameLookup() {
        final Map<String, List<Person>> res = new HashMap<>();
        for (Entry<String, List<String>> e : this.familyMap.entrySet()) {
            final String personLastName = e.getKey();
            final List<Person> people = new LinkedList<>();
            for (String firstName : e.getValue()) {
                people.add(new Person(firstName, personLastName));
            }

            res.put(e.getKey(), people);
        }

        return res;
    }

    List<Person> build() {
        List<Person> people = new LinkedList<>();
        for (Entry<String, List<String>> e : this.familyMap.entrySet()) {
            String personLastName = e.getKey();
            for (String personFirstName : e.getValue()) {
                people.add(new Person(personFirstName, personLastName));
            }
        }

        return people;
    }

    class Person {
        private String firstName;
        private String lastName;

        private Person(String firstName, String lastName) {
            this.firstName = firstName;
            this.lastName = lastName;
        }
    }

    public static void main(String... args) {
        // Usage example
        List<Person> people = new FamiliesBuilder()
                .newFamily("Doe")
                .addFamilyMember("John")
                .addFamilyMember("Jane")
                .newFamily("Smith")
                .addFamilyMember("Tom")
                .build();
    }
}

Sono preoccupato che questa variante sia troppo lontana dal classico modello di builder che chiamarlo builder può essere fonte di confusione e, in tal caso, mi chiedo se ci siano modelli o tecniche di design riconoscibili che realizzano un'attività simile.

    
posta Kevin Raoofi 12.07.2018 - 22:37
fonte

2 risposte

4

Risposta breve: mi piace e costruisco i costruttori con le stesse caratteristiche. Non penso che questo sia un anti-modello.

dettagli:

Can hide details of the type being created

Se i dettagli non sono importanti, confusi o inclini agli errori per l'utente del tipo, devi assolutamente nasconderli nel builder. Il costruttore è l'esperto in modo che il consumatore del tipo non debba essere.

Embedding complex build logic into the builder

La complessità fondamentale del tipo, perché il "dominio aziendale" del codice è di per sé complesso? Quindi, assolutamente, incapsula la complessità da qualche parte . Che sia nel builder o nel tipo in costruzione, non ha importanza per me, ma dovrebbe andare da qualche parte.

Using the same builder to create multiple similar types

Questo è il tuo punto più debole. Il riutilizzo dei codici è sopravvalutato e la generalità ha molti costi . Spesso è più economico e più semplice scrivere due soluzioni specifiche piuttosto che scrivere una soluzione generale e specializzarla in due modi.

    
risposta data 12.07.2018 - 22:59
fonte
1

Non penso che l'esempio che hai fornito sia il modello Builder .

Il pattern Builder è un modo di creare un oggetto immutabile che ha molti parametri opzionali. La ragione principale è che non è necessario avere un numero ridicolo di costruttori (o metodi di fabbrica) a causa di tutte le possibili combinazioni. (Offre anche il vantaggio del concatenamento dei metodi che può rendere il codice più facile da leggere).

Se gli oggetti non sono immutabili, il pattern non ha alcun reale scopo, dato che l'oggetto può essere creato e avere il set di parametri desiderato in seguito, sia attraverso l'accesso diretto al campo che attraverso i metodi setter.

Nell'esempio che hai dato stai in realtà semplicemente avvolgendo un oggetto List<Person> e fornendo funzionalità da aggiungere ad esso oltre a fornire un modo per ottenere una HashMap & gt ;.

In questo caso, un modo migliore per farlo sarebbe:

public class Family {
    private String lastName;
    private String[] firstNames;

    //alternatively there can be a List<Person> field instead of lastName and firstNames

    public Family(String lastName, String firstNames){
        //code omitted...        
    }

    public static HashMap<String, List<Person>> getLastNameLookup<Collection<Family>> families) {
        //code omitted...
    }
}

Il codice potrebbe quindi essere usato in questo modo:

List<Family> families = new ArrayList<>();
families.add(new Family("Doe", new String[] { "John", "Jane" });
families.add(new Family("Smith", new String[] { "John" });

HashMap<String, List<Person>> lastNameLookup = Family.getLastNameLookup(families);

In sintesi, penso che sia un anti-pattern perché la complessità aggiunta non fornisce alcun valore. Non ci sono informazioni nascoste qui che non potrebbero essere nascoste altrettanto bene nella classe stessa. Penso che il modello Builder debba essere usato solo per creare oggetti immutabili che possono avere molti valori opzionali come modo per evitare di dover introdurre un numero molto grande di costruttori o metodi di fabbrica.

    
risposta data 12.07.2018 - 23:04
fonte

Leggi altre domande sui tag