Prototype Pattern

The pattern allows:

  • creating new objects identical or closely resembling existing ones;
  • avoiding expense operation when it is required for the initial creating of an object;
  • to decouple composition, creating and representation of objects.

Prototype Pattern


PHP Example

Here in the example we have to make a collection of message objects which differ by only email address property. Thus, every element of the collection is an object derived from the given prototype.

<?php
/*
 * @category Design Pattern Tutorial
 * @package Prototype Sample
 * @author Dmitry Sheiko <me@dsheiko.com>
 * @link http://dsheiko.com
 */

/**
 * Concrete prototype
 */
class Entity_EmailMessage
{
    public $headers = array();
    public $subject = null;
    public $message = null;
    public $to = null;
    public function headersAsString()
    {
        $out = "MIME-Version: 1.0\r\n";
        foreach ($this->headers as $key => $val) {
            $out .= sprintf("%s: %s\r\n", $key, $val);
        }
        return $out;
    }
}
class Lib_PhpNative_Email
{
    public function send(Entity_EmailMessage $message)
    {
        printf ("Following message sent:\n %s\n To: %s\n Subject: %s\n Message: %s\n\n",
            $message->headersAsString(), $message->to, $message->subject
            , $message->message);
    }
}
class Lib_Mailer
{
    private $_messages = array();

    public function enqueue($email, $messagePrototype)
    {
        $message = clone $messagePrototype;
        $message->to = $email;
        $this->_messages[] = $message;
    }
    public function sendToAll()
    {
        $sender = new Lib_PhpNative_Email();
        array_walk ($this->_messages, array($sender, "send"));
    }
}

/**
 * Usage
 */
$prototype = new Entity_EmailMessage();
$prototype->headers = array(
    'From' => 'Our project <no-reply@ourproject.com>',
    'Content-type' => 'text/html; charset="iso-8859-1"',
    'Content-Transfer-Encoding' => '8bit',
);
$prototype->subject = 'Newsletter from our project';
$prototype->message = 'Body text....';

$mailer = new Lib_Mailer();
$mailer->enqueue("email1@email.com", $prototype);
$mailer->enqueue("email2@email.com", $prototype);
$mailer->sendToAll();

// Output:
// Following message sent:
// MIME-Version: 1.0
// From: Our project <no-reply@ourproject.com>
// Content-type: text/html; charset="iso-8859-1"
// Content-Transfer-Encoding: 8bit
//
// To: email1@email.com
// Subject: Newsletter from our project
// Message: Body text....
//
// Following message sent:
// MIME-Version: 1.0
// From: Our project <no-reply@ourproject.com>
// Content-type: text/html; charset="iso-8859-1"
// Content-Transfer-Encoding: 8bit
//
// To: email2@email.com
// Subject: Newsletter from our project
// Message: Body text....

JS/ES5 Example

This example is very close to PHP one, except to make JS cloning I had to use a trick (clone function).

/*
 * @category Design Pattern Tutorial
 * @package Prototype Sample
 * @author Dmitry Sheiko <me@dsheiko.com>
 * @link http://dsheiko.com
 */

(function(){

function Clone() { }
function clone(obj) {
    Clone.prototype = obj;
    return new Clone();
}
/**
 * Concrete prototype
 */
var Row = function(id, title){
    return {
        id: id,
        title: title
    }
};

var TestApp = function() {
    var _private = {
        table: []
    }
    return {
        requestSampleRow: function() {
            return new Row(1, 'Sample');
        },
        pupulateTable: function(sample) {
            _private.table.push(sample);
            for (var i = 1; i < 5; i++) {
                var sampleClone = clone(sample);
                sampleClone.id += i;
                _private.table.push(sampleClone);
            }
        },
        renderTable: function() {
            _private.table.forEach(function(row){
                console.log('id: ' + row.id + ', title: ' + row.title);
            })
        }
    }
};
/**
 * Usage
 */
var app = new TestApp();

var sample = app.requestSampleRow();
app.pupulateTable(sample);
app.renderTable();

// Output:
// id: 1, title: Sample
// id: 2, title: Sample
// id: 3, title: Sample
// id: 4, title: Sample
// id: 5, title: Sample

}());