Today we will discuss the Prototype pattern.
The creation of objects in an application can be configured by metadata, for example by using Dependency Injection containers, or programmatically, in the case that it is accomplished by writing code.
The most customizable option for programmatic creation is the Prototype pattern. This pattern consists in choosing an already existent object, with a clone() operation available, as the base for creating new objects of the same kind.
Often the different objects that can be created by a Client are not instances of different ConcretePrototype classes, but different configurations of the same one; in this case the Prototype pattern may be used to provide all the different configurations to the Client, which needs to abstract away the object creation process.
The Prototype pattern allows also:
- management of available instances at runtime, performed simply by adding and removing operations on a collection of cloneable objects.
- specification of new objects very similar to each other, by cloning an existing one and changing its properties using setters.
- specification of new objects at runtime; a GoF example is about a circuit design system which lets the user insert already defined circuits as blocks of a new one. The circuits implement a clone() operation which clones all the components recursively.
- Prototype: interface of the cloneable classes.
- ConcretePrototype: implements the clone operations (the ones which are not already supported by the language).
- Client: actually clones Prototype instances to use the clones as new objects.
<?php /** * SECTION 1: a Widget interface and two different implementations. * Unlike in previous examples, where the Product was an Helper which generated * widgets, the actual instance created here is the Widget itself. * The problem solved, though, is the same: managing creation of widgets in the * middle of business logic. * * This class purpose is to generate blinking text in spite of all * usability recommendations. This is the Prototype. * As always, interfaces in php may be omitted. This is primary here for type * hinting. */ interface BlinkingWidget { } /** * Implementation that generates html tied to a javascript library. * This is one ConcretePrototype. */ class JavascriptWidget implements BlinkingWidget { public function initialize($text) { $this->_text = $text; } public function __toString() { return '<div dojoType="...">' . $this->_text . '</div>'; } } /** * A collaborator for the next ConcretePrototype. */ class ObjectTag { private $_html; public function setContent($html) { $this->_html = $html; } public function __toString() { return "<object>{$this->_html}</object>\n"; } } /** * Implementation that generates html that loads a flash object. * This is one ConcretePrototype. */ class FlashWidget implements BlinkingWidget { private $_objectTag; public function __construct(ObjectTag $objectTag) { $this->_objectTag = $objectTag; } public function initialize($text) { $this->_objectTag->setContent("<param name=\"text\">$text</param>"); } public function __toString() { return (string) $this->_objectTag; } /** * When using the clone operator, php will perform a shallow copy of the * original object, duplicating references to the same collaborators. * Then this method will be called on the newly created object; it's time * to perform a cloning of the collaborators which cannot be shared with * the original instance. * This is NOT an override: it's a post-cloning hook which completes the * new instance substituting some shallow copies with deep ones. */ public function __clone() { $this->_objectTag = clone $this->_objectTag; } } /** * SECTION 2: a Client class which clones instances of BlinkingWidget. * * We cannot instantiate all the BlinkingWidgets in advance, so we need a base * one which will be cloned every time a new one is needed. */ class LoginPage { private $_widget; public function __construct(BlinkingWidget $toClone) { $this->_widget = $toClone; } public function render() { $userId = uniqid('User '); // insert all the logic needed here... if (true or $complexBusinessLogicRules) { $widget = clone $this->_widget; $widget->initialize("Welcome, $userId"); return (string) $widget; } } } $prototypeWidget = new FlashWidget(new ObjectTag); $page = new LoginPage($prototypeWidget); echo $page->render(), "\n"; $page = new LoginPage(new JavascriptWidget); echo $page->render(), "\n";Theoretical notes
- in languages where a Class object (meta programming) is available, the Prototype pattern is less important because Class objects are already standard prototypes. In Php, the most we can configure is a class name with a string, along with an array that represents a series of constructor options, so it can be considered as a practical solution.
- main problem is implementing a clone() method, which should produce copies or references to collaborators, a difference that should be decided case-by-case (deep or shallow copies); php has a standard clone operator and __clone() magic method which are used in the code sample; I suggest to refer to the sample and the manual for more information.
- after the cloning process, it is often suggested to add an initialize() method which allows for setting pieces of state which may differ from the prototype object; for instance, an id or a name field. This method should not be included in the Prototype interface or its parameters be put in the clone() method: first, php does not support cloning with parameters; second, different ConcretePrototypes will need different initialization values or no values at all.
- in Javascript, object-oriented programming is prototype-based and not class-based. It means that, as a language feature, every new object is created by cloning a prototype. Class-based programming is often emulated by javascript frameworks.
Prototype seems a very simple pattern but this post turned out as detail-rich one. Do you feel anything is missing? Add a comment.
11 comments:
Some thoughts I was having on the idea:
1. I was wondering if it is a good idea to limit copying to only the original prototype. something like:
private $cloned = false;
public function __clone(){
if ($this->cloned)
throw LogicException('already cloned');
$clone = clone $this;
$clone->cloned = true;
}
to prevent a loss of control (at least that's what it seems to me).
2. Also i find that cloning an object that has collaborators feels it could lead to stepping of toes. the clone must make sure it doesn't affect it's hidden (as they are injected only once in a different "hidden" place) collaborators, so not to affect the state of all it's siblings
You should not call the clone operator on $this inside __clone(). Check the manual for the correct usage (http://php.net/clone). It may also resolve the problem with stepping the toes, as in __clone() you can duplicate the shared collaborator. :)
Please!! Use some syntax highlighting!!!!!
I set up the necessary css, but unfortunately Blogger strips away the html tags.
Sorry for newbie question, but I really can not get it why we use clone instead of just returning objects or creating them using `new`? The benefits of prototype instead of using some kind of Factory are not clear for me...
This pattern is very useful when you are dealing with a bunch of objects that are very similar, so you want to 1) reuse existing objects for creating new ones; 2) specify a real object to clone instead of a factory method.
An examples in the GoF book is about a vectorial drawing application: if you want to copy&paste lines, squares and figures cloning objects will be very handy...
Ok, thank you for your kind answer.
Can you offer an example of a real world usage for the prototype pattern in php web applications? Personally I cannot find one. It seems to me like you have no benefits using it because we have no application store in the global scope.
You have an application scope, but it is destroyed at the end of every request (it is the "global" scope. Every time you need to create an object with a life time shorter than the request one (like a User in a registration page) you can inject a factory, or a prototype from which clone a new object (the two solutions are functionally equivalent.)
In my opinion prototypes are useful for applications where you need to get many instances of the same object to build something.
A Dependency Injection container is a good example. In other technologies like asp.net you can load the objects defined in a xml file for example and use the container for every request without having to parse the xml file for each request. So the container is used in a global scope meaning that each request gets its objects from the same container.
In PHP from my knowledge is not possible to achieve this. So loading the object tree for each request makes this pattern unsuitable for PHP web application.
Can you provide an example where you used this pattern in an real world application of yours?
I have not written this but there is a quasi-Prototype pattern in the newInstance() method:
http://github.com/doctrine/doctrine2/blob/master/lib/Doctrine/ORM/Mapping/ClassMetadata.php
In PHP it is true that each requests has its own global scope, but that does not mean DI containers should not be used. Any time you have a class to instantiate *in* the logic of your application, say a view helper or a form, you set it up with a Factory or Prototype. The creator classes are always instantiated (but they manage multiple objects so their number is little), while the created class only when needed (Factory) or only one time if N instances are needed (Prototype). Factories and Prototypes are equivalent.
The Doctrine 2 example is hybrid, because the instance is lazy created instead of being passed at creation time. A classical implementation would be instantiating the creator object at the startup of the PHP script.
Post a Comment