tutorials.de Buch-Aktion 05/2012
Like Tree2Danke
  • 1 Beitrag von saftmeister
  • 1 Beitrag von einfach nur crack
ERLEDIGT
NEIN
ANTWORTEN
13
ZUGRIFFE
298
EMPFEHLEN
  • An Twitter übertragen
  • An Facebook übertragen
AUF DIESES THEMA
ANTWORTEN
  1. #1
    Registriert seit
    Mar 2004
    Beiträge
    1.854
    Blog-Einträge
    2
    Eigentlich könnte der Titel auch "Multiple-erweiterbare Klassen" oder "Mehrfachdefinition von Methoden" sein, so wie es in diesen beiden Threads oft ein Problem war:
    Lösungen konnten durch Aspects, das runkit oder "Stubbles - Extending Objects With New Methods At Runtime" erreicht werden.

    Leider sind dies keine Hausmittel bzw. können diese Konzepte nur umständlich Methoden ersetzen / erweitern. Daher habe ich mich mit einem Freund zusammengesetzt und eine Klasse "Extensionable" geschrieben, die im Anhang zu finden ist.

    Wie ist die Klasse zu verstehen?
    Die bereits in eurem System enthaltenen und laufenden Klassen können nun die Schnittmenge mehrerer anderer Klassen repräsentieren und behalten ihren eigenen Namen bei.
    Das ganze hört sich nach viel Speicher an, jedoch wurde es so ausgelegt, dass wirklich nur die nötigen und neuen Methoden eingebunden werden. Es ist ein Gemisch aus Strategy-Pattern, Bäumen und SkipLists.

    Kosten?
    Leider ist die Laufzeit der eizelnen Aktionen noch nicht berechnet aber die schätze ich auf O(log n) [mit n als Anzahl der Erweiterungen], da durch eine einfache SkipList iteriert wird.
    Jedes Blatt (wenn man alle Erweiterungen als Baum sieht) kostet ein Objekt im Speicher, was durch das Strategy-Pattern bedingt ist.

    Beispiel:
    Ihr habt eine Klasse für den View. Diese soll multilingual werden und gleichzeitig um Zugriffsrechte erweitert werden. Später kommt zu dem multilingualen noch sprechende URLs hinzu. Leicht lösbar:
    PHP-Code:
    class View extends Extensionable
    { ... }

    // mehrere Sprachen
    Hook::registerExtension("MultiLang""View");
    class 
    MultiLang extends View {...}

    // sprechende URLs
    Hook::registerExtension("URL""MultiLang");
    class 
    URL extends MultiLang {...}

    // Zugriffsrechte
    Hook::registerExtension("Rights""View");
    class 
    Rights extends View {...}

    $x = new View(); // verhält sich wie Rights, URL und View selbst 
    Bitte beachten: Bisher nur argumentlose Konstruktor und Methoden!
    Angehängte Dateien Angehängte Dateien
     
    Gebe keine Hilfe per PN, Mail, Instant Messenger etc.
    und keine Copy&Paste-Lösungen - ein bisschen selbst nachdenken sollte drin sein. Konstruktivismus 4tw!


    MfG, Zod

    __________________
    rpd Framework: Rapid Web-Engineering in PHP (Manual | Google Code)

  2. #2
    Avatar von saftmeister
    saftmeister saftmeister ist offline Der Saft sei mit dir
    tutorials.de Premium-User
    Registriert seit
    May 2006
    Ort
    There is no place like 127.0.0.1
    Beiträge
    3.520
    Interessant. Aber worin besteht der Vorteil gegenüber schlichter Vererbung? Dieses System stelle ich mich unglaublich schwer zu warten und vor allem schwer zu debuggen.

    Versteh mich bitte nicht falsch, aber ich habe gern ein nach oben hin abgeschlossenes System. Ich sehe auch noch keinen Use-Case für solch ein Pattern.

    Was mir auch ein bisschen missfällt an dieser Stelle ist, das eine public static Member-Variable benötigt wird. Könnte man Extensionable nicht von Hook erben lassen? Bzw. was bringt es für Vorteile, die Extensions in einer separaten Klasse abzulegen?

    Man könnte wenigstens einen Getter einbauen (für die Extensions) und die Member-Var private deklarieren.

    Nenn doch bitte mal ein paar Use-Cases und Vorteile gegenüber klassischer Vererbung.
    ZodiacXP bedankt sich. 
    Grüße
    --
    Qualität des Codes wird in WTF's/Min gemessen: Je mehr, desto schlechter der Code ;-)

  3. #3
    Avatar von einfach nur crack
    einfach nur crack einfach nur crack ist offline mag Cookies & Kekse
    tutorials.de Premium-User
    Registriert seit
    May 2007
    Ort
    Dresden (Sachsen)
    Beiträge
    1.961
    Also ich finde den Ansatz auch interessant, da ich persönlich auch schon vor dem Problem stand, dass ich eine Klasse hatte, die aber von mehreren Klassen erben sollte. Das ist leider in dieser Form bei PHP nicht möglich, deshalb bin ich dir schon jetzt dankbar dafür, dass mal jemand über eine Lösung dieses Problems nachdenkt.
    ZodiacXP bedankt sich. 
    Ich mag:
    • positive Bewertungen meiner Beiträge
    • ein Danke für meine hilfreichen Beiträge

    Dabei kann ich dir helfen: PHP --- Javascript --- Ruby --- Coffeescript --- CSS --- HTML --- Webtechnologien --- Shell --- UNIX

    ... noch was: falls du mit dem Thema hier fertig bist, dann kannst du es auch als erledigt markieren.

  4. #4
    Avatar von saftmeister
    saftmeister saftmeister ist offline Der Saft sei mit dir
    tutorials.de Premium-User
    Registriert seit
    May 2006
    Ort
    There is no place like 127.0.0.1
    Beiträge
    3.520
    Kurze Frage: Wieviele Mütter/Väter hast du?

    Gegeben sei Klasse A, B und C; A und B sind Klassen, die Funktionalität bereit stellen, die in C gebraucht werden könnten.

    Wäre es nicht logischer, C von A erben zu lassen und anschließend eine Instanz von B innerhalb von A als Member-Variable zu erzeugen?
     
    Grüße
    --
    Qualität des Codes wird in WTF's/Min gemessen: Je mehr, desto schlechter der Code ;-)

  5. #5
    Avatar von einfach nur crack
    einfach nur crack einfach nur crack ist offline mag Cookies & Kekse
    tutorials.de Premium-User
    Registriert seit
    May 2007
    Ort
    Dresden (Sachsen)
    Beiträge
    1.961
    Das mag sein, dass man vom rein logischen immer nur einen Vater und eine Mutter hat, aber Interfaces können auch von mehreren Interfaces erben. Dementsprechend wäre solch eine Möglichkeit für Klassen auch schön. Aber du hast schon Recht, dass man ansonsten eine Hauptklasse hat, welche in sich dann weitere Instanzen von anderen Klassen führt, so wie ich es bisher gelöst habe. Wenn man denen dann noch eine Referenz auf $this mit gibt, dann können die anderen Methoden auch problemlos die Hauptklasse verändern.
     
    Ich mag:
    • positive Bewertungen meiner Beiträge
    • ein Danke für meine hilfreichen Beiträge

    Dabei kann ich dir helfen: PHP --- Javascript --- Ruby --- Coffeescript --- CSS --- HTML --- Webtechnologien --- Shell --- UNIX

    ... noch was: falls du mit dem Thema hier fertig bist, dann kannst du es auch als erledigt markieren.

  6. #6
    Avatar von saftmeister
    saftmeister saftmeister ist offline Der Saft sei mit dir
    tutorials.de Premium-User
    Registriert seit
    May 2006
    Ort
    There is no place like 127.0.0.1
    Beiträge
    3.520
    Ich bin deiner Meinung und sage ja auch nicht, das ich das für überflüssig halte. Mir fällt kein Fall ein, bei dem ich diesem Entwurfsmuster (könnte ja ein neues sein/werden) den Vorzug gegenüber der Vererbung geben würde.

    Aber das kann ZodiacXP ja noch erläutern.

    Das Thema Interface halte ich deshalb für indiskutabel, weil ein Interface nun mal eine Schnittstelle ist. Das bedeutet, andere Klassen finden über die Implementierung des beliebigen Interface eine Schnittstelle, an die sie andocken können. Daher macht das auch Sinn, das eine Klasse mehrere Schnittstellen einbinden kann. Man kann ja beliebig viele Freunde haben, die irgendwelche Sachen von einem wollen, wenn du verstehst, was ich meine

    Aber zum Test: Der ging, wie zu erwarten war, korrekt aus.
     
    Grüße
    --
    Qualität des Codes wird in WTF's/Min gemessen: Je mehr, desto schlechter der Code ;-)

  7. #7
    Avatar von einfach nur crack
    einfach nur crack einfach nur crack ist offline mag Cookies & Kekse
    tutorials.de Premium-User
    Registriert seit
    May 2007
    Ort
    Dresden (Sachsen)
    Beiträge
    1.961
    Ja, es stimmt zwar auch, dass eine Klasse mehrere Interfaces haben kann, aber was ich meinte war, dass auch Interfaces von Interfaces erben können, nur eben im Gegensatz zu Klassen von mehreren Interfaces gleichzeitig, solange diese keine gleichen Methoden definieren.
     
    Ich mag:
    • positive Bewertungen meiner Beiträge
    • ein Danke für meine hilfreichen Beiträge

    Dabei kann ich dir helfen: PHP --- Javascript --- Ruby --- Coffeescript --- CSS --- HTML --- Webtechnologien --- Shell --- UNIX

    ... noch was: falls du mit dem Thema hier fertig bist, dann kannst du es auch als erledigt markieren.

  8. #8
    Registriert seit
    Mar 2004
    Beiträge
    1.854
    Blog-Einträge
    2
    Danke das ihr so rege an der Diskussion teilnehmt. Schön eine Partei zu sehen die eher dafür ist und eine die eher dagegen ist Da kommen die Vor- und Nachteile richtig zur Geltung.

    Unser Ansatz richtet sich mehr an das Problem, welches "einfach nur crack" beschreibt.
    Eine Klasse soll von mehreren anderen Erben. Aber zunächst mal eine Antwort auf die Fragen:

    Zitat Zitat von saftmeister Beitrag anzeigen
    Interessant. Aber worin besteht der Vorteil gegenüber schlichter Vererbung? Dieses System stelle ich mich unglaublich schwer zu warten und vor allem schwer zu debuggen.
    Schlichte Vererbung ist wie du selbst mit Mutter/Vater zeigst "Monogam" Mit dem Hook als "Registrierungsstelle" und der Klasse können mehrere gleichzeitig genutzt werden. Klar, das löst jeder bisher durch Decorator- oder Facade-Pattern. Jedoch blähen diese Ansätze den Code und auch den Speicher mehr auf als die hier vorgestellte lösung, da die "Registrierungsstelle" weis, wie vererbt wurde und so nur die Endknoten und somit wirklich relevanten Klassen berücksichtigt. Insofern ist das Warten und Debuggen von gleichem Aufwand (wenn nicht sogar geringer) als bei dem Decorator/Facade.


    Zitat Zitat von saftmeister Beitrag anzeigen
    Versteh mich bitte nicht falsch, aber ich habe gern ein nach oben hin abgeschlossenes System. Ich sehe auch noch keinen Use-Case für solch ein Pattern.
    Klar, ich hab abgeschlossene Systeme auch gern. Dieses Problem besteht dennoch zum Beispiel bei Systemen welche verschiedene Erweiterungen dynamisch aufnehmen (Beispiel: CMS, Frameworks, etc.). Da ist es super nicht in den Quelltext vom System eingreifen zu müssen, sondern einfach seine Erweiterung "anzuhängen".

    Zitat Zitat von saftmeister Beitrag anzeigen
    Was mir auch ein bisschen missfällt an dieser Stelle ist, das eine public static Member-Variable benötigt wird. Könnte man Extensionable nicht von Hook erben lassen? Bzw. was bringt es für Vorteile, die Extensions in einer separaten Klasse abzulegen?
    Das wurde separiert um Speicher zu sparen. Sonst würden die Variablen und Infos überall kreuz und quer in allen Klassen rumliegen.

    Zitat Zitat von saftmeister Beitrag anzeigen
    Man könnte wenigstens einen Getter einbauen (für die Extensions) und die Member-Var private deklarieren.
    Wie sähe hier dein Ansatz aus?

    --

    So hoffe die Vorteile gg. klassischer Vererbung sind hinreichend erklärt worden. Sonst gerne fragen.

    Es geht halt darum ein laufendes System so gut wie nicht anzufassen und es trotzdem Erweitern zu können. Mit dieser Lösung ist es besonders schön weil selbst die Erweiterungen mehrfach erweitert werden können.

    Die Use-Case hierzu wäre das rasche lösen von neu hinzugekommenen Use-Cases, die bei einem abgeschlossenen System bisher nicht beachtet worden sind Beispiel: CMS, Foren, Wiki und sonstige Informationssysteme denen neue Anforderungen zukommen sollen wie Multilinguale Seiten, sprechende URL, Rechtesysteme, und was der Wandel noch so mit sich bringt. So kann sehr kurzfristig auf Änderungen eingegangen werden, während die "Neu-Modellierung" des Systems noch stunden-/tagelang geplant wird.
     
    Gebe keine Hilfe per PN, Mail, Instant Messenger etc.
    und keine Copy&Paste-Lösungen - ein bisschen selbst nachdenken sollte drin sein. Konstruktivismus 4tw!


    MfG, Zod

    __________________
    rpd Framework: Rapid Web-Engineering in PHP (Manual | Google Code)

  9. #9
    Avatar von saftmeister
    saftmeister saftmeister ist offline Der Saft sei mit dir
    tutorials.de Premium-User
    Registriert seit
    May 2006
    Ort
    There is no place like 127.0.0.1
    Beiträge
    3.520
    Zitat Zitat von ZodiacXP Beitrag anzeigen
    Wie sähe hier dein Ansatz aus?
    Ich würde $extension im Hook privat deklarieren und einen public static Sucher dafür schreiben:

    Code php:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    
    class Hook
    {
        private static $extension;
        
        public static function registerExtension($add, $orig)
        {
            self::$extension[$orig][$add] = $add;
        }
     
            public static function hasExtension($extension)
            {
                    return in_array($extension, self::$extension);
            }
    }

    Innerhalb der Extensionable dann ein Zugriff über den Sucher, der true zurückgibt, wenn es die gewünschte Klasse gibt.

    Macht logisch keinen Unterschied, sieht aber schöner aus und keiner pfuscht zur Laufzeit in der Hook-Property rum.

    --------

    Soweit habe ich den Ansatz und seine Vorteile verstanden. Kannst du schon sagen, was da an Ressourcen eingespart wird?
     
    Grüße
    --
    Qualität des Codes wird in WTF's/Min gemessen: Je mehr, desto schlechter der Code ;-)

  10. #10
    Registriert seit
    Mar 2004
    Beiträge
    1.854
    Blog-Einträge
    2
    Klar! Wo war ich denn grad mit meinen Gedanken!?
    Ach, mit dem "setter" (registerFunction) soll nur verhindert werden, dass eine Erweiterung falsch angehängt wird. Eigentlich kann der setter eher weg, was auch höchstwahrscheinlich passieren wird um Lightweight entgegen zu kommen.
    Geändert von ZodiacXP (07.08.10 um 15:16 Uhr)
     
    Gebe keine Hilfe per PN, Mail, Instant Messenger etc.
    und keine Copy&Paste-Lösungen - ein bisschen selbst nachdenken sollte drin sein. Konstruktivismus 4tw!


    MfG, Zod

    __________________
    rpd Framework: Rapid Web-Engineering in PHP (Manual | Google Code)

  11. #11
    Avatar von einfach nur crack
    einfach nur crack einfach nur crack ist offline mag Cookies & Kekse
    tutorials.de Premium-User
    Registriert seit
    May 2007
    Ort
    Dresden (Sachsen)
    Beiträge
    1.961
    Hallo ihr beiden,

    ich habe mich jetzt ebenfalls mit diesem Thema näher auseinander gesetzt und eine eigene Variante geschrieben, welche eines der beiden Probleme von Zodiac löst: die argumentlosen Methoden und die klassenübergreifende Variablendeklaration.

    ExtensionStorage
    Code PHP:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    
    <?php
        class ExtensionStorage {
            private static $extensions = array();
     
            public static function get($extensionable) {
                if(isset(self::$extensions[$extensionable]))
                    return self::$extensions[$extensionable];
                return false;
            }
     
            public static function has($extensionable) {
                return isset(self::$extensions[$extensionable]);
            }
     
            public static function extend($extensionable, $extension) {
                self::$extensions[$extensionable][$extension] = $extension;
            }
        }
    ?>
    Registry
    Code PHP:
    1
    2
    3
    
    <?php
        class Registry {}
    ?>
    Extensionable
    Code PHP:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    
    <?php
        class Extensionable {
            private $__methods = array();
            private $__variables = null;
     
            public function __construct($variables = null) {
                if($variables instanceof Registry)
                    $this->__variables = $variables;
                else
                    $this->__variables = new Registry;
                $this->__get_parent(get_called_class());
            }
     
            private function __get_parent($parent) {
                if($extensions = ExtensionStorage::get($parent)) {
                    foreach($extensions as $extension) {
                        if(ExtensionStorage::has($extension)) {
                            $this->__get_parent($extension);
                        }
                        else {
                            if(($methods = get_class_methods($extension)) !== null && $methods !== array()) {
                                $instance = new $extension($this->__variables);
                                foreach($methods as $method) {
                                    if($method[0] . $method[1] === '__')
                                        continue;
                                    if(isset($this->__methods[$method]))
                                        throw new Exception('Cannot redeclare method "' . $method . '"');
                                    else
                                        $this->__methods[$method] = $instance;
                                }
                            }
                        }
                    }
                }
            }
     
            public function __call($method, $parameters) {
                if(isset($this->__methods[$method]))
                    return call_user_func_array(array($this->__methods[$method], $method), $parameters);
            }
     
            public function __get($name) {
                if(isset($this->__variables->{$name}))
                    return $this->__variables->{$name};
                return null;
            }
     
            public function __set($name, $value) {
                $this->__variables->{$name} = $value;
            }
        }
    ?>

    Und damit das Ganze auch noch hübsch zu erweitern ist:
    Code PHP:
    1
    2
    3
    4
    5
    
    <?php
        function extend($extensionable, $extension) {
            ExtensionStorage::extend($extensionable, $extension);
        }
    ?>
    Im Gegensatz zu der Variant Zodiacs erben alle angesprochenen Klassen von der Klasse Extensionable.
    Code PHP:
    1
    2
    3
    4
    5
    6
    7
    8
    
    <?php
        class A extends Extensionable {}
        class B extends Extensionable {}
        class C extends Extensionable {}
     
        extend('A', 'B'); # erweitert A um B
        extend('A', 'C'); # erweitert A um C
    ?>
    Weiterhin bleibt das Problem, dass man dem Konstruktor keine spezielle Funktionalität zuweisen kann. Man könnte dies allerdings über ein Callback zu einem Alias-Konstruktor (beispielsweise "initialize") lösen, welchem man die Argumente des Konstruktors übergibt.
    Geändert von einfach nur crack (13.08.10 um 15:33 Uhr)
     
    Ich mag:
    • positive Bewertungen meiner Beiträge
    • ein Danke für meine hilfreichen Beiträge

    Dabei kann ich dir helfen: PHP --- Javascript --- Ruby --- Coffeescript --- CSS --- HTML --- Webtechnologien --- Shell --- UNIX

    ... noch was: falls du mit dem Thema hier fertig bist, dann kannst du es auch als erledigt markieren.

  12. #12
    Avatar von saftmeister
    saftmeister saftmeister ist offline Der Saft sei mit dir
    tutorials.de Premium-User
    Registriert seit
    May 2006
    Ort
    There is no place like 127.0.0.1
    Beiträge
    3.520
    Damit schließt du aber auch nette Funktionen wie z.B. __toString() aus. Eine Sache, die ich sehr bedauere, denn ich nutze diese häufig in Templates um z.B. Forms und dergleichen zu erstellen.

    Edit:
    Oder auch so tolle Sachen wie __sleep() oder __wakeup() zum automatischen serialisieren und deserialisieren.
    Geändert von saftmeister (13.08.10 um 22:12 Uhr)
     
    Grüße
    --
    Qualität des Codes wird in WTF's/Min gemessen: Je mehr, desto schlechter der Code ;-)

  13. #13
    Avatar von einfach nur crack
    einfach nur crack einfach nur crack ist offline mag Cookies & Kekse
    tutorials.de Premium-User
    Registriert seit
    May 2007
    Ort
    Dresden (Sachsen)
    Beiträge
    1.961
    Nun gut, ich habe nur der Einfachheit wegen die Prüfung des Methodennamen über die zwei Unterstriche am Namensanfang vorgenommen, was aber letztendlich kein Problem sein sollte dies zu ändern, um auch andere magische Methoden zu erlauben. Diese würde ich dann aber anders implementieren, als über die Methode __call(). Aber davon abgesehen, wie gefällt dir diese Lösung?
     
    Ich mag:
    • positive Bewertungen meiner Beiträge
    • ein Danke für meine hilfreichen Beiträge

    Dabei kann ich dir helfen: PHP --- Javascript --- Ruby --- Coffeescript --- CSS --- HTML --- Webtechnologien --- Shell --- UNIX

    ... noch was: falls du mit dem Thema hier fertig bist, dann kannst du es auch als erledigt markieren.

  14. #14
    Avatar von saftmeister
    saftmeister saftmeister ist offline Der Saft sei mit dir
    tutorials.de Premium-User
    Registriert seit
    May 2006
    Ort
    There is no place like 127.0.0.1
    Beiträge
    3.520
    Sieht nett aus, funktioniert auch. Aber mir ist beim Testen grad was aufgefallen:

    Ich bin ein sehr fauler Entwickler und lasse meine IDE viel erledigen. Das heißt, ich nutze Code-Hinting und Auto-Completion in Eclipse. Mit dieser Art der - ich nenn es einfach mal - umgekehrten Vererbung habe ich diese Vorzüge leider nicht mehr. Ich muss immer wissen, was meine Klasse kann und was alle Klassen können, die die derzeitige Klasse erweitern. Für den Entwicklungsprozess ist das etwas hinderlich.

    Bitte nicht falsch verstehen, ich will es nicht kaputt und zu Tode diskutieren, ich zeige nur Nachteile auf die durch dieses Vorgehen entstehen.

    Das bedeutet nicht, das der Ansatz schlecht wäre.
     
    Grüße
    --
    Qualität des Codes wird in WTF's/Min gemessen: Je mehr, desto schlechter der Code ;-)

Ähnliche Themen

  1. Diskussion
    Von MiMi im Forum Smalltalk
    Antworten: 16797
    Letzter Beitrag: 13.02.12, 15:46
  2. Antworten: 2
    Letzter Beitrag: 08.11.10, 11:17
  3. Erweiterbare Anwendungsarchitektur mit MAF/MEF
    Von StupidBoy im Forum .NET Application und Service Design
    Antworten: 0
    Letzter Beitrag: 11.03.10, 19:48
  4. test test lösch ich gleich wieder^^
    Von 3Cyb3r im Forum C/C++
    Antworten: 0
    Letzter Beitrag: 30.09.09, 09:23
  5. OOP Diskussion [Aus PHP - Klassen - Ja/Nein ? - ]
    Von Saber im Forum Coders Talk
    Antworten: 36
    Letzter Beitrag: 05.11.03, 09:58