T-Rex: Abstrakte methoden praxistest

Moin,

jetzt arbeite ich schon ziemlich lange Objekt orientiet. Dabei verwende ich auch schon ziemlich lange abstrakte Klassen und Methoden. Doch gerade die letzte Woche hätte ich öfter den Fall gehabt wo ich die Abstrakte Klasse um weitere Abstrakte Methoden hätte erweitern können. Hab mich dann aber dagegen entschieden da es bereits ein paar Hände voller Ableitungen gab und ich alle Ableitungen hätte anpassen müssen.
Ein Prinzip der OOP heißt ja offen für Erweiterung. Doch sehe ich das Prinzip ziemlich absurd an, wenn ich an die 20 Ableitungen anpassen müsste.

Dass stellt bei mir das Prinzip der Abstraktion in Frage.

Damit ich die 20 Ableitungen nicht alle anpassen muss, bekommt die erste Ebene meiner Vererbungshierarchie eine "Dummy" Methode, welche einen Standardwert zurück gibt. Wenn eine Ableitung diese Methode benutzen möchte überschreibt sie einfach die dummy Methode und füllt die echte Methode mit entsprechendem Leben.
Jetzt frage ich mich, wieso mach ich sowas nicht gleich von Anfang an?

Würde gerne eure Meinung hören

