Designfrage zum Polymorphismus

8

Zuerst tut mir leid für die lange Frage, aber ich konnte es nicht kürzer schreiben:)

Beispiel aus der realen Welt: Wir haben eine große Papierrolle, auf der kleine "Aufkleber" aufgedruckt sind. Jeder Aufkleber hat einen Code. Die ersten zwei Buchstaben des Codes sagen uns, welche Art von Aufkleber das ist (Aufkleber, der eine neue Rolle darstellt, Aufkleber, der das Ende der aktuellen Rolle darstellt, Aufkleber, der zur Qualitätskontrolle gehen soll, ... aber die meisten von ihnen sind normale aufgezählte Aufkleber) .

Zum Beispiel bedeutet Aufkleber mit dem Code XX0001, dass danach nur normale Aufzählungscodes (wie NN0001 bis NN9999) immer die gleiche Nummer haben sollen. Der Code QC0001 sagt uns, dass die nächsten 10 Codes (von QC0001 bis QC0010) zur Qualitätskontrolle gehen sollten.

Ich habe die Anwendung so entworfen, dass jeder Typ eines Aufklebers seine eigene Klasse hat - NormalSticker , BadSticker , ControllSticker , QualitySticker , ... Sie alle erben von einer SticerBase-Klasse, die einige enthält gemeinsame Daten für alle (Qualität des Scans, Datum und Uhrzeit des Scans, Inhalt des Codes). Instanzen dieser Klassen werden in einer statischen Parser-Klasse erstellt, die den Code prüft und das entsprechende Objekt an uns zurückgibt.

Das geht alles gut, aber jetzt bin ich stehen geblieben. Ich habe auch eine Roll -Klasse, die eine Reihe von Stickern hat, implementiert als List<StickerBase>. Diese Klasse hat eine öffentliche AddSticker(StickerBase) -Methode, mit der wir Sticker zur Rolle hinzufügen. Aber diese Methode sollte etwas Logik enthalten, zum Beispiel, wenn wir den Code XX001 bekommen, dann sollten die nächsten 9999 Aufkleber von NN0001 bis NN9999 sein. Die einzige Option, die ich hier sehe, ist, Entscheidungen basierend auf dem Typ des Aufklebers zu treffen, wie:

%Vor%

Ich wäre wirklich überrascht, wenn dies der richtige Ansatz ist. Irgendwelche Ideen?

Bearbeiten - mögliche Lösung: Weil ich für jeden Sticker weiß, wie der nächste aussehen soll, kann ich jeder public Sticker NextStickerShouldLookLike() -Klasse eine neue Methode Sticker hinzufügen. In der Validierungslogik (ähnlich wie bei Péter Töröks Antwort ) kann ich einfach nachsehen, ob der aktuelle Aufkleber ist ist gleich wie previousSticker.NextStickerShouldLookLike() . Die Validate-Methode würde zwei Eingabeparameter haben - aktuelle und vorherige Aufkleber.

    
sventevit 03.02.2011, 11:08
quelle

7 Antworten

2

Ich kann sehen, dass die Debatte in den anderen Antworten wütet, aber ich bin mir nicht sicher, ob es möglich ist, zu einer guten Lösung zu kommen, ohne Ihr Beispiel genauer zu untersuchen.

Erstens haben Sie drei Klassen, die Rolle, den Parser, der die Aufkleber und die Aufkleber selbst erstellt (unterteilt in die abgeleiteten Klassen). Vielleicht gibt es eine andere Klasse, die eine Geschäftslogik implementiert, die Sie nicht erwähnt haben? Es könnte eine Beschreibung wert sein ...

Die erste Frage ist: Da einige Klassen dafür verantwortlich sind, die Aufkleber an die Rolle anzuhängen (die Parser-Klasse?), Können Sie die Rolle völlig ambivalent belassen, zu welchen Stickern sie gehören, und Ihre Logik an anderer Stelle platzieren? Ist die Logik, was zu tun ist, wenn Aufkleber vom Typ X wirklich etwas für die Rolle ist, zu wissen, da es nicht die Rolle ist, die ausgeht und die Aufkleber bekommt?

