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

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

description

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.

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

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

Singletons inSingletons in Why they are bad and how you can eliminate

them from your applications

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

Gordon OheimGordon Oheim@go_oh

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

a.k.a.The GoF Book

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

Singleton

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

Creational Pattern„control when and how objects are

created“

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

“Ensure a class has only one instance, and provide a global

access point to it.”

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

Singleton- instance : Singleton+ getInstance() : Singleton- __construct() : void- __clone() : void- __wakeup() : void

Singleton- instance : Singleton+ getInstance() : Singleton- __construct() : void- __clone() : void- __wakeup() : void

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

class Singleton… public static function getInstance() { if (!isset(self::$instance)) { self::$instance = new self; } return self::$instance; }}

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

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;}

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

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;}

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

„Nobody should need a mechanism to make it as easy as pie to clutter the code base

with singletons“

- Sebastian Bergmann

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

Testing Singletons

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

„In my stilted view of the universe anything that impedes testing is something to be avoided. There are those who don't agree with this view, but I'll

pit my defect rates against theirs any time.“

- Robert „Uncle Bob“ C. Martin

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

class TableDataGateway… public function __construct() { $this->db = Db::getInstance(); } public insert(array $data) { $this->db->sql('INSERT …', $data); }}

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

class TableDataGatewayTest extends PHPUnit… /**

* @dataProvider provideInsertData */

public function testInsertCallsDbWithData($data) { $gateway = new TableDataGateway; $gateway->insert($data); $this->assertSame( $data, $gateway->findById($data['id']) ); }}

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

throws LogicException: No key found for 'pdo' in Registry

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

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); } }

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

Singletons are pathological liars- Miško Hevery

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

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']) ); }}

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

Your tests are not isolated.You still need a real database.

No easy way to mock the Db Singleton.

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

„The real problem with Singletons is that they give you such a good excuse not to think carefully

about the appropriate visibility of an object.“

- Kent Beck

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

Hard to change code manifests itself in a cascade of

subsequent changes in dependent code

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

Fragile code breaks in many places when you

change just one place

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

Non-reusable code is code that you cannot reuse in

another project because it contains too much extra baggage

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

S.O.L.I.D.

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

Single Responsibility Principle

A class should have one, and only one, reason to change.

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

Open Closed Principle

You should be able to extend a classes behavior, without modifying it.

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

Liskov Substitution Principle

Derived classes must be substitutable for their base classes.

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

Interface Segregation Principle

Make fine grained interfaces that are client specific.

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

Dependency Inversion PrincipleDepend on abstractions, not on concretions.

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

Solid Singleton?

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

SRP Violation

Creation Logic + Business Logic= 2 reasons to change

Mitigated by using Abstract Singletons

But responsibilities are still strongly coupled through inheritance

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

OCP Violation

In PHP < 5.2 Singletons are closed for extending

Boilerplate code has to be removed when no longer needed

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

DIP Violation

Global access breaks encapsulation

Hides dependencies

Adds concrete dependencies into Clients

Signature lies

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

Singletons in the Wild

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

Database

FrontController

Logger

Cache

Registry

Request

ResponseConfig

Bootstrap

Application

Acl

FooController

BarController

ThisService

ThatService

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

Database

FrontController

Logger

Cache

Registry

Request

ResponseConfig

Bootstrap

Application

Acl

FooController

BarController

ThisService

ThatService

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

Recap:“Ensure a class has only one

instance, and provide a global access point to it.”

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

„So much of the Singleton pattern is about coaxing language protection mechanisms into

protecting this one aspect: singularity. I guess it is important, but it seems to have grown out of

proportion.“

- Ward Cunningham

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

You do not need to ensure singularity when you are going to instantiate the

object only once anyway.

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

You do not need to provide a Global Access Point when you can inject the

object.

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

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')); } }

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

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; } }

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

But then I have to push dependencies all the way through my object graph?!

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

Recap: Creational Pattern

„control when and how objects are created“

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

Use Builders and Factories to create Collaborator Graphs

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

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); }}

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

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); }}

???

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

class UserControllerBuilder { public function build($config) { $db = new LogDecorator( new PDO($config['…']); new Log($config['log']); ); $tdg = new TableDataGateway($db); return new UserController($tdg); }}

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

// 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);

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

„Singletons aren't necessary when you can design or redesign

to avoid using them.“

- Joshua Kerievsky

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

„I'm in favor of dropping Singleton. Its use is almost always a design

smell“

- Erich Gamma