Olaf Schneider: Klassenname von abgeleiteter Klasse in statischer Methode

Hallo,

folgender Testcode:

  
<?php  
  
abstract class Foo {  
   public final static function getClassname() {  
      return get_class();  
   }  
}  
  
class Bar1 extends Foo {}  
class Bar2 extends Foo {}  
  
echo Bar1::getClassname();  
  
?>  

Ich möchte von der abstrakten Klasse Foo mehrere konkrete Klassen wie z.B. Bar1 ableiten. In der Klasse Foo soll später eine Fabrik dann über den Aufruf Bar1::getInstance() ein Objekt der Klasse Bar1 zurückgeben. Um dieses zu tun, muss allerdings die statische Methode Foo::getInstance() wissen, mittels welcher Klasse sie aufgerufen wurde.

Das obrige Beispiel gibt (erwartungsgemäß) „Foo“ zurück. Gibt es eine Möglichkeit, in der MethodeFoo::getClassname den Namen der Unterklasse Bar1 zu erhalten, ohne diesen explizit aus jeder Unterklasse als Stringliteral zu übergeben?

Viele Grüße
Olaf Schneider

  1. Hallo,

    class Bar1 extends Foo {}

    Das obrige Beispiel gibt (erwartungsgemäß) „Foo“ zurück. Gibt es eine Möglichkeit, in der MethodeFoo::getClassname den Namen der Unterklasse Bar1 zu erhalten, ohne diesen explizit aus jeder Unterklasse als Stringliteral zu übergeben?

    es wird sich nicht anders machen lassen, als z. B. eine Variable des Objekts mit dem Namen zu befüllen.

    Gruß aus Berlin!
    eddi

  2. Moin!

    Wenn ich mal unprofessionell (mit PHP 5 Objekten habe ich noch nicht zu tun gehabt) da reinkritzeln darf:

    <?php

    abstract class Foo {
       public final static function getClassname() {
          return get_class();
       }
    }

    class Bar1 extends Foo {
      function getClassname() {
        return "Bar1";
      }
    }

    class Bar2 extends Foo {}
      function getClassname() {
        return "Bar2";
      }
    }

    echo Bar1::getClassname();

    ?>

    
    >   
    > Ich möchte von der abstrakten Klasse Foo mehrere konkrete Klassen wie z.B. Bar1 ableiten. In der Klasse Foo soll später eine Fabrik dann über den Aufruf Bar1::getInstance() ein Objekt der Klasse Bar1 zurückgeben. Um dieses zu tun, muss allerdings die statische Methode Foo::getInstance() wissen, mittels welcher Klasse sie aufgerufen wurde.  
      
    Alternativ kann natürlich in jeder abgeleiteten Klasse auch eine lokale Fabrikmethode stehen, die ihrerseits die zentrale Fabrikmethode mit dem passenden Parameter aufruft.  
      
     - Sven Rautenberg
    
    -- 
    My sssignature, my preciousssss!
    
  3. echo $begrüßung;

    Ich möchte von der abstrakten Klasse Foo mehrere konkrete Klassen wie z.B. Bar1 ableiten. In der Klasse Foo soll später eine Fabrik dann über den Aufruf Bar1::getInstance() ein Objekt der Klasse Bar1 zurückgeben.

    Meines Erachtens nach ist das ein "Missbrauch" des Fabrik-Musters (factory pattern). Eine Fabrik soll Produkte herstellen. Du hingegen versucht ein Produkt zu erhalten, welches zur Fabrik geht, um sich dort herstellen zu lassen.

    Gibt es eine Möglichkeit, in der Methode Foo::getClassname den Namen der Unterklasse Bar1 zu erhalten, ohne diesen explizit aus jeder Unterklasse als Stringliteral zu übergeben?

    Mal abgesehen von obigem Argument: mir fällt dazu keine Lösung ein. Füge mal ein

    echo '<pre>';
      print_r(debug_backtrace());

    in getClassName ein und du wirst feststellen, dass Bar1 ignoriert wird und statt dessen Foo::getClassname() direkt aufgerufen wird.

    Beachte auch diese beiden Sätze im Handbuch-Kapitel Static Keyword unterhalb Classes and Objects (PHP 5).

    "In fact static method calls are resolved at compile time. When using an explicit class name the method is already identified completely and no inheritance rules apply."

    oder in der deutschen Übersetzung:

    "Tatsächlich werden static Methodenaufrufe zum Kompilierungszeitpunkt aufgelöst. Bei der Nutzung des expliziten Klassennamens ist die Methode bereits gänzlich identifiziert und es kommen keine Vererbungsregeln zur Anwendung."

    Der zweite Satz ist vielleicht nicht ganz eindeutig, doch ich entnehme aus beiden Sätzen, dass der Compiler einmalig ermitteln, welche Klasse die auzurufende Methode bereitstellt und dann alle Hinweise auf die Aufruf- bzw. Vererbungskette entfernt.

    echo "$verabschiedung $name";

  4. Hallo eddi, hallo Sven, hallo dedlfix,

    vielen Dank für Eure Antworten.

    Wie es aussieht, werde ich doch jeder abgeleiteten Klasse Bar1 etc. eine eigene statische getClassname()-Methode verpassen. Ich hatte gehofft, dieses zu vermeiden, da diese Methoden ja alle identisch sind. (Stichwort DRY-Prinzip „Don’t repeat yourself“).

    In der realen Umsetzung (das verkürzte Beispiel war ja nur zur Demonstration des Problems) wird also jede Unterklasse eine Methode Bar1::getInstance() haben, die der  Methode Foo::getInstance() als Parameter den Klassennamen mitliefert. Diese bildet dann gegebenenfalls ein neues Objekt mit return new $objectName;.

    Ich hatte gehofft, dass das eleganter geht.

    Viele Grüße
    Olaf Schneider

    1. echo $begrüßung;

      In der realen Umsetzung (das verkürzte Beispiel war ja nur zur Demonstration des Problems) wird also jede Unterklasse eine Methode Bar1::getInstance() haben, die der Methode Foo::getInstance() als Parameter den Klassennamen mitliefert. Diese bildet dann gegebenenfalls ein neues Objekt mit return new $objectName;.

      Ich sehe momentan keinen Vorteil, die deine Methode gegenüber einem simplen

      $instance = new classname();

      bringt. Was versprichst du dir von deiner Vorgehensweise? Oder anders gefragt: Was ist dein eigentliches Problem?

      echo "$verabschiedung $name";

      1. Hallo dedlfix,

        es geht hier um Objekte, die anhand einer eindeutigen ID aus der Datenbank geladen werden sollen. Jedes Objekt mit einer bestimmten ID soll nur einmal vorhanden sein, nicht nur, um doppelte DB-Lesezugriffe zu verhindern, sondern vor allem, um eindeutige Schreibzugriffe zu ermöglichen.

        Um dieses zu erreichen, bietet die abstrakte Parentklasse aller Objekte sowohl ein statisches Array aller Unterobjekte als auch eine getInstance-Funktion, die entweder ein neues Objekt generiert und zurückgibt oder ein im Array vorhandenes Objekt zurückgibt. Daher ist die Objekterzeugung mit new außerhalb der Objektklassen nicht sinnvoll.

        Viele Grüße
        Olaf Schneider

        1. echo $begrüßung;

          es geht hier um Objekte, die anhand einer eindeutigen ID aus der Datenbank geladen werden sollen. Jedes Objekt mit einer bestimmten ID soll nur einmal vorhanden sein, nicht nur, um doppelte DB-Lesezugriffe zu verhindern, sondern vor allem, um eindeutige Schreibzugriffe zu ermöglichen.

          Ja, das liest sich nach einem Anwendungsfall für das Factory-Pattern.

          Du sparst dir ein wenig Schreibarbeit, wenn du

          Bar1::getInstance()

          statt

          Foo::getInstance('Bar1')

          verwendest. Das erkaufst du dir aber, indem du jeder Bar-Klasse eine eigene getInstance-Methode mitgibst, die dann Foo::getInstance(...) aufruft. Mir wäre das zu teuer.

          Method overloading wäre noch eine Möglichkeit, um mit $foo->getBarX() an BarX zu gelangen, doch geht das nicht mit statischen Methodenaufrufen.

          echo "$verabschiedung $name";