Der zweite ist: Wie polymorph sind deine Sticker? Haben sie unterschiedliche Methoden? Haben sie unterschiedliche Eigenschaften? Oder sind sie ähnlich genug, dass Sie einfach ein Label auf eine StickerBase-Klasse setzen können?

Der dritte ist: Sollte es an den Aufklebern liegen, der Rolle zu sagen, was zu tun ist? I.E. sollte die Rolle die Methode sticker.TellMeWhatToDoNextPlease() aufrufen (als virtuelle Methode implementiert und in den abgeleiteten Klassen von Aufklebern überliefert) - besonders, da die Rolle nicht dafür verantwortlich ist, was die Leute darauf anlegen wollen. Sie können die gleiche Frage der Klasse stellen, die dafür verantwortlich ist, die Aufkleber auf die Rolle zu setzen. Du könntest einen Affen ausbilden und es der Walze überlassen, oder du könntest deine Logik dorthin bringen (wenn dort die Aufkleber gemacht werden, sollte sie schon wissen, was sie macht) und die Rolle akzeptieren lassen, woran sie festhält .

Grundsätzlich - Was steuert Ihren Prozess? Die Papierrolle, auf der Aufkleber aufgeklebt sind, die Aufkleber selbst oder was auch immer die Aufkleber macht und klebt?

    
James Walford 03.02.2011, 13:36
quelle
6

Möchten Sie hinzufügen die mit einem bestimmten Aufkleber verknüpften Aufkleber in einem Zug hinzufügen oder möchten Sie validieren , dass die hinzugefügten Aufkleber übereinstimmen die Beschränkungen, die durch den spätesten speziellen Aufkleber eingestellt werden?

Im ersten Fall könnten Sie eine polymorphe GetAssociatedStickers() -Methode zu Ihren Sticker-Klassen hinzufügen, die den Satz von Stickern von NN0001 bis NN9999 zu einem Sticker mit Code XX001 usw. zurückgibt. Dann können Sie diesen Satz von Stickern direkt danach hinzufügen der Kontrollaufkleber.

Aktualisieren

Für die Validierung könnten Sie eine neue Schnittstelle StickerValidator und eine Methode GetValidator in Ihren Sticker-Klassen haben. Spezielle Sticker würden ein korrektes Validator-Objekt (das als anonyme Klasse oder eine innere Klasse implementiert werden könnte) zurückgeben, während normale Sticker null zurückgeben würden. Dann kann AddSticker so modifiziert werden, dass es wie

aussieht %Vor%     
Péter Török 03.02.2011 11:14
quelle
4

Bedingter Ausdruck basierend auf dem Typ ist ein Antipattern, und Sie haben Recht damit, dass Sie versuchen sollten, es zu vermeiden. Eines der Probleme besteht zum Beispiel darin, dass Sie diese Methode jedes Mal aktualisieren müssen, wenn Sie eine neue Unterklasse StickerBase erstellen. Ein weiteres Problem besteht darin, dass die Methodensignatur signalisiert, dass ein Aufrufer eine Implementierung von StickerBase übergeben kann, aber tatsächlich nur einige wenige unterstützt werden.

Wenn möglich, platzieren Sie Ihre Logik in den Sticker Implementierungen. Verwenden Sie eine abstrakte Methode in der Klasse StickerBase und überschreiben Sie sie in den Unterklassen. Auf diese Weise müssen Sie nur einen Methodenaufruf in der Methode AddSticker durchführen und müssen nicht wissen, welche Art von Aufkleber hinzugefügt wird.

Wenn dies nicht möglich ist und Sie Ihren Code wirklich mit verschiedenen Arten von Stickern in Ihrer Roll -Klasse behandeln müssen, sollten Sie sich die Besucher Muster. Versuchen Sie dies zu vermeiden und gehen Sie nach Möglichkeit mit dem ersten Ansatz.

    
Andreas Vendel 03.02.2011 11:34
quelle
2

