From a51f182cc0e602147c7b4b22d7eb1759afcca82e Mon Sep 17 00:00:00 2001 From: Chris Woodford Date: Tue, 3 May 2011 22:48:40 -0400 Subject: [PATCH 1/6] first draft of cookbook article --- en/cookbook/decorator-pattern.rst | 280 ++++++++++++++++++++++++++++++ 1 file changed, 280 insertions(+) create mode 100644 en/cookbook/decorator-pattern.rst diff --git a/en/cookbook/decorator-pattern.rst b/en/cookbook/decorator-pattern.rst new file mode 100644 index 000000000..5b29aceb8 --- /dev/null +++ b/en/cookbook/decorator-pattern.rst @@ -0,0 +1,280 @@ +Persisting the Decorator Pattern +================================ + +.. sectionauthor:: Chris Woodford + + + +Decorator Pattern +----------------- + +Let's take a quick look at a visual representation of the Decorator +pattern + + +Doctrine Implementation +----------------------- + +In order to persist this pattern, we need fields in the Component +class to be stored and shared among the entire hierarchy, and we +needed any additional fields in the ConcreteComponent and +ConcreteDecorator to be stored as well. In this design, the Decorator +class just delegates calls to the Component decorated and doesn't +require any persistence of its own. However, the Decorator class +does have an association to the persistent Component that it's +decorating, and does need to be a part of this persistence hierarchy. + +Since the Component class needs to be persisted, it's going to be a +Doctrine Entity. As the top of the inheritance hierarchy, it's going +to have to define the persistent inheritance. For this example, we +will use Single Table Inheritance, but Class Table Inheritance +would work as well. + +In the discriminator map, we need to define two concrete subclasses, +ConcreteComponent and ConcreteDecorator. + +Component +--------- + +.. code-block:: php + + id; + } + + /** + * Set name + * @param string $name + */ + public function setName($name) + { + $this->name = $name; + } + + /** + * Get name + * @return string $name + */ + public function getName() + { + return $this->name; + } + + } + +ConcreteComponent +----------------- + +The ConcreteComponent class is pretty simple and doesn't do much more +than extend the abstract Component class (only for the purpose of +keeping this example simple). + +.. code-block:: php + + setDecorates($c); + } + + /** + * (non-PHPdoc) + * @see ImedevacTest.Component::getName() + */ + public function getName() + { + return 'Decorated ' . $this->getDecorates()->getName(); + } + + /** + * the component being decorated + * @return Component + */ + protected function getDecorates() + { + return $this->decorates; + } + + /** + * sets the component being decorated + * @param Component $c + */ + protected function setDecorates(Component $c) + { + $this->decorates = $c; + } + + } + +All operations on the Decorator (i.e. persist, remove, etc) will +cascade from the Decorator to the Component. This means that when we +persist a Decorator, Doctrine will take care of persisting the chain +of decorated objects for us. A Decorator can be treated exactly as a +Component when it comes time to persisting it. + +The Decorator's constructor accepts an instance of a Component, as +defined by the Decorator pattern (using constructor injection). The +setDecorates/getDecorates methods have been defined as protected to +hide the fact that a Decorator is decorating a Component and keeps +the Component interface and the Decorator interface identical. + +To illustrate the purpose of the Decorator pattern, the getName() +method has been overridden to append a string to the Component's +getName() method. + +ConcreteDecorator +----------------- + +.. code-block:: php + + special = $special; + } + + /** + * Get special + * @return string $special + */ + public function getSpecial() + { + return $this->special; + } + + /** + * (non-PHPdoc) + * @see ImedevacTest.Component::getName() + */ + public function getName() + { + return '[' . $this->getSpecial() + . '] ' . parent::getName(); + } + + } + +Tests +----- + +.. code-block:: php + + setName('Test Component 1'); + $em->persist($c); // assigned unique ID = 1 + + // create a new concrete decorator + $c = new ConcreteComponent(); + $c->setName('Test Component 2'); + + $d = new ConcreteDecorator($c); + $d->setSpecial('Really'); + $em->persist($d); + // assigns c as unique ID = 2, and d as unique ID = 3 + + $em->flush(); + + $c = $em->find('Test\Component', 1); + $d = $em->find('Test\Component', 3); + + echo get_class($c); + // prints: Test\Component\Concrete\Component + + echo $c->getName(); + // prints: Test Component 1 + + echo get_class($d) + // prints: Test\Component\Concrete\Decorator + + echo $d->getName(); + // prints: [Really] Decorated Test Component 2 + From c242ab437152f042fea9412951022fb8a260d094 Mon Sep 17 00:00:00 2001 From: Chris Woodford Date: Tue, 3 May 2011 23:04:41 -0400 Subject: [PATCH 2/6] some tweaks to class descriptions --- en/cookbook/decorator-pattern.rst | 46 ++++++++++++++----------------- 1 file changed, 20 insertions(+), 26 deletions(-) diff --git a/en/cookbook/decorator-pattern.rst b/en/cookbook/decorator-pattern.rst index 5b29aceb8..052c9f8b5 100644 --- a/en/cookbook/decorator-pattern.rst +++ b/en/cookbook/decorator-pattern.rst @@ -3,38 +3,22 @@ Persisting the Decorator Pattern .. sectionauthor:: Chris Woodford - - -Decorator Pattern ------------------ +INTRO Let's take a quick look at a visual representation of the Decorator pattern +DECORATOR PATTERN IMAGE -Doctrine Implementation ------------------------ - -In order to persist this pattern, we need fields in the Component -class to be stored and shared among the entire hierarchy, and we -needed any additional fields in the ConcreteComponent and -ConcreteDecorator to be stored as well. In this design, the Decorator -class just delegates calls to the Component decorated and doesn't -require any persistence of its own. However, the Decorator class -does have an association to the persistent Component that it's -decorating, and does need to be a part of this persistence hierarchy. +Component +--------- Since the Component class needs to be persisted, it's going to be a Doctrine Entity. As the top of the inheritance hierarchy, it's going to have to define the persistent inheritance. For this example, we will use Single Table Inheritance, but Class Table Inheritance -would work as well. - -In the discriminator map, we need to define two concrete subclasses, -ConcreteComponent and ConcreteDecorator. - -Component ---------- +would work as well. In the discriminator map, we will define two +concrete subclasses, ConcreteComponent and ConcreteDecorator. .. code-block:: php @@ -46,8 +30,8 @@ Component * @Entity * @InheritanceType("SINGLE_TABLE") * @DiscriminatorColumn(name="discr", type="string") - * @DiscriminatorMap({"cc" = "TestComponentConcreteComponent", - "cd" = "TestDecoratorConcreteDecorator"}) + * @DiscriminatorMap({"cc" = "Test\Component\Concrete\Component", + "cd" = "Test\Decorator\Concrete\Decorator"}) */ abstract class Component { @@ -189,6 +173,13 @@ getName() method. ConcreteDecorator ----------------- +The final class required to complete a simple implementation of the +Decorator pattern is the ConcreteDecorator. In order to further +illustrate how the Decorator can alter data as it moves through the +chain of decoration, a new field, "special", has been added to this +class. The getName() has been overridden and appends the value of the +getSpecial() method to its return value. + .. code-block:: php Date: Thu, 5 May 2011 21:16:40 -0400 Subject: [PATCH 3/6] fixed some typos --- en/cookbook/decorator-pattern.rst | 79 +++++++++++++++---------------- 1 file changed, 38 insertions(+), 41 deletions(-) diff --git a/en/cookbook/decorator-pattern.rst b/en/cookbook/decorator-pattern.rst index 052c9f8b5..f84bade45 100644 --- a/en/cookbook/decorator-pattern.rst +++ b/en/cookbook/decorator-pattern.rst @@ -5,20 +5,15 @@ Persisting the Decorator Pattern INTRO -Let's take a quick look at a visual representation of the Decorator -pattern - -DECORATOR PATTERN IMAGE - Component --------- -Since the Component class needs to be persisted, it's going to be a -Doctrine Entity. As the top of the inheritance hierarchy, it's going +Since the ``Component`` class needs to be persisted, it's going to +be an ``Entity``. As the top of the inheritance hierarchy, it's going to have to define the persistent inheritance. For this example, we -will use Single Table Inheritance, but Class Table Inheritance +will use Single Table Inheritance, but Class Table Inheritance would work as well. In the discriminator map, we will define two -concrete subclasses, ConcreteComponent and ConcreteDecorator. +concrete subclasses, ``ConcreteComponent`` and ``ConcreteDecorator``. .. code-block:: php @@ -30,8 +25,8 @@ concrete subclasses, ConcreteComponent and ConcreteDecorator. * @Entity * @InheritanceType("SINGLE_TABLE") * @DiscriminatorColumn(name="discr", type="string") - * @DiscriminatorMap({"cc" = "Test\Component\Concrete\Component", - "cd" = "Test\Decorator\Concrete\Decorator"}) + * @DiscriminatorMap({"cc" = "Test\Component\ConcreteComponent", + "cd" = "Test\Decorator\ConcreteDecorator"}) */ abstract class Component { @@ -77,9 +72,9 @@ concrete subclasses, ConcreteComponent and ConcreteDecorator. ConcreteComponent ----------------- -The ConcreteComponent class is pretty simple and doesn't do much more -than extend the abstract Component class (only for the purpose of -keeping this example simple). +The ``ConcreteComponent`` class is pretty simple and doesn't do much +more than extend the abstract ``Component`` class (only for the +purpose of keeping this example simple). .. code-block:: php @@ -96,9 +91,9 @@ keeping this example simple). Decorator --------- -The Decorator class doesn't need to be persisted, but it does need to -define an association with a persisted Entity. We can use a -MappedSuperclass for this. +The ``Decorator`` class doesn't need to be persisted, but it does +need to define an association with a persisted ``Entity``. We can +use a ``MappedSuperclass`` for this. .. code-block:: php @@ -111,7 +106,7 @@ MappedSuperclass for this. { /** - * @OneToOne(targetEntity="TestComponent", cascade={"all"}) + * @OneToOne(targetEntity="Test\Component", cascade={"all"}) * @JoinColumn(name="decorates", referencedColumnName="id") */ protected $decorates; @@ -127,7 +122,7 @@ MappedSuperclass for this. /** * (non-PHPdoc) - * @see ImedevacTest.Component::getName() + * @see Test.Component::getName() */ public function getName() { @@ -154,31 +149,33 @@ MappedSuperclass for this. } -All operations on the Decorator (i.e. persist, remove, etc) will -cascade from the Decorator to the Component. This means that when we -persist a Decorator, Doctrine will take care of persisting the chain -of decorated objects for us. A Decorator can be treated exactly as a -Component when it comes time to persisting it. +All operations on the ``Decorator`` (i.e. persist, remove, etc) will +cascade from the ``Decorator`` to the ``Component``. This means that +when we persist a ``Decorator``, Doctrine will take care of +persisting the chain of decorated objects for us. A ``Decorator`` can +be treated exactly as a ``Component`` when it comes time to +persisting it. -The Decorator's constructor accepts an instance of a Component, as -defined by the Decorator pattern (using constructor injection). The -setDecorates/getDecorates methods have been defined as protected to -hide the fact that a Decorator is decorating a Component and keeps -the Component interface and the Decorator interface identical. +The ``Decorator's`` constructor accepts an instance of a +``Component``, as defined by the ``Decorator`` pattern (using +constructor injection). The setDecorates/getDecorates methods have +been defined as protected to hide the fact that a ``Decorator`` is +decorating a ``Component`` and keeps the ``Component`` interface and +the ``Decorator`` interface identical. -To illustrate the purpose of the Decorator pattern, the getName() -method has been overridden to append a string to the Component's +To illustrate the purpose of the ``Decorator`` pattern, the getName() +method has been overridden to append a string to the ``Component's`` getName() method. ConcreteDecorator ----------------- The final class required to complete a simple implementation of the -Decorator pattern is the ConcreteDecorator. In order to further -illustrate how the Decorator can alter data as it moves through the -chain of decoration, a new field, "special", has been added to this -class. The getName() has been overridden and appends the value of the -getSpecial() method to its return value. +Decorator pattern is the ``ConcreteDecorator``. In order to further +illustrate how the ``Decorator`` can alter data as it moves through +the chain of decoration, a new field, "special", has been added to +this class. The getName() has been overridden and appends the value +of the getSpecial() method to its return value. .. code-block:: php @@ -215,7 +212,7 @@ getSpecial() method to its return value. /** * (non-PHPdoc) - * @see ImedevacTest.Component::getName() + * @see Test.Component::getName() */ public function getName() { @@ -235,8 +232,8 @@ objects find('Test\Component', 3); echo get_class($c); - // prints: Test\Component\Concrete\Component + // prints: Test\Component\ConcreteComponent echo $c->getName(); // prints: Test Component 1 echo get_class($d) - // prints: Test\Component\Concrete\Decorator + // prints: Test\Component\ConcreteDecorator echo $d->getName(); // prints: [Really] Decorated Test Component 2 From e9d096e411f2d6b79c775475437dc25ce93935d0 Mon Sep 17 00:00:00 2001 From: Chris Woodford Date: Thu, 5 May 2011 21:23:10 -0400 Subject: [PATCH 4/6] cleaning up some sentences. better flow --- en/cookbook/decorator-pattern.rst | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/en/cookbook/decorator-pattern.rst b/en/cookbook/decorator-pattern.rst index f84bade45..7fe7c2dda 100644 --- a/en/cookbook/decorator-pattern.rst +++ b/en/cookbook/decorator-pattern.rst @@ -157,15 +157,15 @@ be treated exactly as a ``Component`` when it comes time to persisting it. The ``Decorator's`` constructor accepts an instance of a -``Component``, as defined by the ``Decorator`` pattern (using -constructor injection). The setDecorates/getDecorates methods have -been defined as protected to hide the fact that a ``Decorator`` is -decorating a ``Component`` and keeps the ``Component`` interface and -the ``Decorator`` interface identical. +``Component``, as defined by the ``Decorator`` pattern. The +setDecorates/getDecorates methods have been defined as protected to +hide the fact that a ``Decorator`` is decorating a ``Component`` and +keeps the ``Component`` interface and the ``Decorator`` interface +identical. -To illustrate the purpose of the ``Decorator`` pattern, the getName() -method has been overridden to append a string to the ``Component's`` -getName() method. +To illustrate the intended result of the ``Decorator`` pattern, the +getName() method has been overridden to append a string to the +``Component's`` getName() method. ConcreteDecorator ----------------- From 6cd778f940d56aacb1352f9842333253794dfcdf Mon Sep 17 00:00:00 2001 From: Chris Woodford Date: Thu, 5 May 2011 21:36:18 -0400 Subject: [PATCH 5/6] wrote a quick introduction --- en/cookbook/decorator-pattern.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/en/cookbook/decorator-pattern.rst b/en/cookbook/decorator-pattern.rst index 7fe7c2dda..38bf3ef54 100644 --- a/en/cookbook/decorator-pattern.rst +++ b/en/cookbook/decorator-pattern.rst @@ -3,12 +3,14 @@ Persisting the Decorator Pattern .. sectionauthor:: Chris Woodford -INTRO +This recipe will show you a simple example of how you can use +Doctrine 2 to persist an implementaton of the +`Decorator Pattern `_ Component --------- -Since the ``Component`` class needs to be persisted, it's going to +The ``Component`` class needs to be persisted, so it's going to be an ``Entity``. As the top of the inheritance hierarchy, it's going to have to define the persistent inheritance. For this example, we will use Single Table Inheritance, but Class Table Inheritance From d3f6ffb09ef9e774cfbb256dab9de611ebcb99c1 Mon Sep 17 00:00:00 2001 From: Chris Woodford Date: Sun, 18 Sep 2011 18:39:36 -0400 Subject: [PATCH 6/6] Added cookbook/decorator-pattern to index file --- en/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/en/index.rst b/en/index.rst index 2164541c8..67dafce1f 100644 --- a/en/index.rst +++ b/en/index.rst @@ -54,6 +54,7 @@ Cookbook :maxdepth: 1 cookbook/aggregate-fields + cookbook/decorator-pattern cookbook/dql-custom-walkers cookbook/dql-user-defined-functions cookbook/implementing-arrayaccess-for-domain-objects