Announcement

Collapse
No announcement yet.

Was kann OOP was ich mit Komposition nicht erreichen kann

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • Was kann OOP was ich mit Komposition nicht erreichen kann

    Hallo zusammen,

    Das ist mal wieder ein etwas ketzerisches Thema, aber diese Frage geht mir schon länger nicht mehr aus dem Kopf. Gibt es irgendwelche Patterns oder Kontrukte die ich so nur mit OOP erreichen kann und die mit Komposition nicht zur Verfügung stehen? Bisher habe ich kein einziges Konstrukt gefunden was ich nur durch OOP so abbilden kann. Zugegebenermaßen bin ich nicht der größte Freund von OOP, meiner Meinung nach sind Konstrukte die per Komposition entstanden sind viel leichter zu verstehen. Aber das ist nur meine persönliche Meinung. Ihr dürft mich jetzt davon überzeugen, dass man OOP auch sinnvoll einsetzen kann

    Beispiel Template Method Pattern:

    [highlight=c#]
    ///////////////////////////////////
    // Version mit abstrakter Klasse //
    ///////////////////////////////////

    // Die abstrakte Klasse mit der Steuerungslogik
    public abstract class ControllingLogic
    {
    public ExecuteIt()
    {
    if(DoStep1())
    {
    DoStep2();
    }
    else
    {
    DoStep3();
    }
    }

    public abstract bool DoStep1();
    public abstract void DoStep2();
    public abstract void DoStep3();
    }

    // konkrete Implementation
    public class SomeImplementation:ControllingLogic
    {
    public override bool DoStep1() { /* implementation */ }
    public override bool DoStep2() { /* implementation */ }
    public override bool DoStep3() { /* implementation */ }
    }

    /////////////////////////////
    // Version mit Komposition //
    /////////////////////////////
    public interface ICanDoSteps
    {
    bool DoStep1();
    void DoStep2();
    void DoStep3();
    }

    public class ControllingLogic
    {
    private ICanDoSteps _stepExecutor;

    public ControllingLogic(ICanDoSteps stepExecutor)
    {
    _stepExecutor = stepExecutor;
    }

    public ExecuteIt()
    {
    if(_stepExecutor.DoStep1())
    {
    _stepExecutor.DoStep2();
    }
    else
    {
    _stepExecutor.DoStep3();
    }
    }
    }

    public class SomeImplementation:ICanDoSteps
    {
    public bool DoStep1() { /* implementation */ }
    public bool DoStep2() { /* implementation */ }
    public bool DoStep3() { /* implementation */ }
    }
    [/highlight]

    Ok zugegebenermaßen muss ich etwas mehr tippen und ich muss selbst dafür Sorgen dass die Klassen richtig verdrahtet werden. Dafür habe ich weniger Kopplung (Trennung durch ein Interface und nicht direkte Abhängigkeit durch Vererbung). Ähnlich sehe ich das bei vielen anderen Patterns. Dazu tendiert OOP immer noch dazu mit der Zeit Monsterklassen zu erzeugen die zu viel können und Wissen.

    Hat jemand ein gutes Beispiel was für OOP spricht?

    Danke

    Gruß
    Flo
    Zuletzt editiert von fanderlf; 21.07.2012, 10:35.

  • #2
    Dazu tendiert OOP immer noch dazu mit der Zeit Monsterklassen zu erzeugen die zu viel können und Wissen.
    Warum sollte das so sein? Ich will hier nicht gleich behaupten, wer Klassen mit Code über X-Zeilen schreibt hat die OOP nicht verstanden, aber häufig kann man lange Klassen oder Methoden nochmals splitten. Und wenn nicht, dann ist dem halt so. Dann ist diese Klasse halt mal etwas länger

    Ich kann mich mit deinen Kompositionen so gar nicht anfreunden. Wenn ich das richtig sehe wird die ganze Mechanik der OOP wird damit ausgehebelt bsp. nicht genutzt. Das überschreiben/nutzen von protected Variablen oder Methoden ist weiterhin möglich? Gerade das ableiten - ein wie finde ich gute Möglichkeit neue Funktionen zu implementieren - geht wohl auch nicht. Dein obiges Beispiel kann ich auch nicht so ganz nachvollziehen. Während im OOP-Beispiel die konkrete Implementierung in der Klasse SomeImplementation ist, vermisse ich das bei der Komposition. Hier unterstelle ich, dass das in der Klasse _stepExecutor gesehen ist. Im OOP Beispiel hast du mit der abstrakten Klasse und der Ableitung "alles erledigt". Du kannst die abgeleitete Klasse nutzen und sofort alle Methoden aufrufen. Du bist gezwungen, die abstrakten Methoden zu implementieren. In deinem Komposite Beispiel hat das wohl die Klasse _stepExecutor getan. Die nutzt du jetzt im Prinzip als Feld der Klasse ControllingLogic.

    Dafür habe ich weniger Kopplung (Trennung durch ein Interface und nicht direkte Abhängigkeit durch Vererbung)
    Das verstehe ich nicht, wo dass das Argument für eine Komposition ist. Gerade Interface und abstrakte Klassen sind doch Bestandteile der OOP bzw. kommen gerade daher. Das da "mehr" Abhängigkeit" ist, würde ich nicht sagen

    Hat jemand ein gutes Beispiel was für OOP spricht?
    Ich denke die klassischen Beispiele der OOP (Fortbewegungsmittel -> Auto, Rad Flugzeug) und die Beispiele mit dem überschreiben bzw. nutzen der (abstrakten) Basisklasse würden verdeutlichen, was ich meine


    Ich empfinde die OOP als zielführender....
    Zuletzt editiert von Christian Marquardt; 21.07.2012, 10:55.
    Christian

    Comment


    • #3
      Es war auch meine Intention etwas zu provozieren. Wofür brauche ich OOP heutzutage noch? Ich will jetzt auch keinen Glaubenskrieg auslösen, sondern einfach nur mal drüber diskutieren. Sehr viele Konstrukte die ich bisher gesehen habe haben mich nicht richtig überzeugt. Die Implementierung des Interface habe ich noch hinzugefügt. Wahrscheinlich hast Du den Thread einfach etwas zu früh aufgemacht Ich hatte in der ursprünglichen Version noch was vergessen.
      Ein weiterer Grundsatz der Programmierung lautet ja auch "Composition over Inheritance" und lose Kopplung wird überall propagiert. Gefühlt ist es für mich auch besser, allerdings hätte ich gerne mal ein paar konkrete Beispiele gehabt in den wirklich OOP auftrumpft. Unter OOP verstehe ich die Vererbung konkreter Klassen. Interfaces sind meiner Meinung nach kein Konzept von OOP. Schnittstellen lassen sich z.B. auch in C definieren (im Endeffekt ist es dort ja nur eine Vorwärtsdeklaration/Headerfile indem definiert wird was die dort implementieren Konstrukte können).

      Auch diese Beispiele "Auto, Flugzeug, Rad" lassen sich per Komposition erreichen und hier wird auch oft darüber diskutiert ob das nun richtig ist oder nicht. Der Herr Liskov hat hier auch einiges dazu beigetragen

      Mach doch einfach mal ein Beispiel und ich versuche das auf Komposition umzubauen. Dann können wir drüber diskutieren

      Comment


      • #4
        Die konkrete Klasse _stepExecutor feht in deinem Beispiel

        [highlight=java]
        public interface IFortbewegungsmittel
        {
        public int getGeschwindigkeit();

        public void starteFahrzeug();

        }
        [/highlight]

        [highlight=java]
        public abstract class Fortbewegungsmittel implements IFortbewegungsmittel
        {
        private int geschwindigkeit=0;
        protected int beschleunigung=10;

        protected void addGeschwindigkeit()
        {
        geschwindigkeit+=beschleunigung;
        }

        @Override
        public int getGeschwindigkeit()
        {
        return geschwindigkeit;
        }

        @Override
        abstract public void starteFahrzeug();

        }
        [/highlight]

        [highlight=java]
        public class Rad extends Fortbewegungsmittel
        {
        public Rad()
        {
        beschleunigung=6;
        }

        @Override
        public void starteFahrzeug()
        {
        addGeschwindigkeit();
        //Trete in die Pedale
        }

        }

        [/highlight]

        [highlight=java]
        public class Auto extends Fortbewegungsmittel
        {
        public Auto()
        {
        beschleunigung=60;
        }

        @Override
        public void starteFahrzeug()
        {
        addGeschwindigkeit();
        //gib Gas
        }

        }
        [/highlight]

        [highlight=java]
        public class Flugzeug extends Fortbewegungsmittel
        {
        public Flugzeug()
        {
        beschleunigung=600;
        }

        @Override
        public void starteFahrzeug()
        {
        addGeschwindigkeit();
        //starte
        }

        }
        [/highlight]

        Egal was, alle Fahrzeuge starten durch und zeigen die Geschwindigkeit an
        [highlight=java]

        ArrayList<IFortbewegungsmittel> liste=new ArrayList<>();
        Rad rad=new Rad();
        liste.add(rad);
        Auto auto=new Auto();
        liste.add(auto);
        Flugzeug fl=new Flugzeug();
        liste.add(fl);
        for(IFortbewegungsmittel fortbewegungsmittel:liste)
        {
        fortbewegungsmittel.starteFahrzeug();
        System.out.println(fortbewegungsmittel.getGeschwin digkeit());

        }
        [/highlight]
        Christian

        Comment


        • #5
          Das wäre aber ein klassisches Beispiel wo ich eine Factory als sinnvoller erachte als Vererbung. Aktuell ändert sich im Verhalten der einzelnen Fahrzeuge nichts, ausser die Beschleunigung. Konfiguration sollte nicht durch Vererbung erfolgen.

          [highlight=c#]
          public interface IFortbewegungsmittel
          {
          public int getGeschwindigkeit();
          public void starteFahrzeug();
          }

          public class Fortbewegungsmittel implements IFortbewegungsmittel
          {
          public static IFortbewegungsmittel CreateRad()
          {
          return new Fortbewegungsmittel(6);
          }

          // andere Factories analog

          private int geschwindigkeit=0;
          protected int beschleunigung=10;

          public Fortbewegungsmittel(int beschleunigung)
          {
          this.beschleunigung = beschleunigung;
          }

          protected void addGeschwindigkeit()
          {
          geschwindigkeit+=beschleunigung;
          }

          public int getGeschwindigkeit()
          {
          return geschwindigkeit;
          }

          public void starteFahrzeug()
          {
          addGeschwindigkeit();
          }
          }
          [/highlight]

          Hier sehe ich klar den Vorteil dass es weniger Code ist und ich mich durch weniger Abstraktionen arbeiten muss. Konfigurationen sollte meiner Meinung nach auch keinesfalls durch Vererbung erfolgen. Das ist Aufgabe der Factory bzw. des Bootstrappers.

          P.S.: Sorry wenn die Java Syntax nicht ganz richtig ist... bin nur in .NET unterwegs

          Comment


          • #6
            Aktuell ändert sich im Verhalten der einzelnen Fahrzeuge nichts
            Sicherlich, sie Starten anders, das sollte der Kommentar kennzeichnen. In dieser Methode "starten die Farhzeuge individuell". Des Weiteren dient das Factorypattern zu erzeugen von Klassen. Hier geht - denke ich um - OOP überhaupt. Des Weitern können die Klassen Rad usw. weitere spezifische Methoden haben.


            return new Fortbewegungsmittel
            Ich will auch kein Fortbewegungsmittel, sondern ein Rad, Auto oder Flugzeug.

            Du wolltest ein analoges Beispiel mit Kompositionen erzeugen.....Dein Beispiel nutzt ja wieder OOP mag man nun Pattern benutzen oder nicht
            Christian

            Comment


            • #7
              Was genau ist daran OOP? Ich sehe genau ein Interface und die Implementierung des Interfaces. Ist das schon OOP? Ich denke die Übereinstimmung über einen Contract kann man noch nicht als OOP betrachten. Ich sehe keine Vererbung von irgendwelcher Logik.

              Warum brauche ich denn explizit ein Rad, ein Auto oder ein Flugzeug, wenn diese sich doch nach aussen gleich Verhalten sollen? Noch dazu ist "wenn ich aber später mal" ein ganz ganz böser Satz. Nach "What if" zu programmieren ist wahrscheinlich einer der schlimmsten Ansätze überhaupt. Meistens trifft nämlich das später niemals ein und es steht massig Code da den keiner braucht. Was ist schlechter daran die Objekte per Factory zu erzeugen? Fortbewegungsmittel.CreateRad oder new Rad()? Ich sehe nicht viel Unterschied, aber wesentlich weniger Code

              Ich weiss das ist ein Reizthema ich finds trotzdem interessant nicht alle Konzepte als gegeben hinzunehmen und sie manchmal zu hinterfragen.

              Comment


              • #8
                Ich sehe keine Vererbung von irgendwelcher Logik.
                Nun, getGeschwindigkeit wurde vererbt, die Variable beschleunigung wurde vererbt. Das Beispiel mit dem setzen der beschleunigung ist etwas unglücklich, soll aber eben den Modifizierer protected nutzen

                Was genau ist daran OOP?
                Ich kenne in der strukturierten Programmierung kein Interface (Jedenfalls nicht mit der Definition aus der OOP). Warum sollte das nun nicht OOP sein? Schnittstellen an sich gab es wohl schon immer.

                Warum brauche ich denn explizit ein Rad, ein Auto oder ein Flugzeug, wenn diese sich doch nach aussen gleich Verhalten sollen?
                Wer sagt den das? In dem Beispiel KÖNNEN sie über das Interface gleich angesprochen werden. Innerhalb der Anwendungen kann es doch - und soll es doch auch, sonst brauche ich keine verschiedenen Klassen - Unterschiede geben. Beispielsweise kann jed nach Klasse eine unterschiedliche Routenberechnung erfolgen. Oder Bei dem Rad wird eine gefahrende Strecke ausgewertet, beim Auto die verbrauchten Liter. Das wird dann IN der Klasse implementiert und nur in der dazu gehörenden Klasse. Und dann brauche ich eben unterschiedliche Klassen. Und das hat nichts mit "What if" zu tun. Wenn Anforderunge bestehen wird dazu ein passendes Modell entworfen. In diesem Fall bestanden Anforderungen für Rad Auto und Flugzeug. Ggf. kann jetzt Motorrad und Schiff dazu kommen. (Dahingehend wäre die Implementierung mit Interface usw. ein "what if")
                Und sorry ich kann hier nicht innerhalb von 5 Minuten ein perfektes Beispiel bringen
                Was ist schlechter daran die Objekte per Factory zu erzeugen?
                In dem Beispiel von mir ist doch gar keine Factory drin. Die würde noch dazu kommen.

                Ich sehe nicht viel Unterschied, aber wesentlich weniger Code
                Dein Beispiel kann alles nicht, was ich bereits oben erwähnt habe, Ableitungen (abtract und protected).

                Wo ist nun dein Beispiel mit einem Komposite?

                Ausgangspunkt war doch nicht die Factory, sondern, warum OOP Vorteile gegenueber deinem Komposite hat. Sicherlich kann man auch in der OOP immer alles noch anders machen. Aber das ist oben ein einfaches Beispiel wie es üblicherweise in den Grundlagen der OOP verwendet wird. Es zeigt grundlegende Eigenschaften der OOP. Wie willst du das mit einer Klassen- oder Feldvariable in einer Klasse (ControllingLogic) nachbilden? Wobei die konkrete Implementierung deiner Klasse noch fehlt.

                Mein Beispiel hat sich doch an deine Vorgaben gehalten (mit Ausnahme der Basisklasse). Es enthält

                - ein Interface zur Beschreibung der Schnittstelle
                - eine abstrakte Basisklasse die Grundfunktionalität zur Verfügung stellt
                - konkrete Implementierungen die diese Grundfunktionalität nutzen oder ändern und selbst auch weitere Methoden bereitstellen (unterstellt das dem so ist, sonst würde man keine Klassen ableiten; angedeutet durch die Kommetare)

                Da kann sich die Diskussion doch nun nicht darum drehen, WIE die konkreten Implementierungen erschaffen werden (Factory) oder ob sie notwendig sind. Sicherlich sind sie notwendig, sonst wüede man kein Interface/Basisklasse implementieren

                Vielmehr war dein Ausgangspunkt, dass das Ganze mit OOP zu aufwendig ist, und das anderes besser wäre.

                Mach doch einfach mal ein Beispiel und ich versuche das auf Komposition umzubauen.
                Dann gehe von dem gegebenen Beispiel aus. Interface, abstrakte Klasse, mehrere konkrete Implementierungen
                Zuletzt editiert von Christian Marquardt; 22.07.2012, 06:00.
                Christian

                Comment


                • #9
                  Die Beispiele oben sind alle komplett und machen genau das gleiche als Deine Implementierungen. Meine Fortbewegungsmittel Klasse hat eine static Factory Methode die Dir auch ein Fahrrad erzeugt und im ersten Beispiel habe ich ganz unten noch eine Implementierung von ICanDoSteps angefügt (diese hast Du vielleicht beim ersten mal nicht gesehen, weil ich die Anfangs auch vergessen hatte ). Klar sind das alle schöne Beispiele aus der OOP. Die Frage ist jetzt aber brauche ich das oder brauche ich das nicht? Produziere ich damit wirklich nachhaltig guten Code den jeder versteht oder ist es langfristig besser sich an Composite zu orientieren. Composite ist meiner Meinung wesentlich leichter zu verstehen und daher auch leichter zu ändern, wenn mehrere Entwickler an einem Stück Code schreiben.

                  Meine Frage drehte sich darum dass ich gerne ein Beispiel - einen Mechanismus - hätte den ich in Composite nicht abbilden kann. Interessant finde ich auch dass die Patterns der Gang of Four fast ausschließlich aus Composites bestehen und eben nicht aus Vererbung. Die Patterns die dann doch noch Vererbung benutzen, wie das Template Method Pattern ganz oben, die lassen sich dann auch noch auf Composite umbauen.

                  Comment


                  • #10
                    Deine Beispiele sind nicht passend, da du alle Methoden in die Klasse Fortbewegungsmittel aufgenommen hast. Du bist leider nicht darauf eingangen, dass die einzelnen Implementationen eigene Funktionalität mitbringen (sollen). Sonst machen verschiedene Klassen keinen Sinn! Des Weiteren schreibst du zwar hin


                    public static IFortbewegungsmittel CreateRad()
                    aber die konkrete Klasse RAD usw. hast nicht programmiert/hingeschrieben. Wie auch schon bei deinem ersten Beispiel. Dann wäre der Aufwand genau so hoch. -> Nämlich 3 weitere Klassen schreiben. Und wenn du dann auch noch verschiedene Funktionalität reinbringst, hast du auch eine Basisklasse. Also 4 Klassen.

                    Du hast lediglich die ERZEUGUNG der Instanzen anders gemacht
                    Christian

                    Comment


                    • #11
                      Warum brauche ich eine eigene Klasse Rad wenn ich sie sowieso hinter einem Interface verstecke? Und wenn das einzige was diese Klassen gemeinsam haben eine Variable beschleunigung ist dann könnte ich auch ohne Vererbung leben. So viel hat dann wohl ein Auto doch nicht mit einem Rad gemeinsam.

                      Comment


                      • #12
                        Warum brauche ich eine eigene Klasse Rad
                        Weil die Klasse RAD andere Funktionalität zur Verfügung stellst als die Klassen FLUGZEUG und AUTO. Das mag aus meinem Beispiel nicht deutlich hervorgehen, aber warum hat man sonst unterschiedliche Klassen

                        Wo wird was "hinter einem Interface versteckt"? Das Interface beschreibt die zwingend vorhanden Methoden, wenn eine Klasse das Interface benutzen will. Will ich dann die Funktionalität aller Klassen aus dem Interface benutzen, interessiert die konkrete Ausprägung der Klasse nicht (sieh Beispiel ob mit der Liste). Will ich konkret mit einer Klasse arbeiten wird die Ausprägung genommen. Das sind zwei Fälle, die so normlerweise vorkommen



                        Und wenn das einzige was diese Klassen gemeinsam haben eine Variable beschleunigung ist dann könnte ich auch ohne Vererbung leben. So viel hat dann wohl ein Auto doch nicht mit einem Rad gemeinsam.
                        Sorry, was ist den das für ein Argument? Ich habe das jetzt schon öfter gesagt:

                        - das ist ein Beispiel. Du wirst wohl kaum erwarten können, dass ich hier das komplett ausprogrammiere. Vielleicht kann das als gegeben gelten, dass Klassen gemeinsame (dafür ist Vererbung mit all ihren Vorteilen) und eigene Funktionalität haben
                        - man sollte wohl unterschiedliche Klassen nur anlegen, wenn sie auch was unterschiedliches tun und auch die Anforderungen dafür da sind
                        - gemeinsame Funktionalität kommt in eine Basisklasse von der abgeleitet wird
                        - die Schnittstelle wird in einem Interface beschrieben, sofern vorhanden/vonnöten

                        Ich sollte eben nicht alle Klassen die irgendwas gemeinsam haben zusammen mixen egal welche Klasse nun genau was braucht. Das Macht den Code unübersichtlich und unleserlich. Des Weiteren erschwert das u.U. ein Refactoring von einzelnen Funktionalitäten
                        Zuletzt editiert von Christian Marquardt; 22.07.2012, 11:30.
                        Christian

                        Comment


                        • #13
                          Ja ich verstehe dein punkte. Aber ich hätte trotzdem gerne mal ein Beispiel indem OOP wirklich Sinn macht. Du hast ja selbst schon gesagt, dass es in dem Beispiel oben nicht wirklich Sinn macht. Ich glaube aber auch nicht dass es mehr Sinn macht wenn mehr Logik in diese Klassen kommt. Warum gruppiere ich denn diese 3 Klassen dann nach Fortbewegungsmittel? Was mache ich wenn später noch ein Typ Tankstellenbenutzer dazu kommt? Ausserdem kann ich Dir auch 3 Klassen liefern die das ganze per Komposition abbilden

                          [highlight=c#]
                          public interface IFortbewegungsmittel
                          {
                          public IAntrieb { get; }
                          }

                          public interface IAntrieb
                          {
                          int Beschleunigung { get; }
                          int Starten();
                          }

                          public class Pedale: IAntrieb
                          {
                          // Implementierung spare ich mir mal
                          }

                          public class Motor: IAntrieb
                          {
                          // Implementierung spare ich mir mal
                          }

                          public class DuesenAntrieb: IAntrieb
                          {
                          // Implementierung spare ich mir mal
                          }

                          public class Rad: IFortbewegungsmittel
                          {
                          private IAntrieb _antrieb = new Pedale(3);

                          public IAntrieb Antrieb { get { return _antrieb; } }
                          }

                          public class Auto: IFortbewegungsmittel
                          {
                          private IAntrieb _antrieb = new Motor(6);

                          public IAntrieb Antrieb { get { return _antrieb; } }
                          }

                          public class Flugzeug: IFortbewegungsmittel
                          {
                          private IAntrieb _antrieb = new Duesenantrieb(150);

                          public IAntrieb Antrieb { get { return _antrieb; } }
                          }
                          [/highlight]

                          Das wäre jetzt ein Beispiel komplett über Composition. In der wirklichen Anwendung werden wohl auch Pedale, Motor und Duesenantrieb nicht viel mehr gemeinsam haben als die Beschleunigung. Du siehst hier aber dass Rad, Auto und Flugzeug sich nur die Interfaces teilen. Ob Code geteilt wird kann ich in jedem Fall über die Composition selbst steuern. Bei Vererbung MUSS ich zwingend die Logik der Basisklasse übernehmen, selbst wenn ich diese gar nicht bräuchte oder möchte. Dazu kommt noch dass ich hier wunderbar DI betreiben kann

                          Comment


                          • #14
                            Warum gruppiere ich denn diese 3 Klassen dann nach Fortbewegungsmittel?
                            Weil

                            Will ich dann die Funktionalität aller Klassen aus dem Interface benutzen, interessiert die konkrete Ausprägung der Klasse nicht (sieh Beispiel ob mit der Liste). Will ich konkret mit einer Klasse arbeiten wird die Ausprägung genommen. Das sind zwei Fälle, die so normlerweise vorkommen
                            Aber ich hätte trotzdem gerne mal ein Beispiel indem OOP wirklich Sinn macht.
                            auswertungpanels.zip
                            Christian

                            Comment


                            • #15
                              Oki schau ich mir nachher mal an Danke!

                              Comment

                              Working...
                              X