Following the previous post on the Singleton design pattern, this article will discuss some features of the Builder pattern.
First thing’s first. As a creational pattern, the Builder pattern deals with creation of new items throught the application, but unlike the Singleton, which has the main focus of creating a globally avaible instace of an object, the Builder focuses on creating an object by following various steps and then returning the final product in a neatly organized object. As The Great Book sais, the Builder’s main intent is to “separate the construction of a complex object from its representation so that the same construction process can create different representations”.
Now that we know how the Builder is supposed to work in theory, it’s time to look at a real life example. Due to my lack of imagination (hopefully it’s just temporary), but mostly my desire to keep things simple, has made me to choose as example a simple form generator, which consists of an input, and a submit button. How does this tie to the pattern? Well, the input can be constructed in various ways, depending on the input type and element count. In this case, we can build text forms (with a text input), radio forms (with radio buttons) and finally area forms (which use textareas).
All there of them extend an abstract Builder class, and in particular the way they create an element. On the other hand, they inherit a method for creating the submit button, createSubmit(), because we can admit the fact that each form creates the same type of button. They also inherit the renderForm() method. So, let’s take a first glance at the abstract class:
class AbstractBuilder { public function createSubmit( $value ) { $submitButton = new Element(); $submitButton->setType( "submit" ); $submitButton->setName( "submit" ); $submitButton->setValue( "Create new form" ); array_push( $this->_storedProducts, $submitButton ); } public function renderForm() { echo "\r\n".'<form method="post">'."<table>\r\n"; foreach($this->_storedProducts as $product) { if( $product->getType() != "submit" && $product->getType() != "area" ) echo $product->getValue().'<input type="'.$product->getType().'" name="'.$product->getName().'" value="'.$product->getValue().'" /><br />'."\r\n"; else if($product->getType() == "area") echo '<textarea name="'.$product->getName().'" rows="5" cols="30"></textarea><br />'."\r\n"; else echo '<input type="'.$product->getType().'" name="'.$product->getName().'" value="'.$product->getValue().'" /><br />'."\r\n"; } echo "</table>\r\n</form>\r\n"; } protected $_storedProducts = array(); }
You will notice the protected member variable $_storedElements. Its used to store the items created by the concrete builders and is basically an array of Element objects, which store the information needed to define a form element, like its type, name and value, and some methods for getting and setting these variables, since they are private. This is how the class looks like:
class Element { public function Element() {} public function setType( $type ) { $this->_type = $type; } public function setName( $name ) { $this->_name = $name; } public function setValue( $value ) { $this->_value = $value; } public function getType() { return $this->_type; } public function getName() { return $this->_name; } public function getValue() { return $this->_value; } private $_type = ""; private $_name = ""; private $_value = ""; }
Simple as pie. Now we’ll have to combine these two classes in order to make one (ore more) concrete builder(s) that build a form and then render it.
Our first candidate is the TextBuilder. It extends the AbstractBuilder class by implementing the createElement( $name ) method, which creates and Element object, and sets its name and type (text), the default value is not important in this case. So, this class has only one method, just to keep things as simple as possible:
class TextFormBuilder extends AbstractBuilder { public function InputFormBuilder() {} public function createElement( $name ) { $textInput = new Element(); $textInput->setType( "text" ); $textInput->setName( $name ); array_push( $this->_storedElements, $textInput ); } }
Done! Now was that easy, or what? Now we can create custom text input forms. And if you think of the final form as an object, you definetly see the pattern applied in this case. But just one builder alone, won’t do much good in the long run, we need more of them and use the appropriate one for each situation. So, how should we go about making a radio input form? Easy, just like before, we extend the AbstractBuilder class and then override the createElement() method. This time, besides the name, we need to add the value of the element, since only one radiobutton in a form doesn’t make much sense, but they do share the name. This way can create as many radio buttons as we want in a form calling this method:
class RadioFormBuilder extends AbstractBuilder { public function RadioFormBuilder() {} public function createElement( $name, $value ) { $radioInput = new Element(); $radioInput->setType( "radio" ); $radioInput->setName( $name ); $radioInput->setValue( $value ); array_push( $this->_storedElements, $radioInput ); } }
Now we have a builder for radio forms. As you can see form the previous TextForm builder, this one also has the createElement() method, but it passes one more argument, which is the element value. This way we can have multiple radio buttons, which send different data to the server. Finally, we have a form with a textarea. This one differs from the first builder only in regards to the type it sets to the element. Since textareas can’t be created with the <input> tag, we just set a “pseudo-element-type” that will be reconginzed by the renderForm() method and render the textarea correctly.
class AreaFormBuilder extends AbstractBuilder { public function AreaFormBuilder() {} public function createElement( $name ) { $areaInput = new Element(); $areaInput->setType( "area" ); $areaInput->setName( $name ); array_push( $this->_storedElements, $areaInput ); } }
There we have it! Let’s focus a bit on the type of the element. Like I said, we’ll set the element type “area”, even if we can’t render a textarea using <input type=”area” />. But that’s no problem for the AbstractBuilder class. It can check the type of each stored Element and if, by any chance, its type is “area” it will render the element accordingly.
Phew! That was quite a bit of code to go through… But we’re almost there. Now we can include these classes in an application and start creating forms. To better illustrate this, let’s consider the following application: we create a form based on a value passed by the user in the input element. That is to say, the user can enter either “text”, “radio” or “area” and the application will create an appropriate form by making an instance of the required builder:
switch( $_POST["input"] ) { case "text": $builder = new TextFormBuilder(); $builder->createElement( "input" ); break; case "radio": $builder = new RadioFormBuilder(); $builder->createElement( "input", "text" ); $builder->createElement( "input", "radio" ); $builder->createElement( "input", "area" ); break; case "area": $builder = new AreaFormBuilder(); $builder->createElement( "input" ); break; default: $builder = new TextFormBuilder(); $builder->createElement( "input" ); break; }
As we all know, no form is complete without a submit button. But since all builders create this element in the same way, we call that function after this switch like this: $builder->createSubmit( “Create new form” );
Now that we have all the elements in place, we need to render the form, so we can start testing the application. We call the renderForm() method anywhere we see fit to do so, like this: $builder->renderForm();
That’s it! The $builder variable provides an interface for creating any type of form, and it’s elements can be added any way we please. As always, you can see the application here. And if you want to mess around with the code, you can donwload it here.