• Abstract Singleton

    von am 15. September 2008

    Ich weiß, ich bin heute ziemlich spät dran, das liegt aber nicht daran, dass ich keine Lust hatte, aber mein Tag war wirklich anstrengend. Deswegen gibt es heute auch nur einen kurzen Beitrag von mir.
    Ich habe ja schon vor ein paar Tagen über das Singleton Pattern geredet. Dank PHP 5.3 ist es jetzt kein Problem mehr schon alles für dieses Entwurfsmuster in einer abstrakten Elternklasse zu implementieren.

    abstract class aSingleton
    {
       private static $instance;
    
       public static function getInstance( )
       {
          $className = get_called_class( );
          if ( ! isset(self::$instance ) )
          {
             self::$instance = new $className( );
          }
          return self::$instance;
       }
    
       protected function __construct( )
       {
       }
    
       final private function __clone( )
       {
       }
    }

    Prinzipiell habe ich ja bereits bei meinem Artikel über die Singleton alles erzählt, was nötig ist, deswegen werde ich jetzt nur noch anmerken, wie man das ganze verwenden sollte.

    Class Database extends aSingleton
    {
    }
    
    $db = Database::getInstance( );

    Vielleicht kann man ja noch als Schlusswort hinzufügen, dass es nur funktioniert solange man einen einheitlichen Konstruktor für alle Singletons benutzt, da man in der abstrakten Klasse nicht unterscheiden kann.

    Sorry wenn dieser Artikel ein wenig hektisch wird, aber man hat leider nicht immer genug Zeit.

    Nils Langner

    Auch wenn Ihr es mir nicht glauben werdet, aber ich habe nichts gegen PHP. Ich rege mich einfach nur gerne auf. Ok so schlimm ist es auch nicht. Eigentlich wollte ich schon immer einen Blog haben und da ...

    Zum Profil von Nils Langner

    6 Kommentare »


    • Frank Kleine
      am 15. September 2008 um 20:26 Uhr

      Apropos nicht genug Zeit: das müsste __clone() sein, nicht clone(). Und die Eigenschaft $instance sollte als static deklariert sein. :-)

      Ich kann zwar den Hintergrund verstehen, nämlich dass man nicht immer wieder ein Singleton neu implementieren möchte, finden den Ansatz aber trotzdem nicht gut. Zum einen denke ich ist es wenig sinnvoll, etwas vom Typ Singleton zu haben (wobei sollte das helfen?), zum anderen bekommt man damit eine Hierarchie in alle Singletons, die eine Abhängigkeit darstellt welche so nicht gegeben ist.

      IMHO wäre es sinnvoller zu warten bis Traits verfügbar sind, und das dann darüber zu lösen.


    • Nils Langner
      am 16. September 2008 um 06:51 Uhr

      Morgen Frank, da muss ich dir absolut recht geben, bei allen deinen Punkten. Die beiden Fehler im Code habe ich behoben, dass ich nicht immer ein Freund des Singleton Musters bin, habe ich ja auch schon geschrieben.
      Die Traits Idee werde ich auch jeden Fall noch mal aufgreifen, finde ich ziemlich “sexy”.


    • Manuel Grundner
      am 16. Mai 2009 um 12:39 Uhr

      Hallo Leute!
      Ich beobachte diesen Blog schon längere Zeit und ich muss sagen hier sitzen wirklich fähige Autoren dahinter!

      Nun zu meinem Kommentar:
      Ich entwickle schon seit anbeginn von PHP5.3 ein recht großes Framework für PHP5.3

      Meine Singleton Implementation sieht ein wenig anders aus:

      /**
       * Abstract Singleton Class
       */
      abstract class Singleton
      {
      	/**
      	 * The name of the class
      	 *
      	 * @var string classname
      	 */
      	public static $classname = __CLASS__;
      
      	/**
      	 * The instance of the class
      	 *
      	 * @var Singleton
      	 */
      	protected static $instance;
      
      	/**
      	 * Returns the instance
      	 *
      	 * @return Singleton
      	 */
      	public static function getInstance()
      	{
      		if(!is_null(static::$instance))
      			static::$instance = new static::$classname();
      
      		return static::$instance;
      	}
      
      	/**
      	 * Constructor. Singleton.
      	 */
      	protected function __construct()
      	{
      
      	}
      
      	/**
      	 * Cloning is not allowed
      	 */
      	final private function __clone()
      	{
      
      	}
      }
      
      /**
       * A Counter
       */
      class Counter extends Singleton
      {
      	/**
      	 * The name of the class
      	 *
      	 * @var string
      	 */
      	public static $classname = __CLASS__;
      
      	/**
      	 * The instance of the class
      	 *
      	 * @var Singleton
      	 */
      	protected static $instance;
      
      	/**
      	 * Constructor. Singleton.
      	 */
      	protected function __construct()
      	{
      		//Do some stuff here
      	}
      
      	/**
      	 * Returns the count
      	 *
      	 * @return int
      	 */
      	public function count()
      	{
      		//Or Here
      	}
      }

      Nutzt halt LSB.
      Das schöne is, damit kann man “schöne” Enums in PHP realisieren.
      Nachteil ist natürlich das man den Klassennamen und die Instanzvariable in jeder abgeleiteten Klasse hinterlegen muss.

      Bei mir ist das mit dem Klassennamen nicht so das Problem, weil diese Variable bei mir ohnehin in jeder Klasse vorhanden ist.

      Grüße aus Österreich
      Manuel


    • Nils Langner
      am 16. Mai 2009 um 15:52 Uhr

      @manuel: Erstmal Danke für das Kompliment. Kannst du mal ein Beispiel zu dem ENUM Vorteil bringen? Das ist mir irgendwie noch nicht so ganz klar. Denn ohne das Bsp. finde ich unsere Implementierung schöner, da wir keine protected Variable mitschleppen müssen.


    • Manuel Grundner
      am 16. Mai 2009 um 17:33 Uhr

      ja, da hast du vollkommen recht, nur mit singletons muss/soll man ohnehin sparsam umgehen, nur beim enum gehts nicht ohne.

      Via Reflection ist sogar ein iterierbarer Enum machbar (Delphi like)

      namespace HazAClass\type;
      
      use HazAClass\type\exception\IteratorMethodWasTriedToUsedAsEnumValue;
      
      /**
       * Base-Class for Typesave Enum
       */
      abstract class Enum implements \IteratorAggregate, \Countable
      {
      	/**
      	 * Der Name der Klasse
      	 *
      	 * @var string classname
      	 */
      	public static $classname = __CLASS__;
      
      	/**
      	 * Value of enum
      	 *
      	 * @var string
      	 */
      	private $value;
      
      	/**
      	 * The Methods/Values in the Enum
      	 *
      	 * @var array[Enum]
      	 */
      	private static $methods;
      
      	/**
      	 * Sets value of enum
      	 *
      	 * @param string $value
      	 */
      	final protected function __construct($value)
      	{
      		$this->value = $value;
      	}
      
      	/**
      	 * Returns an 'null'-enum used to iterate a enum.
      	 *
      	 * Note: if you try to use this enum-value as an 'real' enum
      	 * an exception is thrown in the getValue or __toString method!
      	 *
      	 * @return Enum
      	 */
      	final public static function iterator()
      	{
      		return new static::$classname(null);
      	}
      
      	/**
      	 * Returns the current value of the enum
      	 *
      	 * @see iterator
      	 * @throws IteratorMethodWasTriedToUsedAsEnumValue
      	 *
      	 * @return string
      	 */
      	final public function getValue()
      	{
      		if(is_null($this->value))
      			throw new IteratorMethodWasTriedToUsedAsEnumValue('The iterator-method can only be used to iterate, not as an value');
      		return $this->value;
      	}
      
      	/**
      	 * Casts Enum to String
      	 *
      	 * @return string
      	 */
      	final public function __toString()
      	{
      		if(is_null($this->getValue()))
      			return '';
      		return $this->getValue();
      	}
      
      	/**
      	 * @see Countable::count()
      	 *
      	 */
      	public function count()
      	{
      		return count(self::getMethods());
      	}
      
      	/**
      	 * Returns the values (methods) in the enum
      	 *
      	 * @return array[methods]
      	 */
      	private static function getMethods()
      	{
      		if(is_null(self::$methods))
      		{
      			$ref = new \ReflectionClass(static::$classname);
      
      			foreach($ref->getMethods(\ReflectionMethod::IS_PUBLIC) as $method)
      			{
      				if($method->isStatic())
      				{
      					$name = $method->getName();
      					if($name != 'iterator')
      					{
      						$classname = static::$classname;
      						self::$methods[$name] = $classname::$name();
      					}
      				}
      			}
      		}
      		return self::$methods;
      	}
      
      	/**
      	 * @see IteratorAggregate::getIterator()
      	 */
      	public function getIterator()
      	{
      		return new \ArrayIterator(self::getMethods());
      	}
      
      }

      das is halt schon a recht komplexe implementierung, aber dann kann man folgendes machen:

      
      namespace HazAClass\output;
      
      use HazAClass\type\Enum;
      
      /**
       * Classdescription
       */
      class OutputTypes extends Enum
      {
      	/**
      	 * The Name of the class
      	 *
      	 * @var string $classname
      	 */
      	public static $classname = __CLASS__;
      
      	/**
      	 * xhtml
      	 *
      	 * @return OutputTypes
      	 */
      	public static function xhtml()
      	{
      		return new self::$classname('xhtml');
      	}
      
      	/**
      	 * xml
      	 *
      	 * @return OutputTypes
      	 */
      	public static function xml()
      	{
      		return new self::$classname('xml');
      	}
      }

      und dann:

      foreach (OutputTypes::getIterator() as $outputType)
      	echo $outputType;

      Das schöne ist, das die wirklich typensicher sind

      also

      public function render(OutputTypes $type)
      {
      //Do Something here
      }

      Hoffe das ist klar verständlich soweit, sonst einfach rühren ^^


    • Manuel Grundner
      am 16. Mai 2009 um 17:38 Uhr

      Ich seh grad das is was aus nem alten commit hochgekommen..

      Die iterator methode kann man streichen, dann spart man sich die abfrage im getMethods, weil im getIterator eh kein $this aufgerufen wird (da wären wir wieder bei einem älteren eintrag von euch ^^, in dem fall sogar sinnvoll einsetzbar)

      es ginge dann sogar:
      foreach (OutputTypes::xhtml() as $outputType)
      echo $outputType;

      aber das ist schwer zu lesen, und irreführend.

    RSS Feed für Kommentare zu diesem Artikel. TrackBack URL

    Hinterlasse einen Kommentar

    Werbung
    PHP Magazin
    Ausgabe 02/2010

    Dieses Mal mit Artikeln zu den Themen OpenSocial und Apache Shindig, Graphentheorie, Smarty3

    t3n
    Ausgabe 19

    Social Media (R)evolution. Weitere Themen sind noSQL, Crowdsourcing ...

    PHP Journal
    Ausgabe 2/2010

    PHP & Windows optimal nutzen, die besten PHP-CMS im Überblick, Google-API mit Zend Framework nutzen.

    Wir wurden schon öfters gefragt, ob man uns nicht irgendwie unterstützen kann. Die Antwort war immer einfach: Klar! Am einfachsten ist es eure nächsten Einkäufe bei Amazon über unsere Link abzuwickeln. Damit würdet ihr uns schon sehr helfen. Über Co-Autoren freuen wir uns aber noch mehr.