Laden...

Design Frage zu FabrikMethode und Strategy-Pattern

Erstellt von rollerfreak2 vor 12 Jahren Letzter Beitrag vor 12 Jahren 1.507 Views
rollerfreak2 Themenstarter:in
916 Beiträge seit 2008
vor 12 Jahren
Design Frage zu FabrikMethode und Strategy-Pattern

Hallo zusammen,

ich schildere mal kurz mein Design. Ich habe eine Struktur die Nodes enthält. Diese sind so aufgebaut wie ein Baum, daher eine Node hat einen Parent und n Childs usw. Nun muss jede Node einmal besucht werden um daran Algorithmen auszuführen. Dazu habe ich eine abstrakte Basisklasse bsp. AbstractStrategy von der ich dann konkrete Strategien ableite. Diese Strategien nehmen die Node und wenden darauf ihren Algorithmus an. Es ist auch möglich das mehrere Algorithmen pro Node angewendet werden müssen, die Reihenfolge spielt dabei keine Rolle. Dazu gibt es also eine Art Factory die zu einer bestimmten Node eine List<T> mit Strategien zurück liefert. Dazu gibt es am IStrategy Interface eine Methode bool IsRelevantForNode(INode).

Nun sollte der Code wie folgt aussehen.

(PseudoCode -> Strategies)


foreach (INode node in Nodes)
{
    IList<IStrategy> strategies = this.Factory.GetStrategies(node);
    foreach (IStrategy strategy in strategies)
    {
        strategy.Execute(node);
    }
}

(PseudoCode -> Factory)


public IList<IStrategy> GetStrategies(INode node)
{
    List<IStrategy> list = new List<IStrategy>();
    foreach (IStrategy strategy in ListOfStrategies)
    {
        if (strategy.IsRelevantForNode(node))
        {
             list.Add(Strategy);
        }
    }

    return list;
}

Das Problem ist jetzt bei ListOfStrategies in der FabrikMethode. Ich müsste jetzt also am Anfang einmal eine Liste mit allen konkreten Strategien erzeugen. Bsp


private void InitStrategies()
{
    this.ListOfStrategies = new List<IStrategy>();
    this.ListOfStrategies.Add(new ConreteStrategy1());
    this.ListOfStrategies.Add(new ConreteStrategy2());
    //....
}

Dabei ist zu bedenken das in meinem Konreten Fall der Erschaffung neuer Konreter Strategien relativ oft passiert. Nun hab ich keine Lust jedesmal die Factory Methode anzufassen bzw. es ist sehr fehleranfällig.

Ich könnte jetzt Beispielsweise via Reflection alle Klassen suchen die das entsprechende Interface implementieren und dann die Liste damit füllen. Das halte ich jedoch für keine so gute Idee. Mir wäre lieber es gebe eine "saubere" architektonische Lösung für dieses Problem. Hat einer eine Idee bzw. geht das was ich vorhabe, quasi die FabrikMethode unberührt zu lassen, nicht generisch zu lösen.

Again what learned...

6.911 Beiträge seit 2009
vor 12 Jahren

Hallo rollerfreak2,

wenn ich deine Frage richtig verstanden habe, dann wäre das Visitor-Pattern eine passende Alternative. Schau dir das mal an.

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

rollerfreak2 Themenstarter:in
916 Beiträge seit 2008
vor 12 Jahren

Hallo gfoidl,

ich kenne das Visitor Pattern, jedoch denke ich hab ich dann auch das Problem dass das reine Hinzufügen eines neues Visitors reicht in meinem Falle dann dort auch nicht aus, oder irre ich mich?

Again what learned...

49.485 Beiträge seit 2005
vor 12 Jahren

Hallo rollerfreak2,

was die Auflistung aller Strategien angeht, gibt es im Grunde doch nur zwei Möglichkeiten: Entweder es gibt eine Stelle, an der aufgelistet ist, welche Strategien es gibt. Egal, ob das in einer Methode oder über eine Collection passiert. Dann musst du daran denken, die Liste jedes Mal zu erweitern, wenn eine neue Strategie dazu kommt. Oder du kennzeichnest die Klassen in irgendeiner Form als Strategien und ermittelst dann über ein Suche, welche Klassen als Strategien gekennzeichnet sind. Dann musst du bei der Erstellung einer neuen Strategie-Klasse dran denken, diese zu kennzeichnen. Ich persönlich würde die zweite Vorgehensweise vorziehen.

