8 Stimmen

Auf der Suche nach einem Entwurfsmuster, um huuuge if on object Type zu ersetzen

OK, also ich bin auf der Suche nach einem Code, der in etwa wie folgt aussieht:

void DoSomething(object o)
{
    if (o is Sometype1) { 
    //cast o to Sometype and do something to it
    }
    else if (o is Sometype2) {
    //cast o to Sometype2 and do something to it
    }
    ...
    else if (o is SometypeN) {
    //cast o to SometypeN and do something to it
    }
}

Ein Ansatz wäre nun, alle Objekte zu o die als Parameter verwendet werden, implementieren eine Schnittstelle wie

interface ICanHaveSomethingDoneToMe
{
    //expose various properties that the DoSomething method wants to access
}

Das Problem dabei ist jedoch, dass ich nicht möchte, dass alle meine Objekte diese Schnittstelle implementieren - die Logik für die "do something"-Methode gehört nicht wirklich zu ihnen. Welches Muster sollte ich verwenden, um damit umzugehen?

Ich vermute so etwas wie eine Reihe von Implementierungen von

interface IPropertiesForDoingSomethingTo<T>
{
    //expose various properties that the DoSomething method wants to access
}

könnte besser sein. Ich würde eine Implementierung für jeden der Objekttypen haben, für die ich das tun möchte, aber dann habe ich dieses neue Problem. Ich müsste dann eine Methode haben wie

IPropertiesForDoingSomethingTo<T> GetPropsGeneric(T t);

aber muss dafür ein massiver Schalter verwendet werden? Soll ich eine Klasse mit vielen Methoden definieren wie

IPropertiesForDoingSomethingTo<Someobject1> GetProps(Someobject1 t);
...
IPropertiesForDoingSomethingTo<Someobject1> GetProps(SomeobjectN t);

Dies hat gegenüber der generischen Version den Nachteil, dass man zur Laufzeit keine neuen Typen hinzufügen kann. Gibt es etwas schlaues, was man mit einem DI-Container in GetPropsGeneric machen kann, um den Container aufzulösen? Vielen Dank!

6voto

Steven A. Lowe Punkte 59247

Jedes Mal, wenn Sie eine switch-Anweisung (oder eine Reihe von if-Anweisungen) sehen, die den Typ eines Objekts prüfen, ist dies ein deutlicher Hinweis auf eine fehlende Basisklasse oder Schnittstelle. Mit anderen Worten, der Code sollte sich auf Polymorphismus verlassen und nicht den Objekttyp testen.

Wenn Sie die Basisklasse nicht ändern oder eine Schnittstelle implementieren können, bleibt Ihnen wahrscheinlich nur ein Wörterbuch, um dynamisches Dispatching zu simulieren. In C# könnten Sie einen anonymen Delegaten für die Methode verwenden, die den Cast enthält

was den Eigenschaftszugriff betrifft, wenn die Eigenschaften nicht konform sind und der Zugriff über Reflexion keine Option ist, müssen Sie möglicherweise die Eigenschaftswerte in der Methode/dem Delegat oben extrahieren und sie stattdessen an eine generische Funktion übergeben

1 Stimmen

Sie könnten einen Wrapper um eine beliebige Klasse implementieren, die Sie nicht ändern können, und sie Ihre Schnittstelle implementieren lassen. Dann haben Sie nur mit Objekten zu tun, die gewrappt wurden. Ich mache das zum Beispiel mit DataContext, um Unit-Tests zu erleichtern.

0 Stimmen

Wie wäre es, wenn Sie eine if-Anweisung/einen Schalter haben, um einen Parameter zu prüfen und dann ein Objekt zu erstellen, d.h. das Factory Pattern

0 Stimmen

@Franco aus dem Stegreif würde ich sagen, dass Sie das Factory Pattern falsch interpretiert haben; siehe de.wikipedia.org/wiki/Fabrik-Methoden-Muster

2voto

bobwienholt Punkte 17040

Es sieht so aus, als würden Sie C# verwenden. Ich glaube, dass Sie "Erweiterungsmethoden" erstellen können, die an bereits bestehende Klassen angehängt werden.

