Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

PHP: 4 Design Patterns to Make Better Code

713 views

Published on

I present four design patterns that make your development easier and better. Design patterns are a fantastic way to make more readable code, as they make use of common ideas that many developers know and use. These patterns are tried and tested in the enterprise world.

The first one is dependency injection. This covers putting the variables that a class needs to function preferably inside a constructor.

The second one is the factory pattern. A factory moves the responsibility of instantiating an object to a third-party class.

The third one is dependency injection. This allows us to place a class' dependencies at one time, making it easy to come back and see what the class needs to survive.

Finally, we discuss the chain of responsibility. This allows complex operations to be handled by a chain of classes. Each class in the chain determines whether it is capable of handling the request and, if so, it returns the result.

Published in: Software
  • Be the first to comment

  • Be the first to like this

PHP: 4 Design Patterns to Make Better Code

  1. 1. WE FACE SIMILAR PROBLEMS. —OFTEN—
  2. 2. THOSE PROBLEMS HAVE COMMON SOLUTIONS.
  3. 3. “DESIGN PATTERNS” = COMMON SOLUTIONS TO COMMON PROBLEMS
  4. 4. DESIGN PATTERN BENEFITS • Presents a common solution to a problem. • Common solution = faster implementation • Recognizable to other developers. • Encourages more legible and maintainable code.
  5. 5. TERMINOLOGY • Interface: • An outline or a blueprint for classes. Cannot be instantiated. • Abstract class: • A class that can serve as an outline, while providing base functionality of its own. Can implement an interface. Cannot be instantiated.
  6. 6. TERMINOLOGY • Concrete class: • A “creatable” class. Can implement an interface or extend an abstract or concrete class. Can be instantiated.
  7. 7. DEPENDENCY INJECTION FACTORY STRATEGY CHAIN OF RESPONSIBILITY (COR)
  8. 8. class Session() { function __construct() { session_start(); } function getCreatedTime() { return $_SESSION[…]; } } public function getSession() { return new Session(); }
  9. 9. $app = new App(); echo $app->getSession()->getCreatedTime(); sleep(2); echo $app->getSession()->getCreatedTime();
  10. 10. SINGLETONS The way it used to be done: a global means to access one instance of an object
  11. 11. SOLUTION? • Use static class methods to track single instance of object: public static function getInstance() { if (!self::$instance) { self::$instance = new self(); } return self::$instance; }
  12. 12. SOLUTION? • Then, get the class like so: public function getSession() { return Session::getInstance(); }
  13. 13. Model needs session class Session Create instance and store it. self:: $_instance? Return instance. No Yes
  14. 14. SINGLETON’S PROBLEMS: • Hard links the session to its consuming class. • Makes testing more difficult. • Breaks Single Responsibility Principle, as the class manages its own instance.
  15. 15. DEPENDENCY INJECTION (DI) Providing classes the variables they need.
  16. 16. S Title Text
  17. 17. DI DESIGN new Model($session); $session = new Session();
  18. 18. DI DESIGN new Model($session); $session = new Session(); new Controller($session);
  19. 19. DI DESIGN new Model($session); $session = new Session(); new Controller($session); new DataStorage($session);
  20. 20. DI • We create the objects and then “inject” them into each class that depends on them. • Benefits: • Easier to follow Single Responsibility Principle. • Makes testing easier: we can inject mocks or stubs. • Good visibility into what each class needs.
  21. 21. protected $session; public function __construct(Session $session) { $this->session = $session; } public function saveToSession($value) { $this->session->save(‘time’, $value); } public function getFromSession() { return $this->session->get(‘time’); }
  22. 22. $session = new Session(); $app = new App($session); $app->saveToSession(time()); echo $app->getFromSession();
  23. 23. DI CONTAINERS Grouping variables into an object to be inserted into a class.
  24. 24. new App(); new Session(); DI Container class App { protected $session; function __construct(Container $container) { $this->session = $container->get(‘session’); } }
  25. 25. DI CONTAINERS • Pimple (pimple.sensiolabs.org) • Super simple container but is rather manual. • PHP-DI (http://php-di.org/) • More complex to setup but injection is automatic. • Similar to Symphony Dependency Injection component: 
 https://github.com/symfony/DependencyInjection
  26. 26. USE CASES: • Database connections • Sessions • Other objects in a request’s lifecycle.
  27. 27. DEPENDENCY INJECTION FACTORY STRATEGY CHAIN OF RESPONSIBILITY (COR)
  28. 28. FACTORY Allows centralization of code for class construction.
  29. 29. $controller = new HomeController( $request, $response, $templateEngine, $cookieJar, $properties, // … ); Too many properties to call frequently.
  30. 30. FACTORY • We manufacture very little of what we own. • Factories make most of our possessions for us. • Single-purposed: making things.
  31. 31. FACTORY DEFINED: • Moves the task of creating classes to a single-purposed class, the factory. • Factory provides a simple method to instantiate new class.
  32. 32. $application Constructor Arguments new Controller(…)
  33. 33. $application ControllerFactory::load() Constructor Arguments new Controller(…) “I need a Controller class.”
  34. 34. $application ControllerFactory::load() Constructor Arguments new Controller(…)
  35. 35. FACTORY: • Create a class as the factory. • All classes instantiated should extend a common type or implement a common interface. • Return the newly created class.
  36. 36. FACTORY: • Extra kudos: you can use a configuration file (Yaml/Xml) to control the creation of the classes.
  37. 37. public function loadProduct($id) { $data = $this->getProductDataFor($id); return ProductFactory::hydrate($data); }
  38. 38. class ProductFactory { public static function hydrate($data) { switch ($data[‘type’]) { case ‘simple’: $product = new SimpleProduct($data); break; case ‘configurable’: $product = new ConfigProduct($data); break; } return $product; } }
  39. 39. products: simple: name: Simple Product class: ModelProductSimple child_selector: name: Configurable class: ModelProductConfigurable
  40. 40. class ProductFactory { public static function hydrate($data, $config) { $type = $data[‘type’]; $className = $config[‘products’][$type][‘class’]; $product = new $className($data); return $product; } }
  41. 41. USE CASES: • Factory is a design-pattern for instantiating classes. • Offloading the creation of parameter-heavy constructors: • Dependency Injection • Determine what type of class to setup. • Gives means for 3rd-party developers to extend the functionality of your software.
  42. 42. DEPENDENCY INJECTION FACTORY STRATEGY CHAIN OF RESPONSIBILITY (COR)
  43. 43. STRATEGY Pluggable objects that have the same interface but can do different things.
  44. 44. PROBLEM: • How to separate changeable details? public function persist($data, $to) { switch($to) { case 'mysql': $connection = new PDO(...); $connection->exec(...); break; case 'salesforce': $connection = new HttpClient(...); // ... break; } }
  45. 45. PROBLEM: public function persist($data, $to) { switch($to) { case 'mysql': // … case 'salesforce': // … case 'redis': // … } }
  46. 46. PROBLEM: public function persist($data, $to) { switch($location) { case 'mysql': // … case 'salesforce': // … case 'redis': // … case 'file': // … } }
  47. 47. PROBLEM: public function persist($data, $to) { switch($location) { case 'mysql': // … case 'salesforce': // … case 'redis': // … case 'file': // … case 'ec2': // … } }
  48. 48. PROBLEM: public function persist($data, $to) { switch($location) { case 'mysql': // … case 'salesforce': // … case 'redis': // … case 'file': // … case 'ec2': // … case 'dropbox': // … } }
  49. 49. THIS CAN QUICKLY BECOME UNMANAGEABLE….
  50. 50. UPDATED: public function persist($data, $to) { switch($location) { case 'mysql': $this->persistToMysql($data); case 'salesforce': $this->persistToSalesForce($data); case 'redis': $this->persistToRedis($data); case 'file': $this->persistToFile($data); case 'ec2': $this->persistToEc2($data); case 'dropbox': $this->persistToDrop($data); } }
  51. 51. …BUT THERE’S A BETTER WAY.
  52. 52. FORD MUSTANG Engines: 5.2L V8 5.0L V8 2.3L EcoBoost
  53. 53. DRILL http://www.dewalt.com/tool-categories/Drills.aspx
  54. 54. STRATEGY • Parent class handles “what” happens. • Child class handles “how” it happens. • Parent doesn’t know who the child is. • The children must all expose the same methods.
  55. 55. • Factory: • initializes objects, returns them for use • should expose a similar set of methods • Strategy: • uses objects • must expose a similar set of methods
  56. 56. Data Storage (parent: “what happens”) Provider (child: “how it happens”) save()load() update() save()load() update() list()
  57. 57. Data Storage MySqlProvider save()load() update() save()load() update() connect()
  58. 58. Data Storage SalesForceProvider save()load() update() save()load() update() push()
  59. 59. Data Storage MySqlProvider SalesForceProviderOR:
  60. 60. class DataStorage //parent: “what happens” { protected $provider; //child: “how it happens” public function __construct ($provider) { $this->provider = $provider; } public function update ($model) { $this->provider->update($model); } }
  61. 61. interface ProviderInterface { /** * This is the blueprint for the provider, * the child. */ public function update($model); }
  62. 62. class MySqlProvider implements ProviderInterface { /** * Child MySqlProvider */ public function update($model) { $connection = $this->getConnection(); $connection->exec('UPDATE … SET'); return $this; } }
  63. 63. class SalesForceProvider { /** * Child SalesForceProvider */ public function update($model) { $client = new GuzzleHttpClient(); $client->request('POST', ‘{url}’); // ... } }
  64. 64. class DataStorage { /* … */ } interface ProviderInterface { /* … */ } class MysqlProvider { /* … */ } class SalesForceProvider { /* … */ } // using our strategy design pattern: $storage = new DataStorage(new MysqlProvider); $storage->update($data);
  65. 65. STRATEGY USES: • Any case where different algorithms or methods are needed to be interchangeable, but only one used: • Storage locations • Computational Algorithms
  66. 66. DEPENDENCY INJECTION FACTORY STRATEGY CHAIN OF RESPONSIBILITY (COR)
  67. 67. CHAIN OF RESPONSIBILITY Handling logic through multiple links/siblings of functionality.
  68. 68. PROBLEM: • Cascading layers of caching. • How to get the value from the list? • Cascading renderers for a component. • How to have renderers determine who should render?
  69. 69. public function get($key) { $redis = new Redis(); $db = new Db(); $file = new File(); if ($redisVal = $redis->get($key)) { return $redisVal; } else if ($dbVal = $db->get($key)) { return $dbVal; } else if ($fileVal = $$file->get($key)) { return $fileVal; } return ''; }
  70. 70. SOLUTION #1: LOOPS • Easier, but less flexible. • Iterate through each member of an array of the items and have that member check to determine its legibility.
  71. 71. public function get($key) { $cacheTypes = ['Redis', 'Db', 'File']; $value = ''; foreach ($cacheTypes as $cacheType) { $className = new "ModelCache{$cacheType}"; $cache = new $className(); if ($value = $cache->get($key)) { break; } } return $value; }
  72. 72. SOLUTION #2: COR • Horizontal chaining: sibling relationships • Parent class interacts with first item in the chain.
  73. 73. FileDBRedis Entry Point
  74. 74. File: no next link :( DB: $this->_nextLink Redis: $this->_nextLink
  75. 75. SOLUTION #2: COR • Each link inherits from a abstract class. • Abstract class handles chaining. • Concrete objects provide functionality for processing and whether or not to proceed.
  76. 76. COR DESIGN Need something from the cache FileSystemDBRedis processed? processed? processed? No Yes! AbstractCache
  77. 77. SETUP STEPS: • Instantiate first link. • Create next link. • Append to first link. • Continue…
  78. 78. USAGE STEPS: • Call first link’s get method. • Will check itself. • If no value, and next link, call that link. • Repeat…
  79. 79. <?php class Request { protected $_key; protected $_result; public function setKey($value) { /**/ } public function getResult() { /**/ } }
  80. 80. <?php abstract class Base { protected $_nextLink; protected $_result; protected $_data; abstract protected function getDataFor($key); public function append(Base $nextLink) { /**/ } public function get($request) { /**/ } public function processing($request) { /**/ } }
  81. 81. <?php abstract class Base { protected $_nextLink; protected $_result; protected $_data; abstract protected function getDataFor($key); public function append(Base $nextLink) { /**/ } public function get($request) { /**/ } public function processing($request) { /**/ } }
  82. 82. <?php abstract class Base { protected $_nextLink; protected $_result; protected $_data; abstract protected function getDataFor($key); public function append(Base $nextLink) { /**/ } public function get($request) { /**/ } public function processing($request) { /**/ } }
  83. 83. <?php abstract class Base { protected $_nextLink; protected $_result; protected $_data; abstract protected function getDataFor($key); public function append(Base $nextLink) { /**/ } public function get($request) { /**/ } public function processing($request) { /**/ } }
  84. 84. <?php abstract class Base { protected $_nextLink; protected $_result; protected $_data; abstract protected function getDataFor($key); public function append(Base $nextLink) { /**/ } public function get($request) { /**/ } public function processing($request) { /**/ } }
  85. 85. <?php abstract class Base { protected $_nextLink; protected $_result; protected $_data; abstract protected function getDataFor($key); public function append(Base $nextLink) { if (!$this->_nextLink) { $this->_nextLink = $nextLink; } else { $this->_nextLink->append($nextLink); } } public function get($request) { /**/ } public function processing($request) { /**/ } public function getSuccessor() { /**/ } }
  86. 86. abstract protected function getDataFor($key); public function append(Base $nextLink) { /* … */} public function get($request) { if (!($request instanceof Request)) { $key = $request; $request = new Request(); $request->setKey($key); } $success = $this->processing($request); if (!$success && $this->_nextLink) { $this->_nextLink->get($request); } return $request->getResult(); } public function processing($request) { /* … */ }
  87. 87. abstract protected function getDataFor($key); public function append(Base $nextLink) { /* … */} public function get($request) { if (!($request instanceof Request)) { $key = $request; $request = new Request(); $request->setKey($key); } $success = $this->processing($request); if (!$success && $this->_nextLink) { $this->_nextLink->get($request); } return $request->getResult(); } public function processing($request) { /* … */ }
  88. 88. public function get($request) { /* … */ } public function processing($request) { $key = $request->getKey(); $value = $this->getDataFor($key); if ($value) { $request->setResult($value); return true; } else { return false; } } public function getNextLink() { /* ... */} }
  89. 89. class RedisCache extends Base { protected function getDataFor($key) { // connects to redis and returns value } }
  90. 90. $redis = new RedisCache(); $redis->append(new DbCache) ->append(new FileCache); return $redis->get('key_name');
  91. 91. USAGE STEPS: • Call first link’s get method. • Will check itself. • If no value, and next link, call that link. • Repeat…
  92. 92. USE CASES: • Layers of caching • Rendering views with a standard input, but giving each member the equal opportunity to display. • Iterating through strategies to find a solution to an algorithm.
  93. 93. DEPENDENCY INJECTION FACTORY STRATEGY CHAIN OF RESPONSIBILITY (COR)
  94. 94. REVIEW DI Factory Strategy CoR Class has everything it needs on construction. Creation: Chooses and creates any object from options. Action: Similar interface for different applications. Interacts with options to retrieve output. All use specific classes to maintain Single Responsibility Principle.
  95. 95. RESOURCES • Design Patterns: Elements of Reusable Object- Oriented Software • (Gamma, Helm, Johnson, Vlissides; published by Addison-Wesley) • Design Patterns PHP • http://designpatternsphp.readthedocs.org/en/latest/ • PHP the Right Way • http://www.phptherightway.com/
  96. 96. FEEDBACK?https://joind.in/14702

×