Advertisement
ralphschindler

Untitled

Sep 6th, 2011
66
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 7.47 KB | None | 0 0
  1. Object Creation and Configuration
  2.  
  3. The ZF1 State of Things:
  4.  
  5. Over the development of the past few components in ZF2, we've been exploring different patterns that deal with object creation and configuration, as both of these concerns go hand-in-hand for most PHP developers - give the more common styles of coding. In ZF1, the most prolific pattern is the unified constructor.
  6.  
  7. class Foo
  8. {
  9. public function __construct($config = array()) {}
  10. }
  11.  
  12. The obvious pros:
  13.  
  14. * easy of use since the array() is the most well known and versatile
  15. structure in PHP
  16.  
  17. The obvious cons:
  18.  
  19. * knowing what keys are valid for $config
  20. * knowing what keys are required vs. option in $config
  21. * key naming convention is not standardized
  22. * docs cannot be generated from this prototype/signature by existing
  23. tools
  24. * hard to understand what is a scalar value vs. an objects
  25. dependency (another object)
  26.  
  27. The not so obvious cons are
  28.  
  29. * objects do not have a known identity; meaning, without knowing
  30. what instantiation time values distinguish one object from another.
  31.  
  32. * objects throughout the framework are too concerned with
  33. instantiation of other objects (dependencies) in non-obvious
  34. locations: like inside a getter or a setter.
  35.  
  36. Side effects of objects trying to centralize configuration are:
  37.  
  38. * Objects assume that they need to be configured early-on with
  39. configuration, but utilized later - leading developers to add
  40. lazy loading of dependencies as a feature of the object itself.
  41. This has the side effect of pushing creation of other objects into
  42. a getter or a setter in some form.
  43.  
  44. (Lazy loading of dependencies should be only done by objects that
  45. are computationally expensive and/or part of the objects "graph
  46. building" strategy)
  47.  
  48. Important things to remember:
  49.  
  50. * constructors are not subject to Liskov Substitution Principle
  51. (even though PHP allows __construct() in an interface, having
  52. it there is considered bad practice and should be avoided
  53. anyway)
  54.  
  55. What does this mean?
  56.  
  57. It means that any subclass can change the signature of the constructor should be allowed as per the requirements of the sub-type. Since sub-types can change their constructor to suit their own requirements, forcing them to comply with a parents __construct($config = array()) should generally be considered a bad practice.
  58.  
  59.  
  60.  
  61. What is the proposal?
  62.  
  63. * Well named factories plus constructors that describe an objects
  64. hard dependencies / required values, and optional dependencies
  65. should be used.
  66.  
  67. ** Objects with no hard or soft dependencies would not
  68. have constructors. **
  69.  
  70. This means that if an object must have a name, then the
  71. constructor should be
  72.  
  73. class Foo
  74. {
  75. public function __construct($name, $value = null) {}
  76. public function setValue($value) {}
  77. }
  78.  
  79. * Factories should describe the source being used for object
  80. creation, for example:
  81.  
  82. Baz::fromArray(array $array);
  83. Baz::fromConfig(Zend\Config\Config $config);
  84. Baz::fromString($string);
  85.  
  86. // used in Zend\Code
  87. Baz::fromReflection(ReflectionFile $reflection);
  88. (etc)
  89.  
  90. The from<source>() pattern should only be used when these methods
  91. exist within the class/type being constructed.
  92.  
  93. This pattern is well defined on wikipedia, see the "Descriptive
  94. names" section:
  95. http://en.wikipedia.org/wiki/Factory_method_pattern
  96.  
  97. It is understood that *all* factories within that given object
  98. will always produce type used at call time. This is achieved
  99. through PHP's 5.3 LSB (the factory applies to subtypes):
  100.  
  101. public function fromArray(array $array)
  102. {
  103. // constructor param setup from array
  104.  
  105. // static will always apply to extending classes
  106. $obj = new static(/* req. params */);
  107.  
  108. // other wiring from array
  109. return $obj; // will always return subtype
  110. }
  111.  
  112. ** Important Note **
  113.  
  114. This takes advantage of PHP's class level visibility, this means
  115. that the factories can interact with instance protected properties
  116. without having to go through accessors/mutators.
  117.  
  118. Example:
  119.  
  120. class Foo
  121. {
  122. protected $value;
  123.  
  124. public static function fromArray($array)
  125. {
  126. $obj = new static;
  127.  
  128. // interact with protected member
  129. $obj->value = $array['value'];
  130. return $obj;
  131. }
  132.  
  133. public function getValue()
  134. {
  135. return $this->value;
  136. }
  137.  
  138. }
  139.  
  140. * Dynamic/object factories will be allowed when one object is
  141. creating objects of a different type. These methods should
  142. NOT be static. The name of this factory object should contain
  143. the name 'Factory', for example:
  144.  
  145. class FooFactory
  146. {
  147. public function createBarFromArray(array $a) {
  148. // return type Bar
  149. }
  150. public function createBarFromConfig(config $b) {
  151. // return type Bar
  152. }
  153. }
  154.  
  155. The reasoning for having a factory object over a class full of
  156. static factory methods is that since one has opted to have a
  157. dynamic factory, there is some elements of factory configuration
  158. or state tracking that the factory is doing (for example, using a
  159. short name based plugin loader). Since that is the case, it is
  160. important that this state not be static so that other consumers
  161. of this factory have a fair chance at having a "default" factory
  162. object.
  163.  
  164. ** This model should be only used in complex instantiation
  165. scenarios **
  166.  
  167. * Factories are capable of calling factories of similar source type.
  168. So for example, if Foo::fromArray($array) was called, and a
  169. particular key 'bar' is located in $array, where
  170. $Foo->setBar(Bar $bar), and it is established that Bar::fromArray()
  171. exists, Foo::fromArray() would use Bar::fromArray() to instantiate
  172. from the value of the 'bar' key. This solves the problem of
  173. nested configuration/arrays that model the configuration
  174. of an object graph.
  175.  
  176. * Factories should throw exceptions when not enough information
  177. is provided.
  178.  
  179. * Objects should be completely valid and ready to do their object
  180. after instantiation
  181.  
  182. * All required dependencies should be fulfilled at instantiation
  183. time
  184.  
  185. * The special factory: createDefaultInstance() should create a
  186. poka-yoke instance with all dependencies pre-configured with
  187. sane defaults. For example:
  188.  
  189. class Foo
  190. {
  191.  
  192. public static function createDefaultInstance()
  193. {
  194. return new static(new Bar, new Baz);
  195. }
  196.  
  197. public function __construct(Bar $bar, Baz $baz) {}
  198.  
  199. }
  200.  
  201. Concerns Left To Other Components
  202.  
  203. * Lazy loading is not something any one object should be concerned
  204. with. Within an application, lazy loading can be achieved by
  205. the usage of a Service Locator. In other environments, this can
  206. also be solved by using a Dependency Injection container. See
  207. the above note on the special "createDefaultInstance()" factory.
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement