Klasse mit vielen Parametern, die über das Builder-Muster hinausgehen

8

Kontext

Angenommen, Sie haben eine Komponente mit sehr vielen Optionen, um ihr Verhalten zu ändern. Denken Sie an eine Tabelle von Daten mit etwas Sortieren, Filtern, Paging, usw. Die Optionen könnten dann isFilterable , isSortable , defaultSortingKey usw. sein. Natürlich wird es ein Parameter-Objekt geben Um all diese einzukapseln, nennen wir es TableConfiguration . Natürlich wollen wir keinen riesigen Konstruktor oder eine Menge teleskopischer Konstruktoren haben, daher verwenden wir einen Builder , TableConfigurationBuilder . Die Beispielverwendung könnte sein:

%Vor%

So weit, so gut, eine Tonne von SO-Fragen beschäftigt sich schon damit.

Vorwärtsgehen

Es gibt jetzt eine Tonne Tables und jeder von ihnen verwendet seine eigene TableConfiguration . Es wird jedoch nicht der gesamte "Konfigurationsraum" einheitlich verwendet: Sagen wir, die meisten Tabellen sind filterbar, und die meisten davon sind paginiert. Nehmen wir an, es gibt nur 20 verschiedene Kombinationen von Konfigurationsoptionen, die sinnvoll sind und tatsächlich verwendet werden. Nach dem DRY-Prinzip leben diese 20 Kombinationen in Methoden wie diesen:

%Vor%

Frage

Wie man diese 20 Methoden verwaltet, so dass Entwickler, die neue Tabellen hinzufügen, die gewünschte Kombination leicht finden oder eine neue hinzufügen können, falls sie noch nicht existiert?

Alles oben genannte benutze ich bereits, und es funktioniert einigermaßen gut, wenn ich eine existierende Tabelle zum kopieren-einfügen habe ("es ist genau wie Kunden"). Aber jedes Mal, wenn etwas Außergewöhnliches erforderlich ist, ist es schwer herauszufinden:

  • Gibt es eine Methode, die genau das macht, was ich will? (Problem A)
  • Wenn nicht, welcher ist der nächste, von dem aus man am nächsten kommt? (Problem B)

Ich habe versucht, den Methoden einige sehr aussagekräftige Namen zu geben, um auszudrücken, welche Konfigurationsoptionen im Inneren erstellt werden, aber sie skaliert nicht wirklich gut ...

Bearbeiten

Als ich über die großen Antworten nachdachte, fiel mir noch eine Sache ein: Bonuspunkte für die typsichere Gruppierung von Tabellen mit gleicher Konfiguration. Mit anderen Worten, beim Betrachten einer Tabelle sollte es möglich sein, alle ihre "Zwillinge" durch etwas wie gehe zur Definition und finde alle Referenzen zu finden.

    
vektor 01.02.2016, 14:50
quelle

5 Antworten

2

Ich denke, wenn Sie das Builder-Muster bereits verwenden, dann wäre es am besten, sich an das Builder-Muster zu halten. Es gibt keine Vorteile darin, Methoden oder eine Enumeration zu haben, um build das am häufigsten verwendete TableConfiguration zu erstellen.

Sie haben jedoch einen gültigen Punkt in Bezug auf DRY. Warum setzen Sie die meisten gängigen Flags für fast jeden Builder an vielen verschiedenen Orten?

Sie müssten also die Einstellung der gebräuchlichsten Flags kapseln (um sich nicht zu wiederholen) und trotzdem zusätzliche Flags über diese gemeinsame Basis setzen. Außerdem müssen Sie auch spezielle Fälle unterstützen. In Ihrem Beispiel erwähnen Sie, dass die meisten Tabellen filterbar und paginiert sind.

Während das Builder-Muster Ihnen Flexibilität verleiht, können Sie die häufigsten Einstellungen wiederholen. Warum nicht spezielle Standard-Builder erstellen, die die am häufigsten verwendeten Flags für Sie festlegen? Mit diesen Optionen können Sie weiterhin zusätzliche Flags festlegen. Und für spezielle Fälle könnten Sie das Builder-Muster auf die altmodische Art und Weise verwenden.

Code für einen abstrakten Builder, der alle Einstellungen definiert und das eigentliche Objekt erstellt, könnte etwa so aussehen:

%Vor%

Und das wäre der Basis-Builder, der nichts tut:

%Vor%

Die Einbeziehung von BaseTableConfigurationBuilder soll Generika im Code, der den Builder verwendet, vermeiden.

Dann könnten Sie spezialisierte Builder haben:

%Vor%

