Singletons in PHP - Why they are bad and how you can eliminate them from your applications

  • 35,636 views
Uploaded on

While Singletons have become a Pattern-Non-Grata over the years, you still find it surprisingly often in PHP applications and frameworks. This talk will explain what the Singleton pattern is, how it …

While Singletons have become a Pattern-Non-Grata over the years, you still find it surprisingly often in PHP applications and frameworks. This talk will explain what the Singleton pattern is, how it works in PHP and why you should avoid it in your application.

More in: Technology , Education
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
  • I would really enjoy audio with this, or text that went along with it. Great stuff - thank you!

    In my current project, I am trying to re-use a huge library of PHP objects and functions that I built about 5 years ago. 1 year of full-time work at the time.

    I built that code and on what I believed to be the best practices for design that I could find, with extensibility and modular re-use in mind. Many many hours of work invested there... no need to re-do all of that.

    Engineering is an investment. Thank goodness I had not chosen to do the easy thing, which would have been php4-style Globals. Instead, i used OOP. I did implement a singleton and a registry pattern, which are now making it more difficult to test. It seems to be wasting my time today, and my time is also more valuable today than it was then.

    Testing is also an investment. I wish I remember where I just saw some say: 'it's like going to the gym'.

    Singletons are like fast food, then. If you are building a fast food website, and if learning to use these tools properly are not important, use a singleton and throw it away later. If there is any chance the code can be used again, then think about it for a few minutes, consider applying a different solution, and move forward. Who knows, maybe the alternatives we come up with will solve other problems.

    These perspectives, (authors and commentors) are very helpful, thank you all for your input. It is a fascinating debate that is very helpful when trying to learn.


    PS: I am a student, and I researched this today. Here are a few things I read this evening:

    The related presentation should appeared for me in the sidebar to the right. It has been viewed over 14000 times: http://www.slideshare.net/weppos/design-patterns-in-php-phpcon-italia

    http://www.slideshare.net/alexmiller/design-patterns-reconsidered

    It's not just PHP: here is one that argues against singleton in java: http://www.slideshare.net/alexmiller/design-patterns-reconsidered

    and of course:
    http://stackoverflow.com/questions/137975/what-is-so-bad-about-singletons

    This is great:
    https://sites.google.com/site/steveyegge2/singleton-considered-stupid

    Anyone else have other suggestions?
    Are you sure you want to
    Your message goes here
  • Ok, singletons are bad, what else is bad? Will you stop it finaly? Just stop! There are billions of ways how to aproach each programming problem, and for each single way, there's someone on the internet saying that this way is bad and should be avoided, because of his own reasons. This never helps anyone. People, who are experienced enough, in that particular topic, they don't give a damn about it and they will keep writing code that works for them. And people, who are not experienced enough get only confused, they don't understand your reasons in it's complexity, because they never encountered problems you're talking about. And if they give in to your dogmatic suggestions, they won't get any benefits, but it will only make them write even more obscure and error-prone code, then they would, if they did it their own way. Now, I will close this window and I'm gonna write singletons. That's because they work fine in what I do. My project is no international CMS, no bloody operation system, it's a small webapp for gods sake, it has handfull of classes. No reason to over-engineer. Dependency-injection doesn't solve my problem here, I have no reason not to use pattern, that exactly fits my needs. And if it breaks, hell what? I can handle that, it won't be first failure of my code. Every code breaks sometimes.
    Are you sure you want to
    Your message goes here
  • 'singletons are bad' is as stupid as saying 'globals variables are bad'. just because something MAY be improper if abused by lack of experience, does not mean that they are not useful, or they are bad or dangerous. it is as idiotic as saying that 'post requests are bad', because there are many inexperienced programmers who forget to sanitize post input. extreme elitism does not serve programming language communities. it should be dropped. in technical side, just because singleton pattern may not be necessary in the types of applications you used to code, does NOT mean that they cannot be used to accomplish things that your current application style cannot provide for. but it is natural. when something is relatively new, there is a lot of resistance and badmouthing about it. thats just conservatism. nothing else. i remember 7-8 years ago, php was also being badmouthed like this, being called 'garbage', 'psuedo-language', and 'dangerous' and whatnot. look what happened.
    Are you sure you want to
    Your message goes here
No Downloads

Views

Total Views
35,636
On Slideshare
0
From Embeds
0
Number of Embeds
3

Actions