Gruß
der Regentanz tanzende
T-Rex

  1. Hallo,

    ich denke, dass es schwierig ist, die Frage allgemein zu beantworten. Wenn jede abgeleitete Klasse diese Methode haben soll, aber bei einigen Klassen eh immer der gleiche Wert zurückgegeben wird, finde ich dein jetziges vorgehen richtig.

    Wenn allerdings nur ein paar Klassen diese Methode benötigen/haben sollen, müsste man vielleicht über das Vererbungsmodell nachdenken. (Es gibt Spezialfälle Stichwort "Kreis-Ellipse-Problem", bei denen es manchmal keine allgemeine richtige Lösung gibt.)

    Möglichkeiten, die ich noch sehe. Die Klassen, die die Methode implementieren sollen, könnten von einer anderen abstrakten Klasse erben, die wiederum von deiner abstrakten Klasse erbt. Oder du verwendest ein Interface für diese eine Methode.

    Viele Grüße Novi

    --
    "(...) deshalb mag ich Binärtechnik. Da gibt es nur drei Zustände: High, Low und Kaputt." (Wau Holland)
    1. Hi Novi,

      Wenn allerdings nur ein paar Klassen diese Methode benötigen/haben sollen, müsste man vielleicht über das Vererbungsmodell nachdenken. (Es gibt Spezialfälle Stichwort "Kreis-Ellipse-Problem", bei denen es manchmal keine allgemeine richtige Lösung gibt.)

      Eigentlich ist der Fall eindeutig. Das Verhalten einer Ellipse und eines Kreises ist unterschiedlich und damit gehören die nicht in die gleiche Vererbungshierarchie. Mehr dazu sagt das LSP; die verlinkte PDF ist lesenswert.

      An den OP: Bei einer Vererbungshierarchie mit 20 Klassen, würde ich über ein Refactoring nachdenken.

      MfG
      Otto

      1. Hallo,

        Eigentlich ist der Fall eindeutig. Das Verhalten einer Ellipse und eines Kreises ist unterschiedlich und damit gehören die nicht in die gleiche Vererbungshierarchie.

        Man könnte sehr wohl die Ellipse vom Kreis erben lassen, da sie sich nicht anders verhält. Sie hat schließlich nur zusätzliche Attribute und Methoden. Es geht allerdings keine Funktionalität verloren. Auffassen könnte man das dann als eine "Erweiterung" des Kreises.

        Mehr dazu sagt das LSP; die verlinkte PDF ist lesenswert.

        Ich habe die PDF nur mal kurz überflogen und gleich einen Fehler gefunden. Scheinbar ist dem Autor der Bergriff Polymorphie nicht ganz klar.

        An den OP: Bei einer Vererbungshierarchie mit 20 Klassen, würde ich über ein Refactoring nachdenken.

        MfG
        Otto

        Viele Grüße Novi

        --
        "(...) deshalb mag ich Binärtechnik. Da gibt es nur drei Zustände: High, Low und Kaputt." (Wau Holland)
        1. Hi Novi,

          Man könnte sehr wohl die Ellipse vom Kreis erben lassen, da sie sich nicht anders verhält. Sie hat schließlich nur zusätzliche Attribute und Methoden. Es geht allerdings keine Funktionalität verloren. Auffassen könnte man das dann als eine "Erweiterung" des Kreises.

          Dann hast du eine Generalisierung und Vererbung dient der Spezialisierung. Dadurch verschlechterst du die Verständlichkeit der Vererbungshierarchie. Der Kreis ist eine Spezialform der Ellipse und des Wegen muss der Kreis von der Ellipse erben, welches zu Problemen führt.

          Ich habe die PDF nur mal kurz überflogen und gleich einen Fehler gefunden. Scheinbar ist dem Autor der Bergriff Polymorphie nicht ganz klar.

          Hast du auch ein Bsp. für deine Behauptung?

          MfG
          Otto

          1. Hallo,

            Man könnte sehr wohl die Ellipse vom Kreis erben lassen, da sie sich nicht anders verhält. Sie hat schließlich nur zusätzliche Attribute und Methoden. Es geht allerdings keine Funktionalität verloren. Auffassen könnte man das dann als eine "Erweiterung" des Kreises.

            Dann hast du eine Generalisierung und Vererbung dient der Spezialisierung. Dadurch verschlechterst du die Verständlichkeit der Vererbungshierarchie. Der Kreis ist eine Spezialform der Ellipse und des Wegen muss der Kreis von der Ellipse erben, welches zu Problemen führt.

            Ja beide Vererbungsrichtungen haben ihre Nachteile. Aber auch keine Vererbung zu verwenden ist ungünstig, da man dann ja quasi die Implementierung eins zu eins kopieren müsste. Folglich sind wir bei meiner ursprünglichen Behauptung angelangt, dass es manchmal keine allgemein richtige Lösung gibt.

            Ich habe die PDF nur mal kurz überflogen und gleich einen Fehler gefunden. Scheinbar ist dem Autor der Bergriff Polymorphie nicht ganz klar.

            Hast du auch ein Bsp. für deine Behauptung?

            Der Schein scheint zu trügen. Ich hätte den Text wohl doch zu Ende lesen und nicht nur überfliegen sollen.

            Ich wollte auf das folgende Beispiel raus:

              
            void Square::SetWidth(double w)  
            {  
            Rectangle::SetWidth(w);  
            Rectangle::SetHeight(w);  
            }  
            void Square::SetHeight(double h)  
            {  
            Rectangle::SetHeight(h);  
            Rectangle::SetWidth(h);  
            }  
            s.SetWidth(1); // Fortunately sets the height to 1 too.  
            s,SetHeight(2); // sets width and heigt to 2, good thing.  
            But consider the following function:  
            void f(Rectangle& r)  
            {  
            r.SetWidth(32); // calls Rectangle::SetWidth  
            }  
            
            

            Da wurde zuerst behauptet, dass dies nicht ginge. Mit Polymorphie ist es aber natürlich möglich.

            Viele Grüße Novi

            --
            "(...) deshalb mag ich Binärtechnik. Da gibt es nur drei Zustände: High, Low und Kaputt." (Wau Holland)
            1. Hallo,

              Man könnte sehr wohl die Ellipse vom Kreis erben lassen, da sie sich nicht anders verhält. Sie hat schließlich nur zusätzliche Attribute und Methoden. Es geht allerdings keine Funktionalität verloren. Auffassen könnte man das dann als eine "Erweiterung" des Kreises.

              Dann hast du eine Generalisierung und Vererbung dient der Spezialisierung. Dadurch verschlechterst du die Verständlichkeit der Vererbungshierarchie. Der Kreis ist eine Spezialform der Ellipse und des Wegen muss der Kreis von der Ellipse erben, welches zu Problemen führt.

              Ja beide Vererbungsrichtungen haben ihre Nachteile.

              Die "richtige" Vererbungsrichtung ist aber Spezialisierung. Das dient ja eben dazu, dass dir der Compiler die Arbeit abnimmt, wann ein Objekt erlaubt ist und wann nicht. Würde man mit Vererbung generalisieren, wäre das ganze witzlos.

              Aber auch keine Vererbung zu verwenden ist ungünstig, da man dann ja quasi die Implementierung eins zu eins kopieren müsste.

              Das Kreis-Ellipse-Problem ist nicht so ganz einfach. Meine Intuition sagt mir, dass ich einen Kreis auch nicht von einer Ellipse erben lassen würde, denn ein Kreis _ist_ keine Ellipse. Er sieht aus wie eine Ellipse, aber er verhält sich nicht wie eine. Folglich kann man hier nicht vererben. Das Kopieren von Code ist nicht immer schlecht oder falsch, nämlich dann nicht, wenn sich Code1 später mal verändert, wobei Code2 gleichbleiben soll.

            2. Hi Novi,

              Ja beide Vererbungsrichtungen haben ihre Nachteile. Aber auch keine Vererbung zu verwenden ist ungünstig, da man dann ja quasi die Implementierung eins zu eins kopieren müsste.

              Wie Whouzuo gesagt hat. Es ist manchmal besser mit Code-Duplikaten zu leben. Allerdings gibt es genug Möglichkeiten diese zu vermeiden, z.B. Strategy Pattern oder Delegation. Bei der Abstraktion unsere Diskussion sind konkrete Lösungen schwer.

              Folglich sind wir bei meiner ursprünglichen Behauptung angelangt, dass es manchmal keine allgemein richtige Lösung gibt.

              Gibt es die Überhaupt? Bei 00-Design machst du immer Kompromisse, nicht ohne Grund gibt es in dem GoF Buch immer ein Abschnitt Konsequenzen. Die Kunst besteht darin jene Lösung zu Wählen die am besten in den gegebenen Kontext passt.

              Der Schein scheint zu trügen. Ich hätte den Text wohl doch zu Ende lesen und nicht nur überfliegen sollen.

              Wie heißt es so schön, wer ließt ist im Vorteil und wer bis zum Ende ließt ist unbesiegbar ;-)

              MfG
              Otto

      2. An den OP: Bei einer Vererbungshierarchie mit 20 Klassen, würde ich über ein Refactoring nachdenken.

        LOL :D. Nein nein, das sind doch keine 20 Hierarchien, es sind 20 Klassen die von einer Erben. Die Hierarchie ist also 1. Eine Eltern Klasse und 20 die davon erben.

        Tut mir leid wenn ich mich nicht richtig ausgedrückt habe.

        Gruß
        der direkt vererbende
        T-Rex

        1. Hi T-Rex,

          An den OP: Bei einer Vererbungshierarchie mit 20 Klassen, würde ich über ein Refactoring nachdenken.

          LOL :D. Nein nein, das sind doch keine 20 Hierarchien, es sind 20 Klassen die von einer Erben. Die Hierarchie ist also 1. Eine Eltern Klasse und 20 die davon erben.

          Egal, also hast Number of Childs = 20. Die Nachteile einer breiten Vererbungshierarchie ist, das es sehr Aufwendig ist Elternklassen zu ändern. Auch wird das schreiben von Testfällen keinen Spaß machen. Ich würde nach einem Weg suchen die Breite zu verringern.

          MfG
          Otto

          1. Egal, also hast Number of Childs = 20. Die Nachteile einer breiten Vererbungshierarchie ist, das es sehr Aufwendig ist Elternklassen zu ändern. Auch wird das schreiben von Testfällen keinen Spaß machen. Ich würde nach einem Weg suchen die Breite zu verringern.

            Ein Ansatz der OOP ist doch "geschlossen für Änderungen". Da kommt man zwar ab und an nicht drum herum aber im großen und ganzen fahre ich damit ganz gut.
            Und das mit den 20 Kindern ist halt so. Wenn man das verringert ist man doch gleich wieder bei der Strukturierten Programmierung. Das wäre ja fast so als ob dein Schniedel nach dem zweiten Kind abfault, weil es sonst zu kompliziert wird für den lieben Gott alle Seelen zu beobachten ;).

            Gruß
            Sexualforscher
            T-Rex

            1. Egal, also hast Number of Childs = 20. Die Nachteile einer breiten Vererbungshierarchie ist, das es sehr Aufwendig ist Elternklassen zu ändern. Auch wird das schreiben von Testfällen keinen Spaß machen. Ich würde nach einem Weg suchen die Breite zu verringern.

              Ein Ansatz der OOP ist doch "geschlossen für Änderungen". Da kommt man zwar ab und an nicht drum herum aber im großen und ganzen fahre ich damit ganz gut.
              Und das mit den 20 Kindern ist halt so.

              Ich vermute ganz ganz stark, dass du hier ein anderes Konzept anwenden solltest. Ich empfehle dir das Buch "Head First Design Patterns" zu lesen.
              Was du vielleicht hättest anders machen sollen, kann man hier nur sagen, wenn das Ziel bekannt ist.
              Ein Beispiel wäre vielleicht das Dekorierer Entwurfsmuster: http://de.wikipedia.org/wiki/Dekorierer

  2. Mahlzeit;

    Würde gerne eure Meinung hören

    Gerne ;)

    Vererbung ist eine feine Sache und das Überschreiben(Overload) von Methoden ist sozusagen Alltag. Es ist eine gute Angewohnheit, diejenigen Methoden, bei denen ein Overload beabsichtigt ist, grundsätzlich in der Basisklasse zu definieren und wenn es nur ein Dummy ist:

      
    class Manager{  
    	// Dummies  
    	public function browse(){$this->STASH = array();}  
    	public function control(){}  
    	public function body(){ return '<pre>BODY DUMMY</pre>';}  
    }  
    
    

    Das ist bei der Fehlersuche und in der Entwicklungsphase sehr hilfreich, es ist dann genau zu sehen, ob die Subklasse erstellt werden konnte und grundsätzlich funktioniert; beispielsweise wird die body()-Methode stets aufgerufen, es ist zu sehen dass sie tut und erst dann wird das Overload geschrieben.

    Des Weiteren (so meine Praxis) sind Vererbungen über 'mehrere Ebenen' nicht immer sinnvoll, i.d.R. reicht es, eine Subklasse abzuleiten. An

    Baseclass::Sub::SubSub::SubSubsub

    verlierst Du schnell die Freude und auch die Übersicht ;)

    Es ist auch so, dass ich eine Klasse sehr genau kennen muss, wenn ich von dieser Klasse erben möchte, was natürlich der Fall ist, wenn ich den PAPA selbst geschrieben habe. Andernfalls nutze ich die Möglichkeit der Delegation ausgewählter Methoden fremder Klassen und habe damit sehr gute Erfahrungen gemacht.

    Schönen Sonntag!

    1. Hi,

      Es ist eine gute Angewohnheit, diejenigen Methoden, bei denen ein Overload beabsichtigt ist, grundsätzlich in der Basisklasse zu definieren und wenn es nur ein Dummy ist:

      da sieht man die Fehler aber erst zur runtime. Wenn eine Basisklasse definiert, dass Kindklassen eine Methode besitzen müssen, dann ist es sinnvoll, diese als abstrakt zu definieren. Dann bekommt man fehlerhafte Kind-Implementierungen sofort zur compile-time.

      In der Basis-Klasse eine Dummy-Methode zu implementieren sorgt nur dafür, dass Fehler nicht oder nur schwer erkannt werden können. Z.B. wäre es nötig, einen Unittest zu schreiben, der testet, ob du die Methode implementiert hast, während das mit einer abstrakten Basisklassenmethode unnötig ist (der Fehler kommt schon, bevor Tests ausgeführt werden können und ist damit inhärent vorhanden).

      Bis die Tage,
      Matti

  3. Ein Prinzip der OOP heißt ja offen für Erweiterung. Doch sehe ich das Prinzip ziemlich absurd an, wenn ich an die 20 Ableitungen anpassen müsste.

    Vielleicht willst du ja das genau so haben, dass auch wirklich jede Ableitung einer Klasse eine bestimmte Methode implementieren MUSS, weil die Ableitung sonst ungültig ist.

    Beispiel: Du erstellst eine Basisklasse für eine Datenstruktur, die gewisse Operationen auf den Daten bereitstellt. Diese Klasse kann abgeleitet werden, um die Daten auf verschiedene Arten zu speichern oder zu laden. Alles sonstige bleibt gleich, abgeleitet wird ausschließlich um die Methoden zum laden und speichern anzupassen.

    • Datenbank
    • CSV Datei
    • XML Datei
    • sonstige Formate je nach Bedürfnis

    Dafür sind abstrakte Methode sinnvoll, denn eine Ableitung wird vielleicht nur wegen den Unterschieden dieser Methoden gemacht. Dann kann dir auch gleich der Compiler sagen dass noch was fehlt.

  4. Damit ich die 20 Ableitungen nicht alle anpassen muss, bekommt die erste Ebene meiner Vererbungshierarchie eine "Dummy" Methode

    Stell dir mal vor jemand anderes benutzt dein Programm. Da ist nun also eine Methode, die da lautet "Diese Methode macht aus X ein Y". Er ruft sie also auf, aber anstatt das zu machen, was sie verspricht, tut sie etwas völlig anderes. Deine Lösung ist also definitiv nicht gut.

    Wenn von deinen 20 Klassen nur 4 Klassen die Methode haben sollen, dann läuft in deiner Vererbung etwas falsch oder anders gesagt: du hättest keine Vererbung verwenden sollen, weil die Klassen anscheinend keine Kindklassen sind.

    Was du stattdessen hättest tun sollen, kann man pauschal nicht sagen. Dazu müsstest du konkret beschreiben, was das für Klassen sind und was sie tun sollen bzw. nicht tun sollen.

    1. Damit ich die 20 Ableitungen nicht alle anpassen muss, bekommt die erste Ebene meiner Vererbungshierarchie eine "Dummy" Methode

      Stell dir mal vor jemand anderes benutzt dein Programm. Da ist nun also eine Methode, die da lautet "Diese Methode macht aus X ein Y". Er ruft sie also auf, aber anstatt das zu machen, was sie verspricht, tut sie etwas völlig anderes. Deine Lösung ist also definitiv nicht gut.

      Vererbung heißt auch Overload. Eine Methode, die in der Basisklasse aus einem X ein Y macht, kann und darf in einer Subklasse aus einem X auch ein Z machen.

      Hier kommt es freilich ein bischen darauf an, darauf zu achten, dass eine überladene Methode nicht was "völlig anderes macht", es sollte wenigstens dem Verwendungszweck einer Subklasse entsprechen.

      Und diesen Verwendungszweck sollten "Mitarbeiter"  nicht anhand des vergebenen Methoden-Namen ersehen sondern aus einem übersichtlichen Code lesen und aus der gesamten Klassenhierarchie erkennen können (s. Anm.).

      Genauso verhält es sich mit "sprechendenden" Variablennamen, wenn eine Hash-Referenz $r_hash heißt (genial, eine Zeile weiter sieht das jeder selbst *G), schön und gut, besser ists jedoch, dann auch noch hinzuschreiben, welche Werte aus dieser Referenz in einer jeweiligen Methode benutzt werden, z.B. so

      Anm.: Siehe Ursprungspost cite: jetzt arbeite ich schon ziemlich lange Objekt orientiet. :cite

      Danke T-Rex!

      1. Damit ich die 20 Ableitungen nicht alle anpassen muss, bekommt die erste Ebene meiner Vererbungshierarchie eine "Dummy" Methode

        Stell dir mal vor jemand anderes benutzt dein Programm. Da ist nun also eine Methode, die da lautet "Diese Methode macht aus X ein Y". Er ruft sie also auf, aber anstatt das zu machen, was sie verspricht, tut sie etwas völlig anderes. Deine Lösung ist also definitiv nicht gut.

        Vererbung heißt auch Overload. Eine Methode, die in der Basisklasse aus einem X ein Y macht, kann und darf in einer Subklasse aus einem X auch ein Z machen.

        Ja, syntaktisch darf sie das. Semantisch nur dann, wenn sie sich an den Vertrag hält. In Java darfst du beispielsweise die equals() und hashCode() Methoden nicht unabhängig voneinander überschreiben, wenn das dazu führt, dass deren Ausgaben inkonsistent werden und ein equals() true ergibt, aber hashCode() einen unterschiedlichen Hash liefert.
        Du KANNST das natürlich tun und der Compiler wird nicht meckern. Es kann (und wird wahrscheinlich) früher oder später aber zu fehlern führen und ist deswegen zu vermeiden.

        Hier kommt es freilich ein bischen darauf an, darauf zu achten, dass eine überladene Methode nicht was "völlig anderes macht", es sollte wenigstens dem Verwendungszweck einer Subklasse entsprechen.

        Genau das ist der Punkt, weswegen eine Dummy Methode eben ganz ganz böse ist und man soetwas nicht tun sollte.

        Und diesen Verwendungszweck sollten "Mitarbeiter"  nicht anhand des vergebenen Methoden-Namen ersehen sondern aus einem übersichtlichen Code lesen und aus der gesamten Klassenhierarchie erkennen können (s. Anm.).

        Es ist dennoch schlechter Stil.

        Genauso verhält es sich mit "sprechendenden" Variablennamen, wenn eine Hash-Referenz $r_hash heißt

        Bei statischer Typisierung hast du das Problem sowieso nicht. Da brauchst du kein "r_" und ähnliches, weil dir der Compiler sagt, was in der Variable drin steckt. Bei dynamischer Typisierung musst du eben "glauben" ;)

        1. Genau das ist der Punkt, weswegen eine Dummy Methode eben ganz ganz böse ist und man soetwas nicht tun sollte.

          Aber eine abstrakte Methode ist doch auch nur ein dummy.
          abstract public function getName() kann in einer Subklasse "T-Rex" oder true zurückgeben. Beim Aufruf sieht es natürlich etwas anders aus.

          Es ist dennoch schlechter Stil.

          Ergo würdest du eine abstrakte Methode definieren und dir die Zeit nehmen um sie in allen Subklassen definieren?

          Gruß
          Deutscher Bundestrainer 2018
          T-Rex

          1. Genau das ist der Punkt, weswegen eine Dummy Methode eben ganz ganz böse ist und man soetwas nicht tun sollte.

            Aber eine abstrakte Methode ist doch auch nur ein dummy.

            Naja, du kannst sie aber nicht aufrufen, weil du die entsprechende Klasse gar nicht instanziieren kannst. Der meckert dich der Compiler an und es entstehen keine Laufzeitfehler, denn du weißt sofort, dass du was falsch gemacht hast.

            Es ist dennoch schlechter Stil.
            Ergo würdest du eine abstrakte Methode definieren und dir die Zeit nehmen um sie in allen Subklassen definieren?

            Ähm ja? Warum sollte ich das auch nicht wollen? Wenn die Methode sowieso in jeder Subklasse gleich ist, dann deklariere ich sie nicht als abstrakt, sodass die Subklassen sie erben. Wenn sie dann in einzelnen Subklassen unterschiedlich implementiert sein soll, dann überschreibe ich sie in den jeweiligen Subklassen.
            Wenn hingegen eine der Subklassen diese Methode gar nicht haben soll (d.h. man darf sie nicht aufrufen, weil das keinen Sinn ergibt), dann sollte diese Klasse eben keine Subklasse sein und man hat das Konzept der Vererbung fälschlicherweise eingesetzt, wo man ein anderes hätte anwenden sollen.

      2. Hi,

        Vererbung heißt auch Overload. Eine Methode, die in der Basisklasse aus einem X ein Y macht, kann und darf in einer Subklasse aus einem X auch ein Z machen.

        in PHP ist das erlaubt. Sinnvoll ist es freilich nur, wenn Z auch ein Y ist, d.h., Z von Y abgeleitet ist (bzw. Y implementiert).

        interface BaseVar {
          public function meineMethode();
        }

        class BaseVarY implements BaseVar {
          public function meineMethode() { return 'Ich bin Y'; }
        }

        class BaseVarZ implements BaseVar {
          public function meineMethode() { return 'Ich bin Z'; }
        }

        class Base {

        /**
           * @return Y y-from-x
           */
          public function getObject(X $x) {
             return new BaseVarY();
          }
        }

        class Derived extends Base {

        /**
           * @return Z y-from-x
           */
          public function getObject(X $x) {
             return new BaseVarZ();
          }
        }

        $obj = rand() % 2 ? new Base() : new Derived();
        // es ist zufällig, ob $obj->getObject() ein BaseVarY oder ein BaseVarZ ist.
        // ich kann mir aber sicher sein, dass das zurückgegebene Object die Methode 'meineMethode' hat.
        echo $obj->meineMethode();

        Es bringt nur potentielle Fehlerquellen, OOP anders zu verwenden.
        Die Basisklasse beschreibt ein Mindestinterface, an dass sich Kindklassen zu halten haben.
        Hat die Basisklasse eine Methode, welche ein X verlangt und ein Y zurückgibt, dann muss jedes overload dieser Methode ein X oder ein parent von X verlangen (es darf die Forderung also relaxieren auf Vaterklassen), und muss ein Y oder eine Kindklasse von Y zurückgeben (also den Werteraum einschränken).

        Nur weil in PHP (ohne Benutzung von Type-Hinting) mehr erlaubt ist, sollte man es schon lange nicht tun.

        Bis die Tage,
        Matti

  5. Moin,

    möchte hier eine Art Abschluss schaffen in dem ich die für mich gefundene Regel präsentiere.

    Für die Klasse wichtige Methoden sollten immer abstract definiert sein.
    Eine Dummy Methode bietet sich bei einer Plugin Programmierung an, da sie Schreibarbeit erspart. Ein Beispiel:

    Es gibt eine Methode save, die speichert ein Array

    public function save()
    {
        $this->_beforeSave( $arSaveDaten );
        //--- speichert Array
        $this->_afterSave( $arSaveDaten );
    }

    damit man die Methode _beforeSave nicht immer definieren muss und den Code zumüllt (es kann ja relativ vieler solcher Methoden geben), ist meine Empfehlung diese Methode nicht abstrakt zu definieren. Somit muss man sie bei einer Ableitung nicht definieren, wenn man sie nicht braucht.

    Die Gefahr ist natürlich wie schon beschrieben, dass ein Fehler nicht oder nur sehr schwer erkannt wird. Wenn man z.B. von der Klasse ableitet und die Methode _betoreSafe nennt, wird sie nie aufgerufen und es gibt keinen Fehler. Desweiteren könnten diese Methoden unterschiedlich aufgerufen werden. Bei der einen Ableitung wird noch ein Parameter übergeben, bei einer anderen Ableitung gar 2 etc...

    Ob man jetzt dem Array Daten hinzufügt ist für das Speichern nicht weiter wichtig, deshalb ist die Methode _beforeSave nicht abstrakt. In der Basisklasse würde es eine Dummy Methode geben, welche einfach das Array unbehandelt wieder zurück gibt.

    Anders sieht es aus, wenn es eine Methode _checkData() gibt, welche die Daten vor dem speichern nochmal überprüft. Diese Methode wiederum wäre abstrakt. Sie wird wahrscheinlich von jeder Ableitung benutzt.

    Gruß
    Abschluss bereiter
    T-Rex

    1. Hi,

      Für die Klasse wichtige Methoden sollten immer abstract definiert sein.
      Eine Dummy Methode bietet sich bei einer Plugin Programmierung an, da sie Schreibarbeit erspart. Ein Beispiel:

      Es gibt eine Methode save, die speichert ein Array

      public function save()
      {
          $this->_beforeSave( $arSaveDaten );
          //--- speichert Array
          $this->_afterSave( $arSaveDaten );
      }

      damit man die Methode _beforeSave nicht immer definieren muss und den Code zumüllt (es kann ja relativ vieler solcher Methoden geben), ist meine Empfehlung diese Methode nicht abstrakt zu definieren. Somit muss man sie bei einer Ableitung nicht definieren, wenn man sie nicht braucht.

      Dein Klassendesign ist kaputt. Deine Klasse macht wahrscheinlich zu viel. Wenn die Funktionen zum funktionieren der Kindklassen notwendig sind, dann sind sie abstrakt zu definieren. Sind sie nur für einige Kinder, aber nicht alle notwendig, dann erben diese Kinder von einer (abstrakten) Zwischenklasse, welche diese Funktionen vorschreibt (besser: sie implementieren ein entsprechendes Interface).

      Eine Klasse ist kein Eimer, in dem man alle Funktionen hineinwirft, die irgendwie zusammengehören. Wenn du das realisiert hast, werden deine Probleme sich auch in Luft auflösen.

      Bis die Tage,
      Matti

  6. Hi,

    ich schreibe mal rein aus dem Gedächtnis ohne Gewähr.

    Von als abstract deklarierte Klassen oder Methoden dürfen keine direkten Instanzen erstellt werden (Für den Compiler wichtig). Sie dürfen genutzt werden (eine Implementierung ist vorhanden) aber nur in Ableitungen.

    Damit ich die 20 Ableitungen nicht alle anpassen muss, bekommt die erste Ebene meiner Vererbungshierarchie eine "Dummy" Methode, welche einen Standardwert zurück gibt. Wenn eine Ableitung diese Methode benutzen möchte überschreibt sie einfach die dummy Methode und füllt die echte Methode mit entsprechendem Leben.

    Nein, sie werden nicht überschrieben, sie müssen implementiert werden und können dann auch gerne die abstrakte Implementierung der Basisklasse nutzen. Was du meinst sind virtuelle Methoden. Diese zeigen dann auf die tatsächliche Implementierung der Instanz.

    Beides ist nicht mit Schnittstellendefinitionen (interface) zu verwechseln. Wenn du zur Compiler-Zeit niemals gesagt hast, dass deine Klasse das oder jenes kann, dann nützt dir auch kein abstract oder virtual.

    Gruß Hirnbrand