2Danke
ERLEDIGT
NEIN
NEIN
ANTWORTEN
13
13
ZUGRIFFE
298
298
EMPFEHLEN
-
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:
- http://www.tutorials.de/php/364337-k...u-aendern.html
- http://www.tutorials.de/php/357633-k...erweitern.html
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:
Bitte beachten: Bisher nur argumentlose Konstruktor und Methoden!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
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)
-
05.08.10 19:55 #2
- 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.Grüße
--
Qualität des Codes wird in WTF's/Min gemessen: Je mehr, desto schlechter der Code ;-)
-
06.08.10 00:11 #3
- 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.
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.
-
06.08.10 08:05 #4
- 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 ;-)
-
06.08.10 15:00 #5
- 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.
-
06.08.10 19:51 #6
- 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 ;-)
-
06.08.10 21:17 #7
- 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.
-
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:
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.
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".
Das wurde separiert um Speicher zu sparen. Sonst würden die Variablen und Infos überall kreuz und quer in allen Klassen rumliegen.
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)
-
07.08.10 14:37 #9
- Registriert seit
- May 2006
- Ort
- There is no place like 127.0.0.1
- Beiträge
- 3.520
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 ;-)
-
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)
-
13.08.10 15:30 #11
- 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
RegistryCode 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; } } ?>
ExtensionableCode PHP:1 2 3
<?php class Registry {} ?>
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:
Im Gegensatz zu der Variant Zodiacs erben alle angesprochenen Klassen von der Klasse Extensionable.Code PHP:1 2 3 4 5
<?php function extend($extensionable, $extension) { ExtensionStorage::extend($extensionable, $extension); } ?>
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.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 ?>
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.
-
13.08.10 22:09 #12
- 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.08.10 22:44 #13
- 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.08.10 11:27 #14
- 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
-
Diskussion
Von MiMi im Forum SmalltalkAntworten: 16797Letzter Beitrag: 13.02.12, 15:46 -
[Eclipse Helios] Plugin, um Unit-Test Klassen zu erstellen/zu managen?
Von DarthShader im Forum JavaAntworten: 2Letzter Beitrag: 08.11.10, 11:17 -
Erweiterbare Anwendungsarchitektur mit MAF/MEF
Von StupidBoy im Forum .NET Application und Service DesignAntworten: 0Letzter Beitrag: 11.03.10, 19:48 -
test test lösch ich gleich wieder^^
Von 3Cyb3r im Forum C/C++Antworten: 0Letzter Beitrag: 30.09.09, 09:23 -
OOP Diskussion [Aus PHP - Klassen - Ja/Nein ? - ]
Von Saber im Forum Coders TalkAntworten: 36Letzter Beitrag: 05.11.03, 09:58





Zitieren


Login






[PHP][Snippet] Array zu XML konvertieren