Olaf Schneider: Klassen überlagern

Hallo,

ich bin gerade dabei, mir Konzepte für das Anpassen von Internetapplikationen (Sprache ist php5) zu überlegen. Die Idee ist folgende: Ich habe ein Kernsystem, welches keine kundenspezifischen Anpassungen enthalten soll. Allerdings soll jede Klasse durch eine andere (in einem anderen Ordner) ersetzt werden können, damit individuelle Anpassungen dennoch möglich sind.

Schritt eins wäre einfach: Ich schaue für jede Klasse erst in den individuellen Ornder und prüfe, ob eine Datei mit dieser Klasse exisitiert. Falls es diese Datei gibt, wird die individuelle Klasse verwendet, falls nicht, benutze ich die nicht veränderbare Klasse aus dem Kernsystem. Die Klassennamen wären dabei identisch.

Dieser Teil lässt sich mit der __autoload()-Funktion hervorragend implementieren.

So schön, so gut. In den meisten Fällen wird man jedoch nicht eine Klasse komplett ersetzen wollen, sondern ledigich ein, zwei Methoden anpassen. Also müsste die individuelle Klasse die Klasse aus dem Kernsystem mit extends erweitern. Jetzt gibt es allerdings ein Problem mit der Namensgleichheit. Die individuelle Klasse heisst bis jetzt ja genauso wie die Basisklasse. Hm …

Zwei Möglichkeiten möchte ich zur Diskussion stellen mit der Hoffnung auf weitere, bessere Möglichkeiten:

(1)

Jedes Objekt wird mit einer allgemeinen Factory erzeugt. Diese verwendet die Klasse {ClassName}_Individuell, falls diese existiert und sonst die Klasse {ClassName}

Nachteil:

Der new-Mechanismus ist nicht mehr gestattet, der gesamte Code muss auf die Verwendung dieser Fabrik umgeschrieben werden.

(2)

Jede ursprüngliche Klasse teilt sich in zwei neue Klassen:

{ClassName}_Core ist eine abstrakte Klasse, die die gesamte Funktionalität beinhaltet. {ClassName} erweitert {ClassName}_Core und ist die konkrete Klasse, auf die zugegriffen werden kann.
Jetzt kann eine individuelle Klasse von {ClassName}_Core erben und den gleichen Klassennamen {ClassName} besitzen, da ja nur genau eines der beiden Klassenfiles inkludiert wird.

Nachteil:

Jede Klasse braucht eine Abstrakte Klasse und eine leere konkrete Klasse; die Anzahl der Klassen verdoppelt sich somit.

Welche Methode fändet Ihr besser: (1), (2) oder Eure, die ich noch nicht kenne?

Gruß
Olaf

  1. Hallo Olaf,

    ich habe genau dieses Problem mit reiner Vererbung gelöst. Die Klassen in diesem "anderem" kundenspezifischem Ordner erben von den "Core" Klassen und überschreiben bei Berdarf einzelne Methoden. Wegen eines geeigneten Packagings habe ich mit Namensgeleichheit keine Probleme. Der Nachteil von meiner Lösung ist, dass in dem kundenspezifischem _alle_ Klassen vorhanden sein müssen und oft sind sie daher nur leer und machen nichts anderes, als das Erben. Möglicherweise kannst du dieses Problem mit deinem autoload Ansatz noch lösen, ich habe in Perl (mein System ist in Perl) bislang keine geeignete Lösung implementiert. Ich nutze diesen scheinbaren Nachteil letztendlich doch zu meinem Vorteil, weil dann alle Klassen quasi als "weisses Blatt" schon vorhanden sind und das hilft zum Beispiel anderen Programmierern, die das System nicht entwickelt haben, sondern nur damit arbeiten müssen. Den Vorteil in diesem Ansatz sehe ich, dass er kein gehacke und gefrickele und kein aufwendiges Pattern nach sich zieht, es ist einfach nur Vererbung und ein "don't call us, we call you" Prinzip und das ist für jeden leicht zu verstehen, der die ersten 10 Seiten eines OO-Tutorials gelesen hat.

    Gruß,
    Cruz

    1. Hallo Cruz,

      die Idee gefällt mir ziemlich gut. Sie ist mit meiner Version (2) verwandt, braucht aber keine Fallunterscheidungen im Sinne von „existiert die Klasse individuell“ etc. .

      Das schreit meiner Meinung nach förmlich nach einem Generator, der nach einem festgelegten Namensschema aus den abstrakten Klassen, die die Funktionalität enthalten, Dateien mit den konkreten, aber leeren abgeleiteten Klassen erzeugt (im Sinne eines build).

      Gruß
      Olaf

  2. Hallo Olaf,

    Die Semantik einer Sprache zu verändern ist immer etwas gefährlich. Ein "new Klasse" sollte auch eine neue Instanz dieser Klasse erzeugen, wenn man diese beim Laden durch eine andere ersetzt, sollte man genau wissen, warum man das tut. So etwas machen typischerweise irgend welche Datenbankabstraktionsschichten o.ä. die die zu ladende Klasse bspw. um Funktionalität für den transparenten Datenbankzugriff ergänzen. Dies ist aber transparent, d.h. die geladene Klasse hat im wesentlichen die Funktionalität des Originals und von den Änderungen merkt man von außen nichts (oder fast nichts ;).
    Aus diesem Grund würde ich in dem Fall einen Factory-ähnlichen Mechanismus bevorzugen.

    Du willst ja erreichen, dass Du die Implementierung der Schnittstelle austauschen kannst. Ich würde also einen zentralen Dienst in der Anwendung vorsehen, der es ermöglicht, für Schnittstellen Implementierungen zu erhalten.
    Ich weiß nicht genau, wie man das in PHP machen würde, aber ich stelle mir das in etwa so vor:

      
    public class ServiceProvider {  
      
      private ServiceProvider sp;  
      
      public static SeviceProvider getInstance() {  
        if (sp == null) {  
          sp = new ServiceProvider();  
        }  
        return sp;  
      }  
      
      public Object createServiceInstance(SerivceType type) {  
        ...  
      }  
      
    }  
    
    

    Offen ist, wie man den "SeviceType" genau spezifiziert. In Java würde man da typischerweise ein Interfaces angeben. In einer Scriptsprache tut es vermutlich auch einfach ein String mit dem Namen des Dienstes.

    Nun ist noch zu überlegen, wie man dann am besten die Implementierung findet. In der einfachsten Variante machst Du das einfach wie bisher, und suchst entsprechend einer Namenskonvention in einem Verzeichnis für die Standardimplementierungen und einem für die erweiterten Implementierungen.

    Typischerweise würde jede Implementierung dann eine Schnittstelle implementieren. Da PHP (meines Wissens) über so etwas gar nicht verfügt, kann man die Vererbung völlig offen lassen. Die implementierenden Klassen kann man von jeder anderen Klasse erben lassen.

    Grüße

    Daniel