Design Patterns - manchmal auch nicht ganz so gutes Design?



  • (getChildren() obwohl Child per Definition keine Children besitzt).

    wobei getChildren nicht wirklich Problematisch ist. Das liefert einfach immer null zurückt. addChildren und removeChildren sind da eher das Problem. Diese müssen im prinzip ein Fehlercode zurückliefern.



  • Dieses Strategiemuster hat den Vorteil das du später weitere Strategien hinzufügen kannst ohne das das Hauptobjekt davon etwas wissen musst. Du entkoppelst beide Seiten, was nicht passieren würde wenn du weitere Methoden in deinem Hauptobjekt anfügen würdest welche die alternativen Algorithmen abbilden.
    Dieses Problem mit dem Bekanntmachen/Delegieren von Implementierungsdetails gibt es auch für andere Muster (State, Facade) aber glücklicherweise gibt es ja hierfür das Schlüsselwort friend.
    Das bei einem Composite die Gefahr eines fat interface besteht wird m.E. aber auch in dem Buch diskutiert. Man muß halt das Beste daraus machen. Ich kombiniere das Pattern gerne mit dem Visitor-Pattern, dann bleibt die Schnittstelle auch recht aufgeräumt.



  • ad 1 & 2: ich seher hier kein problem. nicht alles was "class" heisst muss im OOP sinn ein first class objekt sein. patterns können auch mal implementierungs detail sein, auch spricht nichts dagegen dass ein paar wenige klassen eng gekoppelt sind, so lange man dort entkoppelt wo es wichtig ist.

    beispiel: wenn ich std::sort() aufrufe, klar sieht std::sort() dann meine "privaten" daten. und natürlich hab ich dann eine enge koppelung zu std::sort. und wenn es sinn macht packe ich auch std::sort() in ein objekt, falls ich mal - konfigurierbarerweise - auf foo::sort_faster() oder bar::sort_mighty_smart() umschalten will.

    und? wo ist das problem? 😉

    ad 3: das composite pattern ist ein sinnvoller kompromiss, da es eben viele dinge vereinfacht. das ist der sinn. und deswegen ist es auch gut. WENN es angebracht ist. steht aber IIRC alles recht schön im GoF buch beschrieben.



  • man sollte design patters nicht ganz so ernst nehmen und 1 zu 1 umsetzen, sondern die ideen verstehen und mit neuen ideen programme entwerfen



  • Danke erstmal an alle für die zahlreichen Antworten.

    @byto:

    Private Felder verstoßen doch nicht gegen den Data-Hiding Grundsatz, denn sie gehören nicht zur Schnittstelle der Klasse.

    Das hab ich so auch nicht behauptet. Mein Einwand war eher, dass so Daten und Methoden, die mit ihnen arbeiten, getrennt werden. Das Strategy-Objekt wird immer von einer anderen Klasse gefüttert, "leiht" sich also dessen Daten aus. Ich hatte nur so 'nen Satz wie "Keep data and related behavior in one place." im Kopf.

    @witte: Danke für deinen Kombinationstipp. Ich denke, die Übergabe per Parameter ist der friend-Variante vorzuziehen, oder?

    @hustbaer: Ich denke, Containerklassen kann man da ausnehmen, die sind ein Extremfall.



  • moagnus schrieb:

    Das Strategy-Objekt wird immer von einer anderen Klasse gefüttert, "leiht" sich also dessen Daten aus. Ich hatte nur so 'nen Satz wie "Keep data and related behavior in one place." im Kopf.

    Ne, ist falsch.
    Du rufst ja auch
    std::sort auf um deinen vector zu sortieren.

    algorithmen und daten muessen nicht zusammen sein...



  • Shade Of Mine schrieb:

    moagnus schrieb:

    Das Strategy-Objekt wird immer von einer anderen Klasse gefüttert, "leiht" sich also dessen Daten aus. Ich hatte nur so 'nen Satz wie "Keep data and related behavior in one place." im Kopf.

    Ne, ist falsch.
    Du rufst ja auch
    std::sort auf um deinen vector zu sortieren.
    algorithmen und daten muessen nicht zusammen sein...

    Recht habt ihr beide. Allgemeine Algorithmen arbeiten mit (idealerweise) beliebigen Daten. Spezielle Algorithmen nicht.



  • moagnus schrieb:

    @hustbaer: Ich denke, Containerklassen kann man da ausnehmen, die sind ein Extremfall.

    Hab ich was von Containerklassen geschrieben?
    😕



  • Ja, aber std::sort ist ja auch keine Memberfunktion. Ich weiß nicht, aber ich hab das Gefühl, dass bei Strategy zu viel Daten von außen eingegeben werden. Wenn ich mir dann vorstelle, dass ich mal meiner Strategy nen Zeiger auf ne Membervariable der Klasse, die die Strategy benutzt, übergebe und mittels des Zeigers dann irgendein Wert während des Algorithmuses (in einer Strategy-Methode) verändert wird, dann wird der Wert der Variable nicht mehr nur von seiner Klasse bestimmt, sondern von der Strategy-Methode.

    Ist das ein berechtigter Einwand oder ist das Beispiel eher zu konstruiert und sollte ich mir hustbaers ersten Absatz bzgl. manchmal enge Kopplung ist okay zu Herzen nehmen?



  • moagnus schrieb:

    Ich weiß nicht, aber ich hab das Gefühl, dass bei Strategy zu viel Daten von außen eingegeben werden.

    Das ist halt ein Kompromiss. Dass jede Verantwortlichkeit bei dem Objekt liegt, das die dazugehörigen Daten enthält, ist nur ein Designprinzip von mehreren. Ein anderes wäre, dass man veränderliches Verhalten durch Polymorphie ausdrücken soll. Das beides geht nicht unter einen Hut zu bringen, also muss man sich überlegen, wo man was aufweicht.



  • @moagnus:
    Du kannst dir ja aussuchen wie die "Strategy" mit dem "eigentlichen" Objekt kommuniziert. Ein Extrem wäre "friend", das andere Extrem wäre ne totale Entkopplung mittels SOAP Interface oder etwas in der Art. Nochmal eine andere Möglichkeit ist ein generisches Template-basiertes Interface, wie es oft in der Standard-Library anzutreffen ist (z.B. eben bei sort, partition etc.).

    Es gibt sicher Fälle wo es Sinn macht alles schön zu entkoppelt, und keinen "direkten" Zugriff auf die Eingeweide einer Klasse zu erlauben.

    Es gibt aber auch Fälle, wo es gänzlich egal ist.

    Wichtig ist doch hauptsächlich, dass ein Funktionsblock/Modul sauber von anderen Funktionsblöcken/Modulen getrennt ist. Das heisst aber nicht, dass ein Funktionsblock nicht aus 2, 3 oder auch mal 10 eng gekoppelten Klassen bestehen dürfte.



  • Das Strategie Pattern sagt doch überhaupt nichts darüber aus, ob und wie Daten vom Algorithmus manipuliert werden. Das ist eine Frage, wie Du den Algorithmus implementierst. Wenn Du Seiteneffekt vermeiden willst, dann manipulier halt nicht das übergebene Objekt sondern erzeuge ein neues und liefer das zurück.

    Das Strategie Pattern besagt lediglich, dass man eine einheitliche Schnittstelle schafft, um Algorithmen polymorph austauschen zu können.



  • moagnus schrieb:

    Ja, aber std::sort ist ja auch keine Memberfunktion.

    Ich sehe den Unterschied nicht.

    Die Strategy kann ja zB eine funktion sein.

    class Context {
    private:
      boost::function<void(*)(int)> strategy;
    
    public:
      void setStrategy(boost::function<void(*)(int)> strategy) {
        this->strategy = strategy;
      }
    
      void execute(int data) const {
        strategy(data);
      }
    };
    

    Wie du die Strategy Objekte mit Context und den Daten koppelst, ist dir selbst überlassen...



  • moagnus schrieb:

    Ich denke, die Übergabe per Parameter ist der friend-Variante vorzuziehen, oder?

    Kommt drauf an. Beispiel: Du hast ein Form/Webpage mit 30 Controls und die unterschiedlichen Bearbeitungsmodi dieses Forms sollen in ein Strategiemuster ausgelagert werden. Also ein Objekt setzt das Form im Nur-Lesen-Modus, eins in den Bearbeitungsmodus und eins in den Anfügemodus. Also ICH werde da bestimmt keine 30 Controlreferenzen übergeben.



  • witte schrieb:

    moagnus schrieb:

    Ich denke, die Übergabe per Parameter ist der friend-Variante vorzuziehen, oder?

    Kommt drauf an. Beispiel: Du hast ein Form/Webpage mit 30 Controls und die unterschiedlichen Bearbeitungsmodi dieses Forms sollen in ein Strategiemuster ausgelagert werden. Also ein Objekt setzt das Form im Nur-Lesen-Modus, eins in den Bearbeitungsmodus und eins in den Anfügemodus. Also ICH werde da bestimmt keine 30 Controlreferenzen übergeben.

    Du übergibts eine Referenz auf die Form... Die Form kennt ja die eigenen Controls. Oder übersehe ich da was? Schliesslich ist dem Strategy ja der Rest der Seite komplett egal...



  • Er wollte die Member (hier die Controls des Form) nicht public machen um die Kapselung nicht aufzubrechen, wenn ich das richtig verstanden habe. Also müssen doch entweder die Member übergeben werden oder die Strategieklassen sind friends und manipulieren den Client direkt.



  • Wobei C# ja leider kein "friend" kann... und "internal" ist halt etwas grob.



  • @Bashar: Ok, ich glaube "aufweichen" ist hier ein gutes Schlüsselwort.

    @hustbaer: Ok, ich hatte da vielleicht eine zu idealistische Ansicht. Vollkommen voneinander entkoppelte Klassen sind wohl auch nicht immer möglich.

    @byto: Ja, du hast recht. Ich schätze mal, mein Beispiel war zu überzogen. Ich versteh das Strategy-Pattern jetzt mal als hilfreichen Kompromiss.

    @witte: Die Member public zu setzen, würde mir sowieso nicht einfallen. Die drei Möglichkeiten, die ich sehe - und die auch schon genannt wurden - sind:
    1. Übergabe der notwendigen Parameter. Wenn sich das in Grenzen hält, scheint das mir die beste Variante zu sein.
    2. Die Anfreundung der zwischen Klasse und Strategy-Klasse über "friend".
    3. "Callback" vom Strategy-Objekt aus mittels Getter-Methoden. Das halte ich für die schlechteste Variante, da es das Interface der benutzenden Klasse aufbläht, und das in einer eigentlich unerwünschten Weise.

    Als Resümee versuch ich mal die engere Kopplung positiver zu sehn, sozusagen.



  • moagnus schrieb:

    @hustbaer: Ok, ich hatte da vielleicht eine zu idealistische Ansicht. Vollkommen voneinander entkoppelte Klassen sind wohl auch nicht immer möglich.

    Ich verstehe zwar nicht was daran idealistisch sein soll, aber OK, wenn du meinst 🙂


Anmelden zum Antworten