Delegate Pattern

The pattern allows:

  • an application design where one object (Delegator) instead of performing a task, delegate it to an associated helper object (Delegates).

Delegation can be considered as an extreme form of object composition that can always be used to replace inheritance.


Delegate Pattern


PHP Example

In this example we have Mailer class comprising send method. For the class consumer it seems this Mailer class is performing the send, but in fact, it just delegates the task to another class (MockMailer)

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

/**
 * Delegate
 */

class MockMailer
{
    public function send($to, $subject, $body)
    {
        printf ("Message to: %s, subject: %s, body: %s\n\n", $to, $subject, $body);
    }
}

/**
 * Delegator
 */
class Mailer
{
    public function send($to, $subject, $body)
    {
        $mailer = new MockMailer();
        $mailer->send($to, $subject, $body);
    }
}

/**
 * Usage
 */

$mailer = new Mailer();
$mailer->send("email@address.com", "a subject", "a body");

// Output:
// -> Message to: email@address.com, subject: a subject, body: a body

JS/ES5 Example

Here delegation pattern is used to solve classical task of video-player. When client browser doesn’t support HTML5 video tag, the player delegates the task to one of fallback implementations.

/*
 * @category Design Pattern Tutorial
 * @package Delegate Sample
 * @author Dmitry Sheiko <me@dsheiko.com>
 * @link http://dsheiko.com
 */
(function() {
/**
 * Delegator
 */
var VideoPlayer = function() {
    if (window.navigator.userAgent.toLowerCase().match(/msie [0-8]\./i) !== null) {
        var delegate = new FlashDelegate();
    } else {
        var delegate = new Html5Delegate();
    }
    return {
        render : function(data) {
            return delegate.render(data);
        }
    }
}
/**
 * Concree Delegate 1
 */
var Html5Delegate = function() {
    var _private = {
        getVideoEl : function(width, height, poster) {
            var videoEl = document.createElement('video');
            videoEl.setAttribute('controls', 'controls');
            videoEl.setAttribute('preload', 'none');
            videoEl.setAttribute('poster', poster);
            videoEl.setAttribute('width', width);
            videoEl.setAttribute('height', height);
            return videoEl;
        },
        getSourceEl : function(type, src) {
            var srcEl = document.createElement('source');
            srcEl.setAttribute('type', type);
            srcEl.setAttribute('src', src);
            return srcEl;
        }
    }
    return {
        render : function(data) {
            var videoEl = _private.getVideoEl(
                data.width, data.height, data.poster);
            videoEl.appendChild(_private.getSourceEl('video/mp4', data.mp4Src));
            videoEl.appendChild(_private.getSourceEl('video/webm', data.webmSrc));
            videoEl.appendChild(_private.getSourceEl('video/ogg', data.oggSrc));
            return videoEl;
        }
    }
}
/**
 * Concree Delegate 2
 */
var FlashDelegate = function() {
    var FALLBACK_SWF = 'flashmediaelement.swf';
    var _private = {
        getObjectEl : function(width, height) {
            var objectEl = document.createElement('object');
            objectEl.setAttribute('type', 'application/x-shockwave-flash');
            objectEl.setAttribute('data', FALLBACK_SWF);
            objectEl.setAttribute('width', width);
            objectEl.setAttribute('height', height);
            return objectEl;
        },
        getParamEl : function(name, value) {
            var paramEl = document.createElement('param');
            paramEl.setAttribute('name', name);
            paramEl.setAttribute('value', value);
            return paramEl;
        }
    }
    return {
        render : function(data) {
            var objectEl = _private.getObjectEl(
                data.width, data.height);
            objectEl.appendChild(_private.getParamEl('movie', FALLBACK_SWF));
            objectEl.appendChild(_private.getParamEl('flashvars', 'controls=true&poster='
                + data.poster + '&file=' + data.mp4Src));
            return objectEl;
        }
    }
}

/**
 * Usage
 */

var player = new VideoPlayer();

console.log(player.render({
    width : 320,
    height: 240,
    poster: 'poster.jpg',
    mp4Src: 'myvideo.mp4',
    webmSrc: 'myvideo.webm',
    oggSrc: 'myvideo.ogg'
}));

}());