Shares
Downloads
116
Comments
3
Likes
26

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. Singletons inWhy they are bad and how you can eliminate them from your applications
  • 2. Gordon Oheim @go_oh
  • 3. a.k.a.The GoF Book
  • 4. Singleton
  • 5. Creational Pattern„control when and how objects are created“
  • 6. “Ensure a class has only oneinstance, and provide a global access point to it.”
  • 7. Singleton Singleton- instance : Singleton- instance : Singleton++ getInstance() getInstance() : : Singleton Singleton-- __construct() __construct() : : void void-- __clone() __clone() : : void void-- __wakeup() __wakeup() : : void void
  • 8. class Singleton… public static function getInstance() { if (!isset(self::$instance)) { self::$instance = new self; } return self::$instance; }}
  • 9. abstract class Singleton… public static function getInstance() { return isset(static::$instance) ? static::$instance : static::$instance = new static; } final private function __construct() { static::init(); } protected function init() {}}class A extends Singleton { protected static $instance;}
  • 10. trait Singleton… protected static $instance; final public static function getInstance() { return isset(static::$instance) ? static::$instance : static::$instance = new static; } final private function __construct() { static::init(); } protected function init() {}}class A { use Singleton;}
  • 11. „Nobody should need a mechanism to make it as easy as pie to clutter the code base with singletons“ - Sebastian Bergmann
  • 12. Testing Singletons
  • 13. „In my stilted view of the universe anything thatimpedes testing is something to be avoided. There are those who dont agree with this view, but Ill pit my defect rates against theirs any time.“ - Robert „Uncle Bob“ C. Martin
  • 14. class TableDataGateway… public function __construct() { $this->db = Db::getInstance(); } public insert(array $data) { $this->db->sql(INSERT …, $data); }}
  • 15. class TableDataGatewayTest extends PHPUnit… /** * @dataProvider provideInsertData */ public function testInsertCallsDbWithData($data) { $gateway = new TableDataGateway; $gateway->insert($data); $this->assertSame( $data, $gateway->findById($data[id]) ); }}
  • 16. throws LogicException:No key found for pdo in Registry
  • 17. class Db extends Singleton… protected function init() { $this->logger = Logger::getInstance(); $this->pdo = new PDO(Registry::get(pdo)); } public sql($query, $data) { $this->logger->log(printf( Executed %s with %s, $query, implode(,, $data) )); $sth = $this->pdo->prepare($query); return $sth->execute($data); }}
  • 18. Singletons are pathological liars - Miško Hevery
  • 19. class TableDataGatewayTest extends PHPUnit… /** * @dataProvider provideInsertData */ public function testInsertCallsDbWithData($data) { Registry::set(pdo, array(…)); Registry::set(log, /dev/null); $gateway = new TableDataGateway; $gateway->insert($data); $this->assertSame( $data, $gateway->findById($data[id]) ); }}
  • 20. Your tests are not isolated. You still need a real database.No easy way to mock the Db Singleton.
  • 21. „The real problem with Singletons is that theygive you such a good excuse not to think carefully about the appropriate visibility of an object.“ - Kent Beck
  • 22. Hard to change code manifests itself in a cascade ofsubsequent changes in dependent code
  • 23. Fragile codebreaks in many places when you change just one place
  • 24. Non-reusable code is code that you cannot reuse inanother project because it contains too much extra baggage
  • 25. S.O.L.I.D.
  • 26. Single Responsibility PrincipleA class should have one, and only one, reason to change.
  • 27. Open Closed PrincipleYou should be able to extend a classes behavior, without modifying it.
  • 28. Liskov Substitution PrincipleDerived classes must be substitutable for their base classes.
  • 29. Interface Segregation PrincipleMake fine grained interfaces that are client specific.
  • 30. Dependency Inversion PrincipleDepend on abstractions, not on concretions.
  • 31. Solid Singleton?
  • 32. SRP Violation Creation Logic + Business Logic = 2 reasons to changeMitigated by using Abstract Singletons But responsibilities are still strongly coupled through inheritance
  • 33. OCP ViolationIn PHP < 5.2 Singletons are closed for extending Boilerplate code has to be removed when no longer needed
  • 34. DIP Violation Global access breaks encapsulation Hides dependenciesAdds concrete dependencies into Clients Signature lies
  • 35. Singletons in the Wild
  • 36. Config FooController Response FrontControllerThatService Request Cache Database Application BarController Registry Logger ThisService Bootstrap Acl
  • 37. Config FooController Response FrontControllerThatService Request Cache Database Application BarController Registry Logger ThisService Bootstrap Acl
  • 38. Recap: “Ensure a class has only oneinstance, and provide a global access point to it.”
  • 39. „So much of the Singleton pattern is about coaxing language protection mechanisms intoprotecting this one aspect: singularity. I guess itis important, but it seems to have grown out of proportion.“ - Ward Cunningham
  • 40. You do not need to ensure singularitywhen you are going to instantiate the object only once anyway.
  • 41. You do not need to provide a GlobalAccess Point when you can inject the object.
  • 42. class TableDataGateway… public function __construct() { $this->db = Db::getInstance(); }}class Db extends Singleton… protected function init() { $this->logger = Logger::getInstance(); $this->pdo = new PDO(Registry::get(pdo)); }}
  • 43. class TableDataGateway… public function __construct(Db $db) { $this->db = $db; }}class Db… public function __construct(PDO $pdo, Log $logger) { $this->logger = $logger; $this->pdo = $pdo; }}
  • 44. But then I have to push dependencies all the way through my object graph?!
  • 45. Recap: Creational Pattern„control when and how objects are created“
  • 46. Use Builders and Factories to create Collaborator Graphs
  • 47. class UserControllerBuilder{ public function build($config) { $log = new Log($config[log]); $pdo = new PDO($config[…]); $db = new Db($pdo, $log); $tdg = new TableDataGateway($db); return new UserController($tdg); }}
  • 48. class UserControllerBuilder{ public function build($config) { $log = new Log($config[log]); $pdo = new PDO($config[…]); ??? $db = new Db($pdo, $log); $tdg = new TableDataGateway($db); return new UserController($tdg); }}
  • 49. class UserControllerBuilder{ public function build($config) { $db = new LogDecorator( new PDO($config[…]); new Log($config[log]); ); $tdg = new TableDataGateway($db); return new UserController($tdg); }}
  • 50. // index.phpinclude /path/to/autoload.php;$config = new Config(/path/to/config.ini);$router = new Router( array( /user/{id} => function() use ($config) { $builder = new UserControllerBuilder; return $builder->build($config); } ));$router->route( new Request($_GET, $_POST, $_SERVER), new Response);
  • 51. „Singletons arent necessarywhen you can design or redesign to avoid using them.“ - Joshua Kerievsky
  • 52. „Im in favor of dropping Singleton. Its use is almost always a design smell“ - Erich Gamma