note 58324 added to language.oop5.patterns
| From: | peetersdiet at gmail dot com | Date: | Mon, 31 Oct 2005 15:57:35 +0000 |
| Subject: | note 58324 added to language.oop5.patterns | ||
| Groups: | php.notes | ||
| Request: | Send a blank email to php-notes+get-97883@lists.php.net to get a copy of this message | ||
@ steffen at morkland dot com:
Interesting problem and u are right about always having to put the non-public constructor and public
singleton-method in the classes that need to be singletons. U are also right about an interface not
being able to declare a constructor private or protected.
Why not store all singletons in a static variable of a common parent class and make
singleton-capability available to all childclasses? We can accomplish this by adding a hybrid
singletonFactory()-method in that parent class and make the constructor of the childclass protected
to enforce the singleton-pattern. At this moment all childclasses can be a singleton! Not to robust.
U can have childclasses implement the Singleton-interface as specified in ur post so classes can be
tested on implementing this interface. If u always implement the interface on ur singleton-classes u
can add a test to the singletonFactory()-method to check whether the class u are about to
instantiate does implement the Singleton-interface. Together with the enforced prototyping of the
singleton()-method this provides a more foolproof usage of the singleton-pattern.
The singleton code is implemented in the parent so no rewriting of the code is necessary, thus
avoiding programmer mistakes in the childclasses and keeping the code cleaner to the eye. The only
methods the childclass needs are a protected constructor to avoid direct instantiation and a
wrapping singleton()-method.
<?php
// As stated by steffen we can't restrict visibility of the constructor in an interface
interface Singleton {
public static function singleton();
}
// Parent class:
// U can define these methods at the highest level so the singleton-functionality
// is available to all objects, but it works at all levels of an object hierarchy.
class ParentClass {
private static $childSingletons = array(); // store singletons for each childclass
public static function singletonFactory($class='')
{
if (!isset(self::$childSingletons[$class])) { // singleton not loaded?
if (include_once($class.'.php')) { // classfile included?
$thisClass = __CLASS__;
$object = new $class;
if ($object instanceof $thisClass) { // class is childclass? (so we can access the
protected constructor)
$classImplements = class_implements($object);
if (isset($classImplements['Singleton'])) { // class implements
Singleton?
self::$childSingletons[$class] = new $class;
} else {
throw new Exception ('Class \''.$class.'\' does not
implement interface Singleton');
}
} else {
throw new Exception ('Unknown class
\''.$class.'\'');
}
} else {
throw new Exception ('Class \''.$class.'\' not
found');
}
}
return self::$childSingletons[$class];
}
}
// Child class:
// 1. Define the constructor as protected.
// 2. Implement interface Singleton ...
// ... to enforce prototype of singleton()
// ... to be able to dynamically identify the class as a singleton.
// 3. Define the singleton()-method acting as a wrapper for the parent's
singletonFactory()-method.
class ChildClass extends ParentClass implements Singleton{
protected function __construct() {}
public static function singleton() {
return parent::singletonFactory(__CLASS__);
}
}
// public usage
$singleton = ChildClass::singleton();
?>
If u prefer more loose programming u can leave the Singleton interface out of the picture
alltogether and create your childsingletons directly with the singletonFactory()-method. This
approach only needs the protected constructor(), no singleton()-method. I know this approach allows
all the childclasses to be treated as singletons. In my opinion this is not a problem, but the lack
of robustness and absence of dynamic testability is. ;)
----
Manual Page -- http://www.php.net/manual/en/language.oop5.patterns.php
Edit -- http://master.php.net/manage/user-notes.php?action=edit+58324
Delete: added to the manual -- http://master.php.net/manage/user-notes.php?action=delete+58324&report=yes&reason=added+to+the+manual
Delete: bad code -- http://master.php.net/manage/user-notes.php?action=delete+58324&report=yes&reason=bad+code
Delete: spam -- http://master.php.net/manage/user-notes.php?action=delete+58324&report=yes&reason=spam
Delete: useless -- http://master.php.net/manage/user-notes.php?action=delete+58324&report=yes&reason=useless
Delete: non-english -- http://master.php.net/manage/user-notes.php?action=delete+58324&report=yes&reason=non-english
Delete: already in docs -- http://master.php.net/manage/user-notes.php?action=delete+58324&report=yes&reason=already+in+docs
Delete: other reasons -- http://master.php.net/manage/user-notes.php?action=delete+58324&report=yes
Reject -- http://master.php.net/manage/user-notes.php?action=reject+58324&report=yes
Search -- http://master.php.net/manage/user-notes.php