Die konkrete Umsetzung finde ich zweitrangig. Also ob eine Strategie durch ein Interfaces, eine Basisklasse oder ein Attribut gekennzeichnet ist, kann man abwägen, und dann nimmt man, was einem am besten gefällt. In allen Fällen wird es dann vermutlich auf Reflection hinauslaufen, aber dafür ist Reflection ja gedacht. Mir fällt kein Grund ein, warum das eine schlechte Idee sein sollte.

Der einzige Vorteil den man hat, wenn man von Hand pflegt, ist eine zusätzliche Steuerungsmöglichkeit. Man könnte z.B. mehrere verschiedene Listen für unterschiedliche Einsatzzwecke pflegen. Oder aus der einen Liste bestimmte Strategien ausschließen. Wobei letzteres auch im anderen Fall möglich ist, denn man kann aus der automatisch ermittelten Liste anschließend auch gezielt Strategien wieder rauswerfen. Der Unterschied liegt in dem Default, wenn eine neue Strategie hinzukommt. Im manuellen Fall wird diese erst berücksichtigt, wenn man sie explizit in die Liste aufnimmt. Im automatische Fall wird sie sofort berücksichtigt, bis man sie explizit ausschließt.

herbivore

PS: Ok, es gibt natürlich schon noch andere Möglichkeiten, z.B. könnte man eine Chain aufbauen, in der jede Strategie ihren Nachfolger kennt, aber das wäre ziemlich unsinnig, würde unnötige und störende Abhängigkeiten schaffen und wäre auf jeden Fall schlechter zu warten. Ich sehe außer den zwei oben genannten Möglichkeiten keine anderen sinnvollen. Ich lasse mich aber auch gerne eines besseren belehren.

67 Beiträge seit 2011
vor 12 Jahren

Was mir noch einfällt, wäre dass du keine Fabrikmethode, sondern das Plugin-Factory-Pattern zur Anwendung bringst. Das hätte zumindest den Vorteil, dass die Implementierungen dynamisch zur Laufzeit und nicht beim Compilen erfolgen. So musst du nicht jedes Mal die Methode erweitern und einen neuen Release erstellen.

6.911 Beiträge seit 2009
vor 12 Jahren

Hallo rollerfreak2,

zugegebenermaßen hab ich die Kernfrage erst nach der Antwort von herbivore verstanden und mich vom Text vor dem Code verleiten lassen (mein Fehler). Ich hab es mit dem Visitor-Pattern probiert, das geht, macht es aber ein wenig komplizierter als nötig. Im Grunde ist es ja nichts anderes als du mit

foreach (INode node in Nodes)  
{  
    ...  
    foreach (IStrategy strategy in strategies)  
    {  
        strategy.Execute(node);  
    }  
}  

eh schon machts.

Jedenfalls wollte ich mit dem Visitor-Vorschlag darauf hinaus, dass du statt

  
    foreach (IStrategy strategy in ListOfStrategies)  
    {  
        if (strategy.IsRelevantForNode(node))  
        {  
             list.Add(Strategy);  
        }  
    }  
  

ein Dictionary<Type, List<IStrategy>> verwendest, indem du den Node-Type als Key verwendest. Also IoC-Container-mäßig indem die Strategien für den Node-Type registriert werden können. Das hat herbivore auch allgemein mit Collections abgeckt.
Die "GetStrategies(INode node)"-Methode greift dann einfach auf das interne Dictionary zu und gibt die Liste mit den Strategien zurück. (Und ob dann per foreach od. Visitor über die Nodes iteriert wird, ist dann egal 😉

Dieses Dictionary kannst du dann initial nach einer von herbivore genannten Möglichkeiten befüllen und zur Laufzeit auch noch Strategien "registrieren".
Ich denke auch, dass es das ist worauf ModelViewPresenter hinaus will.

Beim zweiten Vorschlag von herbivore könntest du ein Attribut definieren, indem der Node-Type angegeben wird und dies bei der Initialisierung des Dictionarys auswerten.

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

rollerfreak2 Themenstarter:in
916 Beiträge seit 2008
vor 12 Jahren

Hallo zusammen,

danke für die Zahlreichen Antworten. Ich glaube ich werde mich für die Sache mit Reflection entscheiden, die herbivore beschrieben hat. Entweder via Attribut oder über alle Klassen die das entsprechende Interface implementieren. Dabei kann ich sicher sein, das es sich auf eine Assembly beschränkt. Dann brauch ich die Factory Methode nicht mehr anfassen sondern nur noch konkrete Strategien implementieren.

Again what learned...