Ein anderer Ansatz wäre die Erstellung von Handler-Delegaten für jeden Typ und die Speicherung von Verweisen auf die Delegaten in einer nach Objekttyp verschlüsselten Hashtabelle.

Dann könnte Ihre "DoSomething"-Methode einfach den passenden Delegaten nach dem Typ des übergebenen Objekts suchen und ausführen.

0voto

tvanfosson Punkte 506878

Ein konkretes Beispiel wäre hilfreicher. Wenn Sie einfach nur die Implementierung einer Methode für eine Familie verwandter Klassen ändern, dann sollten Sie, wie @Steven A. Lowe sagt, am besten Polymorphismus verwenden und dafür die Unterklassenbeziehungen nutzen. Wenn die Klassen nicht an "is a"-Beziehungen beteiligt sind, dann sind andere Muster wie Besucher ist vielleicht besser geeignet.

0voto

Paul Nathan Punkte 38618

Polymorphismus ist die Antwort, bei der ein Basisobjekt übergeben wird. Allerdings ist dies deutlich mehr Boilerplate und semantische Komplexität, als Sie vielleicht wollen.

Es wird erforderlich sein, Ihre Funktionalität irgendwie in die abgeleiteten Klassen zu verlagern und eine "virtuelle" Funktion zu implementieren.

0voto

Paul Sonier Punkte 37609

Ich denke, dies ist eher eine Frage für einen aspektorientierten Programmieransatz.

Ich würde wollen, dass Ihre DoSomething-Methode eine ICanHaveSomethingDone-Schnittstelle als Parameter annimmt. Dann würde ich die Schnittstelle ICanHaveSomethinhgDone definieren und von ihr Unterklassen ableiten (eine für jedes Objekt, mit dem Sie etwas tun möchten), die DoSomethingToMe implementieren, und zwar für jede implementierende Klasse unterschiedlich. Jede von ihnen nimmt einfach einen Konstruktor des Typs, mit dem Sie etwas tun wollen, so dass Sie, wenn Sie die DoSomething aufrufen, tatsächlich eine Factory aufrufen (sehr einfach, nur die Erstellung einer Instanz Ihrer ICanHaveSomethingDone-Klasse aus dem Eingabetyp, um eine Instanz einer Klasse zu erstellen, die die DoSomethingToMe-Methode implementiert, und die den richtigen Code für das zugrunde liegende Objekt hat.

Stellen Sie sich das im Wesentlichen so vor: Sie definieren einen Schnittstellenvertrag, den die Parameterobjekte implementieren sollen, und definieren "Dekoration" in Form von Unterklassen Ihres Objekts und der Schnittstelle, die bestimmte Implementierungen des Schnittstellenverhaltens (und damit die Erfüllung des Vertrags) für Ihre spezifische Klasse implementieren. Auf diese Weise können Sie Ihre Implementierung der DoSomething-Methode für jede der Klassen vollständig von der Quelle dieser Klassen trennen.

Das bedeutet, dass Sie zur Laufzeit neue Typen hinzufügen können, wenn Sie Ihre Fabrik zu einem DI-Container machen. Indem Sie die neuen Typen, mit denen Sie etwas tun möchten, in Ihren Fabrik-Container injizieren, können Sie eine Implementierung der gewünschten Aktion als Klasse definieren, die von Ihrer Schnittstelle und dieser Klasse abgeleitet ist. Sie können das Verhalten sogar zur Laufzeit definieren, ohne die abgeleitete Klasse implementiert zu haben, wenn Sie einen vollständigen AOP-Ansatz verfolgen: Definieren Sie Ihre Schnittstelle, definieren Sie Ihr Verhalten für die Aktionen, und parametrisieren Sie die Implementierung Ihrer abgeleiteten Klassen, um zur Laufzeit das gewünschte Verhalten mit dem übergebenen Objekt zusammenzustellen. Aber das ist kompliziert... :-)

Spring AOP ist übrigens großartig in diesen Dingen. Ich würde es nachlesen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X