Erstellen Sie eine virtuelle Funktion GetNextStickerLabel (); und implementieren Sie es anders in jedem Aufklebertyp. Als allgemeine Regel, wenn Sie fragen müssen, welcher Typ ein Objekt ist und eine Entscheidung treffen (wenn), ist etwas falsch im Design.

    
Felice Pollano 03.02.2011 11:13
quelle
2

Es sieht so aus, als wäre das Muster Besucher (möglicherweise mit Iterator-Muster) ein perfekter Kandidat hier.

Ich denke, dass das Annehmen von Aufklebern als erstklassiger Bürger und das Erstellen einer eigenen Klasse bereits ein guter Anfang für eine elegante Lösung in Ihrem Fall ist.

Sie sollten zuerst eine Besucherschnittstelle (oder eine Basisklasse) für Ihre Aufkleberhierarchie erstellen.

%Vor%

Dann müssen Sie eine abstrakte Accept -Methode zu Ihrer StickerBase-Klasse hinzufügen, die einen Besucher als Parameter wie folgt verwendet;

%Vor%

Der Inhalt dieser Methode in den konkreten Klassen sollte einfach wie folgt aussehen;

%Vor%

An dieser Stelle können Sie einen bestimmten Besucher erstellen, sagen wir StickerRollerVisitor , der die erforderliche Logik enthält, um Aufkleber zu der gewünschten Liste hinzuzufügen.

%Vor%

In dieser Besucherimplementierung können Sie einen Iterator anstelle der ursprünglichen Liste akzeptieren, um hineinzugehen (vorwärts, rückwärts, überspringen usw.) die Liste mit einer benutzerdefinierten Strategie.

Nach dem Erstellen eines Standard StickerRollerVisitor in Ihrem RollList oder dem Akzeptieren des Besuchers als Konstruktorparameter, könnte Ihr Code wie folgt aussehen:

%Vor%

Nachdem Sie dieses Muster in Ihr Design eingefügt haben, können Sie es auf viele verschiedene Arten verwenden, indem Sie neue Besucher erstellen. Es wird eine großartige Verbesserung / Ergänzung für Ihr Design sein.

    
orka 03.02.2011 13:55
quelle
0

Ich würde es trotzdem auf diese Weise umschreiben:

%Vor%

...

    
Davide Piras 03.02.2011 11:13
quelle
0

Sie haben eine gute Möglichkeit, das zu lösen.

Sie können eine Aufzählung von Beschriftungsarten und ein Attribut implementieren.

%Vor%

Jetzt können Sie eine Klasse namens "StickerLabeler" haben, die eine Methode namens "NextLabel" implementiert, die einen Eingabeparameter vom Typ StickerKinds akzeptiert und eine Zeichenfolge zurückgibt, die das gesamte Label als Text darstellt.

Und schließlich eine Erweiterungsmethode zum einfachen Abrufen von Attributen:

%Vor%

Ihr Code sieht folgendermaßen aus:

%Vor%

EDIT: Ich habe vergessen zu erwähnen, dass Sie das StickedAttribute in Ihren Unterklassen von StickerBase verwenden werden:

%Vor%

UPDATE: Nun, in der Tat gibt es keinen Grund mehr, Unterklassen zu bilden. Sie können eine Aufkleber-Klasse haben und dieses Attribut wird die Art des Aufklebers entscheiden.

UPDATE 2: Sie können eine weitere Verbesserung vornehmen. Implementieren Sie eine schreibgeschützte Eigenschaft in der Sticker-Klasse "Kind", die das Attribut lesen kann und den Enumeration-Wert von StickerKinds des so genannten Attributs zurückgibt, sodass Ihr Code jetzt noch sauberer sein kann:

%Vor%

UPDATE 3: Andreas - ein Kommentator meiner Antwort - ließ mich denken, dass Sie Unterklassen benötigen könnten, da jede Art von Aufkleber seine eigenen Eigenschaften haben würde und das so genannte Attribut würde auf diese abgeleiteten Klassen angewendet werden.

    
Matías Fidemraizer 03.02.2011 11:34
quelle