Sunday, February 28, 2010

Singleton is bad !!

An interesting suggestion emerged from some speakers (1 and 2) of the last PHP UK conf is: do not use the Singleton design pattern !!


At the beginning it made me a little astonished as it's one of the most widely used design patterns, in addition ZF and lots of other projects use it. However, after a while I agreed the idea. Singleton actually had created some problems when I had to do some class testing and refactoring in some PHP code.


Why is it not a good practice ? Singleton is appropriate when dealing with threads (JAVA, C#), but as a matter of fact in PHP applications it's used just as a global constant and it creates dependency injection. That's a bad practice as it couples the components/classes and usually makes difficult unit testing, refactoring and structural changes.

class A {
public function f(){
$c1 = c1::GetInstance(); #bad !!
# use $c1
$c2 = c2::GetInstance(); #bad !!
# use $c2
}
}

What's the alternative ? Simply, instead of retrieving the instance in the method of the class, retrieve it from an internal class field after it has been set by the constructor.

class A {
private $c1;
private $c2;

public function __constructor(C1 $c1, C2 $c2){# alternative, use Interface name
$this->c1 = $c1;
$this->c2 = $c2;
}

public function f(){
# use $this->c1;
# use $this->c2;
}
}

What about static methods ? In a static context, $this is not available.

One first solution occurs in my mind it's passing the instances to all the the static methods. But if there are lots of methods and external dependencies, another solution might be using another static method to get all the instances needed, in order to move all the external dependencies to one method and make easier managing the class. A kind of internal 'Registry' design pattern.

class A {
const INST_c2 = 1;
const INST_C2 = 2;

public static function getInstance($instance){
if ($instance==self::INST_C1){
return c1::GetInstance();
} else if ($instance==self::INST_C2){
return c2::GetInstance();
}
}

public static function f(){
$c1 = self::GetInstance(self::INST_DB);
# use $c1;

$c2 = self::GetInstance(self::INST_C2);
# use $c2;
}

}

Of course there are other solutions, more or less convient depending on the needs.
Any other ideas ?

No comments:

Post a Comment

 

PHP and tips|PHP