Die Idee ist, dass Sie Builder haben, die die häufigsten Kombinationen von Flags setzen. Sie könnten eine Hierarchie erstellen oder haben keine Vererbungsbeziehung zwischen ihnen, Ihr Anruf.

Dann könntest du deine Erbauer benutzen, um alle Kombinationen zu erstellen, ohne dich selbst zu verstärken. Zum Beispiel würde dies eine filterbare und paginierte Tabellenkonfiguration erzeugen:

%Vor%

Und wenn Ihr TableConfiguration filterbar, paginiert und auch sortierbar sein soll:

%Vor%

Und eine spezielle Tabellenkonfiguration mit einer sehr seltsamen Einstellung, die auch sortierbar ist:

%Vor%     
Federico Peralta Schaffner 02.02.2016, 15:23
quelle
2

Ich würde Ihre Bequemlichkeitsmethoden entfernen, die mehrere Methoden des Builders aufrufen. Der springende Punkt eines solchen flüssigen Builders ist, dass Sie keine Methoden für alle akzeptablen Kombinationen erstellen müssen.

  

Gibt es eine Methode, die genau das macht, was ich will? (Problem A)

Ja, die Methode, die das tut, was Sie wollen, ist new TableConfigurationBuilder() . Übrigens, ich denke, es ist sauberer, das Builder-Konstruktorpaket privat zu machen und es über eine statische Methode in TableConfiguration zugänglich zu machen, dann kannst du einfach TableConfiguration.builder() aufrufen.

  

Wenn nicht, welcher ist der nächste, von dem aus zu starten? (Problem B)

Wenn Sie bereits eine Instanz von TableConfiguration oder TableConfigurationBuilder haben, kann es sinnvoll sein, sie an den Builder zu übergeben, sodass sie auf der Basis der vorhandenen Instanz vorkonfiguriert wird. Dies ermöglicht Ihnen beispielsweise Folgendes:

%Vor%     
rinde 02.02.2016 08:16
quelle
1

Wenn fast alle Konfigurationsoptionen boolesch sind, können Sie sie zusammen ODER:

%Vor%

Die Tabellenerstellung sieht jetzt nicht so hässlich aus:

%Vor%     
gudok 01.02.2016 17:39
quelle
1

Wie wäre es mit einer Konfigurationszeichenfolge ? Auf diese Weise könnten Sie die Tabelleneinstellungen auf eine prägnante, aber dennoch lesbare Art und Weise kodieren.

Als Beispiel wird festgelegt, dass die Tabelle s ortable und r nur ead-only:

ist %Vor%

Diese Zeichenfolgen ähneln in gewisser Weise der Befehlszeilenschnittstelle.

Dies könnte für andere Szenarios gelten, die die Wiederverwendung der Tabelle unterstützen. Mit einer Methode, die die Customers-Tabelle erstellt, können wir sie konsistent ändern:

%Vor%

Möglicherweise könnte diese DSL sogar verbessert werden, indem man ein Trennzeichen vorlegt, das die set und unset Operationen trennt. Das vorherige Beispiel würde dann folgendermaßen aussehen:

%Vor%

Außerdem können diese Konfigurationszeichenfolgen aus Dateien geladen werden, wodurch die Anwendung ohne Neukompilierung konfigurierbar ist.

Wenn ich einem neuen Entwickler helfe, sehe ich den Vorteil in einem gut getrennten Teilproblem, das leicht dokumentiert werden kann.

    
Daniel Lovasko 02.02.2016 07:46
quelle
1

Was hätte ich getan, wenn ich SO nicht gewusst hätte

Annahme: Es gibt wahrscheinlich weniger als 2 ^ (Anzahl der Konfigurationsflags) sinnvolle Konfigurationen für die Tabelle.

  1. Ermitteln Sie alle derzeit verwendeten Konfigurationskombinationen.
  2. Zeichne ein Diagramm oder was auch immer, finde Cluster.
  3. Finde Ausreißer und denke sehr genau darüber nach, warum sie nicht in diese Cluster passen: Ist das wirklich ein spezieller Fall oder eine Auslassung oder nur Faulheit (niemand hat die Volltextsuche für diese Tabelle implementiert noch )?
  4. Wählen Sie die Cluster aus, denken Sie über sie nach, packen Sie sie als Methoden mit beschreibenden Namen und verwenden Sie sie ab sofort.

Das löst das Problem A: Welches benutzt man? Nun, es gibt jetzt nur eine Handvoll Optionen . Und auch Problem B: Wenn ich etwas Besonderes haben möchte? Nein, wahrscheinlich nicht .

    
vektor 02.02.2016 08:42
quelle

Tags und Links