(window.webpackJsonp=window.webpackJsonp||[]).push([[2448],{2856:function(t,a,e){"use strict";e.r(a);var s=e(31),n=Object(s.a)({},(function(){var t=this,a=t.$createElement,e=t._self._c||a;return e("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[e("h1",{attrs:{id:"design-patterns"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#design-patterns"}},[t._v("#")]),t._v(" Design Patterns")]),t._v(" "),e("p",[t._v("This topic provides examples of well known design patterns implemented in PHP.")]),t._v(" "),e("h2",{attrs:{id:"method-chaining-in-php"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#method-chaining-in-php"}},[t._v("#")]),t._v(" Method Chaining in PHP")]),t._v(" "),e("p",[t._v("Method Chaining is a technique explained in "),e("a",{attrs:{href:"http://rads.stackoverflow.com/amzn/click/0321712943",target:"_blank",rel:"noopener noreferrer"}},[t._v("Martin Fowler's book "),e("strong",[t._v("Domain Specific Languages")]),e("OutboundLink")],1),t._v(". Method Chaining is summarized as")]),t._v(" "),e("blockquote"),t._v(" "),e("p",[e("a",{attrs:{href:"http://martinfowler.com/dslCatalog/methodChaining.html",target:"_blank",rel:"noopener noreferrer"}},[e("strong",[t._v("Makes modifier methods return the host object, so that multiple modifiers can be invoked in a single expression")]),e("OutboundLink")],1),t._v(".")]),t._v(" "),e("p",[t._v("Consider this non-chaining/regular piece of code (ported to PHP from the aforementioned book)")]),t._v(" "),e("div",{staticClass:"language-php extra-class"},[e("pre",{pre:!0,attrs:{class:"language-php"}},[e("code",[e("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$hardDrive")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("new")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("HardDrive")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$hardDrive")]),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("->")]),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("setCapacity")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("150")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$hardDrive")]),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("->")]),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("external")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$hardDrive")]),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("->")]),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("setSpeed")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("7200")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n")])])]),e("p",[t._v("Method Chaining would allow you to write the above statements in a more compact way:")]),t._v(" "),e("div",{staticClass:"language-php extra-class"},[e("pre",{pre:!0,attrs:{class:"language-php"}},[e("code",[e("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$hardDrive")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("new")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("HardDrive")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("->")]),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("setCapacity")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("150")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("->")]),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("external")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("->")]),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("setSpeed")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("7200")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n")])])]),e("p",[t._v("All you need to do for this to work is to "),e("code",[t._v("return $this")]),t._v(" in the methods you want to chain from:")]),t._v(" "),e("div",{staticClass:"language-php extra-class"},[e("pre",{pre:!0,attrs:{class:"language-php"}},[e("code",[e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token class-name-definition class-name"}},[t._v("HardDrive")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("protected")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$isExternal")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token constant boolean"}},[t._v("false")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("protected")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$capacity")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("protected")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$speed")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("public")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("function")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token function-definition function"}},[t._v("external")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$isExternal")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token constant boolean"}},[t._v("true")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$this")]),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("->")]),e("span",{pre:!0,attrs:{class:"token property"}},[t._v("isExternal")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$isExternal")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" \n "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$this")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// returns the current class instance to allow method chaining")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("public")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("function")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token function-definition function"}},[t._v("setCapacity")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$capacity")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$this")]),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("->")]),e("span",{pre:!0,attrs:{class:"token property"}},[t._v("capacity")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$capacity")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" \n "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$this")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// returns the current class instance to allow method chaining")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("public")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("function")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token function-definition function"}},[t._v("setSpeed")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$speed")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$this")]),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("->")]),e("span",{pre:!0,attrs:{class:"token property"}},[t._v("speed")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$speed")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" \n "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token variable"}},[t._v("$this")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// returns the current class instance to allow method chaining")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),e("h3",{attrs:{id:"when-to-use-it"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#when-to-use-it"}},[t._v("#")]),t._v(" When to use it")]),t._v(" "),e("p",[t._v("The primary use cases for utilizing Method Chaining is when building internal Domain Specific Languages. Method Chaining is "),e("strong",[t._v("a building block")]),t._v(" in "),e("a",{attrs:{href:"http://martinfowler.com/bliki/ExpressionBuilder.html",target:"_blank",rel:"noopener noreferrer"}},[t._v("Expression Builders"),e("OutboundLink")],1),t._v(" and "),e("a",{attrs:{href:"http://martinfowler.com/bliki/FluentInterface.html",target:"_blank",rel:"noopener noreferrer"}},[t._v("Fluent Interfaces"),e("OutboundLink")],1),t._v(". "),e("a",{attrs:{href:"http://stackoverflow.com/a/17940086/208809",target:"_blank",rel:"noopener noreferrer"}},[t._v("It is not synonymous with those, though"),e("OutboundLink")],1),t._v(". Method Chaining merely enables those. Quoting Fowler:")]),t._v(" "),e("blockquote"),t._v(" "),e("p",[t._v("I've also noticed a common misconception - many people seem to equate fluent interfaces with Method Chaining. Certainly chaining is a common technique to use with fluent interfaces, but true fluency is much more than that.")]),t._v(" "),e("p",[t._v("With that said, using Method Chaining just for the sake of avoiding writing the host object is considered a "),e("a",{attrs:{href:"http://martinfowler.com/bliki/CodeSmell.html",target:"_blank",rel:"noopener noreferrer"}},[t._v("code smell"),e("OutboundLink")],1),t._v(" by many. It makes for unobvious APIs, especially when mixing with non-chaining APIs.")]),t._v(" "),e("h3",{attrs:{id:"additional-notes"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#additional-notes"}},[t._v("#")]),t._v(" Additional Notes")]),t._v(" "),e("h3",{attrs:{id:"command-query-separation"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#command-query-separation"}},[t._v("#")]),t._v(" Command Query Separation")]),t._v(" "),e("p",[e("a",{attrs:{href:"http://martinfowler.com/bliki/CommandQuerySeparation.html",target:"_blank",rel:"noopener noreferrer"}},[t._v("Command Query Separation is a design principle brought forth by Bertrand Meyer"),e("OutboundLink")],1),t._v(". It states that methods mutating state ("),e("strong",[t._v("commands")]),t._v(") should not return anything, whereas methods returning something ("),e("strong",[t._v("queries")]),t._v(") should not mutate state. This makes it easier to reason about the system. Method Chaining violates this principle because we are mutating state "),e("strong",[t._v("and")]),t._v(" returning something.")]),t._v(" "),e("h3",{attrs:{id:"getters"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#getters"}},[t._v("#")]),t._v(" Getters")]),t._v(" "),e("p",[t._v("When making use of classes which implement method chaining, pay particular attention when calling getter methods (that is, methods which return something other than "),e("code",[t._v("$this")]),t._v("). Since getters must return a value other than "),e("code",[t._v("$this")]),t._v(", chaining an additional method onto a getter makes the call operate on the "),e("strong",[t._v("gotten")]),t._v(" value, not on the original object. While there are some use cases for chained getters, they may make code less readable.")]),t._v(" "),e("h3",{attrs:{id:"law-of-demeter-and-impact-on-testing"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#law-of-demeter-and-impact-on-testing"}},[t._v("#")]),t._v(" Law of Demeter and impact on testing")]),t._v(" "),e("p",[t._v("Method Chaining as presented above does not violate "),e("a",{attrs:{href:"https://en.wikipedia.org/wiki/Law_of_Demeter",target:"_blank",rel:"noopener noreferrer"}},[t._v("Law of Demeter"),e("OutboundLink")],1),t._v(". Nor does it impact testing. That is because we are returning the host instance and not some collaborator. It's a common misconception stemming from people confusing mere Method Chaining with "),e("strong",[t._v("Fluent Interfaces")]),t._v(" and "),e("strong",[t._v("Expression Builders")]),t._v(". It is only when Method Chaining returns "),e("strong",[t._v("other objects than the host object")]),t._v(" that you violate Law of Demeter and end up with Mock fests in your tests.")])])}),[],!1,null,null,null);a.default=n.exports}}]);