Ein bisschen mehr Delegation bitte…

Die Vererbung in der OOP hat ja durchaus eine Berechtigung. Leider wird sie viel zu oft unbedacht eingesetzt. Ein prominentes Beispiel ist die Java-Klasse Properties. Diese leitet von der Klasse Hashtable ab und erbt somit auch alle Methoden der Oberklasse. Aber dieser Zusammenhang zwischen Properties und HashMap passt nicht ganz (Properties sind eine HashMap, „is-a“-Beziehung). Meiner Meinung nach würde hier eine „Properties verwendet eine HashMap“-Beziehung besser passen. Natürlich kann man darüber streiten, aber ich möchte hier nicht einer OOP-Diskussion lostreten, sondern für PHP die Möglichkeit der Delegation vorstellen.

In meinem Beispiel möchte ich verschiedene Artikel in einer Liste halten. Konkret handelt es sich dabei um Stühle und Tische mit unterschiedlichen Merkmalen. Die Stühle und Tische haben eine Artikelnummer und einen Preis. Ein Stuhl hat zudem eine Farbe und ein Tische besteht aus einem bestimmten Material. Dieses Model ist natürlich stark vereinfacht, reicht aber für meine Zwecke vollkommen aus. Diese Aufgabenstellung kann natürlich auch mit Vererbung gelöst werden, indem eine Oberklasse Artikel erstellt wird und Stuhl und Tisch von dieser Klasse erben. Ein konkretes Unterscheidungsmerkmal läßt sich jedoch schwer abgrenzen. Beispielsweise könnte die Produktpalette auch um Pflegemittel erweitert werden, die für einen bestimmten Stuhl oder Tisch vorgesehen sind. Diese könnten zum Beispiel auch in unterschiedlichen Mengen bereitgestellt werden. Die Oberklasse müßte all diese Ausprägungen abbilden können.

Leichter geht es in diesem Fall mit Delegation.

Wir brauchen zuerst ein Interface, dass die gemeinsame Funktionalität definiert:

/**
 * ArticleTypeIF
 */
interface ArticleTypeIF
{
    public function getArticleNumber();
    public function getPrice();
}
//------------------------------------------------------------

Dannach können wir bereits mit der Implementierung unserer Artikel beginnen, die das soeben erstellte Interface implementieren. Wir beginnen mit dem Stuhl:

/**
 * Chair
 */
class Chair implements ArticleTypeIF
{
    public function __construct($articleNo_, $price_, $color_)
    {
        $this->articleNo=$articleNo_;
        $this->price=$price_;
        $this->color=$color_;
    }
    public function getArticleNumber() {return $this->articleNo;}
    public function getPrice() {return $this->price;}
    public function __toString()
    {
        return 'Chair "'. $this->getArticleNumber(). '", Price '.
                $this->getPrice(). ', Color '. $this->color;
    }
}
//------------------------------------------------------------

Wir sehen hier, dass im Konstruktor die Klassen-Variablen dynamisch angelegt werden. Das ist in PHP möglich und erspart mir Schreibarbeit. Die Methoden des Interface geben natürlich entsprechende Werte zurück. Außerdem fügen wir noch eine toString-Methode hinzu, die uns die Klasse sinnvoll ausgeben kann. Die anderen zwei Artikelvarianten sind ebenfalls schnell implementiert:

/**
 * Table
 */
class Table implements ArticleTypeIF
{
    public function __construct($articleNo_, $price_, $material_)
    {
        $this->articleNo=$articleNo_;
        $this->price=$price_;
        $this->material=$material_;
    }
    public function getArticleNumber() {return $this->articleNo;}
    public function getPrice() {return $this->price;}
    public function __toString()
    {
        return 'Table "'. $this->getArticleNumber(). '", Price '.
               $this->getPrice(). ', Material '. $this->material;
    }
}
//------------------------------------------------------------
 
/**
* CareFix
*/
class CareFix implements ArticleTypeIF
{
    public function __construct($articleNo_, $price_, $packSize_,
                                ArticleTypeIF $for_)
    {
        $this->articleNo=$articleNo_;
        $this->price=$price_;
        $this->packSize=$packSize_;
        $this->for=$for_;
    }
    public function getArticleNumber() {
        return $this->articleNo;
    }
    public function getPrice() {
        return $this->price;
    }
    public function __toString()
    {
        return 'CareFix "'. $this->getArticleNumber(). '", Price '.
               $this->getPrice(). ', Pack size '. $this->packSize.
               ', for --> '. $this->for;
    }
}
//------------------------------------------------------------

Wir sehen an unserer Klasse ClearFix, dass wir die Problemstellung mit der Abhängigkeit zu anderen Produkten ohne Probleme als zusätzlichen Parameter im Konstruktor lösen können. Nun brauchen wir nur noch eine Liste, die unsere Artikel aufnehmen kann. Ich mache es mir leicht und leite diesmal von ArrayObject ab:

/**
 * Articles
 */
class Articles extends ArrayObject
{
    public function add(ArticleTypeIF $article_) {
      $this->offsetSet($article_->getArticleNumber(), $article_);
    }
    public function get($articleNumber_) {
      return $this->offsetGet($articleNumber_);
    }
}
 
//------------------------------------------------------------

Man könnte das auch schlicht mit einem normalen array implementieren, dann würde man in diesem Beispiel komplett ohne Vererbung auskommen. Zu guter Letzt möchte ich nun die Verwendung dieser Klassen demonstrieren:

// Example
$articles=new Articles();
$articles->add(new Chair(100, 149.99, 'green'));
$articles->add(new Chair(101, 149.99, 'red'));
$articles->add(new Chair(102, 149.99, 'blue'));
$articles->add(new Table(200, 499.00, 'wood'));
$articles->add(new Table(201, 1499.00, 'steel'));
$articles->add(new CareFix(300, 9.99, 'medium', $articles->get(200)));
$articles->add(new CareFix(301, 19.99, 'large', $articles->get(100)));
//------------------------------------------------------------

Die Ausgabe dazu sieht folgendermaßen aus:

  • Chair „100“, Price 149.99, Color green
  • Chair „101“, Price 149.99, Color red
  • Chair „102“, Price 149.99, Color blue
  • Table „200“, Price 499, Material wood
  • Table „201“, Price 1499, Material steel
  • CareFix „300“, Price 9.99, Pack size medium, for –> Table „200“, Price 499, Material wood
  • CareFix „301“, Price 19.99, Pack size large, for –> Chair „100“, Price 149.99, Color green

Kommentar verfassen