Categories: Software Engineering

Prototype design pattern, prototype pattern

Prototype design pattern is used when we want to create one object which can be used as a template (prototype) for creation of some other objects.

Prototype pattern is one of the creational patterns (because it indicates a way of how we create objects).

Creational patterns are: Factory pattern, Abstract factory pattern, Singleton pattern, Prototype pattern, Builder pattern and Object pool pattern.

Table of Contents

Use case example

Imagine for example, having a system that handles large number of user accounts, with different account types (SuperAdministrator, Administrator, Moderator, Member and Guest).

Now, imagine that these classes have a complex system for calculation of permissions. Complex means that there are lots of calculations (expensive and long ones) resulting in array of permissions.

If we would make new object for each user that appears in system (creates, logs in, etc.) we would repeat this expensive action lots of times. On the other hand if we “prepare the prototype” of common classes of users, then we get to quickly clone them and use already prepared values.

Essentially, if used properly prototype design pattern can:

  • Optimize code execution, speeding it up by cloning results of calculations instead of repeating calculations (each time when we do new Object())
  • Make code more readable and prettified
  • Potentially make unit testing more straightforward, because structure is cleaner

Prototype pattern example in PHP

This is one simple example of prototype pattern, written in PHP. As you can see we have a UserPrototype abstract class. Meaning, this class can never be instantiated. Furthermore, this class has all important properties that our prototypes should have.

Moreover, there is an abstract function called __clone() which will be implemented in all classes created from this abstract.

<?php

/**
 * UserPrototype class represents an abstract class to define Users.
 * This class has common values, important setters and getters.
 * This class has one abstract function clone, which will be realized
 * in the "child" classes.
 */abstract class UserPrototype {

    protected $typeId;
    protected $typeName;
    protected $adminAccess;
    protected $databaseAccess;
    protected $securityLogAccess;
    protected $permissions;
    protected $username;
    protected $firstname;
    protected $lastname;

    abstract function __clone();

    function setUsername($username) {
        $this->username = $username;
    }

    function setFirstname($firstname) {
        $this->firstname = $firstname;
    }

    function setLastname($lastname) {
        $this->lastname = $lastname;
    }

}

class SuperAdminPrototype extends UserPrototype {
    function __construct() {
        $this->typeId = 1;
        $this->type = 'SuperAdministrator';
        $this->adminAcces = true;
        $this->databaseAccess = true;
        $this->securityLogAccess = true;
        $this->permissions = [
            'create-user',
            'delete-user',
            'absolutely-all-permissions',
            'rm rf system',
            'read-logs',
            'delete-admins',
            'moderate-content',
            'create-content',
            'read-content',
        ];
    }
    function __clone() {
    }
}

class AdminPrototype extends UserPrototype {
    function __construct() {
        $this->typeId = 2;
        $this->type = 'Administrator';
        $this->adminAcces = true;
        $this->databaseAccess = true;
        $this->securityLogAccess = true;
        $this->permissions = [
            'create-user',
            'delete-user',
            'read-logs',
            'moderate-content',
            'create-content',
            'read-content',
        ];
    }
    function __clone() {
    }
}

class ModeratorPrototype extends UserPrototype {
    function __construct() {
        $this->typeId = 3;
        $this->type = 'Moderator';
        $this->adminAcces = false;
        $this->databaseAccess = true;
        $this->securityLogAccess = true;
        $this->permissions = [
            'moderate-content',
            'create-content',
            'read-content',
        ];
    }
    function __clone() {
    }
}

class MemberPrototype extends UserPrototype {
    function __construct() {
        $this->typeId = 5;
        $this->type = 'Member';
        $this->adminAcces = false;
        $this->databaseAccess = false;
        $this->securityLogAccess = false;
        $this->permissions = [
            'create-content',
            'read-content',
        ];
    }
    function __clone() {
    }
}

class GuestPrototype extends UserPrototype {
    function __construct() {
        $this->typeId = 6;
        $this->type = 'Guest';
        $this->adminAcces = false;
        $this->databaseAccess = false;
        $this->securityLogAccess = false;
        $this->permissions = [
            'read-content',
        ];
    }
    function __clone() {
    }
}
 
echo "=== Examples of Prototype pattern === \n";

// Firstly, we will create object for each prototype

$superAdminPrototype = new SuperAdminPrototype();
$adminPrototype = new AdminPrototype();
$moderatorPrototype = new ModeratorPrototype();
$memberPrototype = new MemberPrototype();
$guestPrototype = new GuestPrototype();

// Secondly, we will create several objects out of prototype objects

$superAdmin = clone $superAdminPrototype;
$superAdmin->setUsername("fancySuperAdminUsername");
$superAdmin->setFirstname("Super");
$superAdmin->setLastname("Admin");

$admin1 = clone $adminPrototype;
$admin1->setUsername("fancyAdminNameNo1");
$admin1->setFirstname("Admin");
$admin1->setLastname("001");

$admin2 = clone $adminPrototype;
$admin2->setUsername("fancyAdminNameNo2");
$admin2->setFirstname("Admin");
$admin2->setLastname("002");

$moderator = clone $moderatorPrototype;
$moderator->setUsername("fancyModeratorUsername");
$moderator->setFirstname("Moderator");
$moderator->setLastname("The Great");

$plainOldMember = clone $memberPrototype;
$plainOldMember->setUsername("PlainOldMemberUsername");
$plainOldMember->setFirstname("PlainOld");
$plainOldMember->setLastname("Member");

$justGuest = clone $guestPrototype;
$justGuest->setUsername("JaneDoe");
$justGuest->setFirstname("Jane");
$justGuest->setLastname("Doe");

// If we list Super Admin object, we will see that all prototype values are assigned and available.

var_dump($superAdmin);

// If we list Admin objects, we will see that all prototype values are assigned and available.

var_dump($admin1);
var_dump($admin2);

// Same case for moderator, member and guest prototypes

var_dump($moderator);
var_dump($plainOldMember);
var_dump($justGuest);

Firstly, there is a SuperAdminPrototype class, which has some specific configuration. As we can see SuperAdmins will have very wide scope of permissions.

Secondly, there is a AdminPrototype class, with different configuration, but it also implements __clone() function. We continue with ModeratorPrototype, MemberPrototype and UserPrototype classes.

Finally, we create several different objects. Notice that we are using clone function instead of “new” directive.

When we do a var_dump to show the content of the variables all configuration data is properly cloned into them.

This is the result of execution

=== Examples of Prototype pattern === 
object(SuperAdminPrototype)#6 (11) {
  ["typeId":protected]=>
  int(1)
  ["typeName":protected]=>
  NULL
  ["adminAccess":protected]=>
  NULL
  ["databaseAccess":protected]=>
  bool(true)
  ["securityLogAccess":protected]=>
  bool(true)
  ["permissions":protected]=>
  array(9) {
    [0]=>
    string(11) "create-user"
    [1]=>
    string(11) "delete-user"
    [2]=>
    string(26) "absolutely-all-permissions"
    [3]=>
    string(12) "rm rf system"
    [4]=>
    string(9) "read-logs"
    [5]=>
    string(13) "delete-admins"
    [6]=>
    string(16) "moderate-content"
    [7]=>
    string(14) "create-content"
    [8]=>
    string(12) "read-content"
  }
  ["username":protected]=>
  string(23) "fancySuperAdminUsername"
  ["firstname":protected]=>
  string(5) "Super"
  ["lastname":protected]=>
  string(5) "Admin"
  ["type"]=>
  string(18) "SuperAdministrator"
  ["adminAcces"]=>
  bool(true)
}
object(AdminPrototype)#7 (11) {
  ["typeId":protected]=>
  int(2)
  ["typeName":protected]=>
  NULL
  ["adminAccess":protected]=>
  NULL
  ["databaseAccess":protected]=>
  bool(true)
  ["securityLogAccess":protected]=>
  bool(true)
  ["permissions":protected]=>
  array(6) {
    [0]=>
    string(11) "create-user"
    [1]=>
    string(11) "delete-user"
    [2]=>
    string(9) "read-logs"
    [3]=>
    string(16) "moderate-content"
    [4]=>
    string(14) "create-content"
    [5]=>
    string(12) "read-content"
  }
  ["username":protected]=>
  string(17) "fancyAdminNameNo1"
  ["firstname":protected]=>
  string(5) "Admin"
  ["lastname":protected]=>
  string(3) "001"
  ["type"]=>
  string(13) "Administrator"
  ["adminAcces"]=>
  bool(true)
}
object(AdminPrototype)#8 (11) {
  ["typeId":protected]=>
  int(2)
  ["typeName":protected]=>
  NULL
  ["adminAccess":protected]=>
  NULL
  ["databaseAccess":protected]=>
  bool(true)
  ["securityLogAccess":protected]=>
  bool(true)
  ["permissions":protected]=>
  array(6) {
    [0]=>
    string(11) "create-user"
    [1]=>
    string(11) "delete-user"
    [2]=>
    string(9) "read-logs"
    [3]=>
    string(16) "moderate-content"
    [4]=>
    string(14) "create-content"
    [5]=>
    string(12) "read-content"
  }
  ["username":protected]=>
  string(17) "fancyAdminNameNo2"
  ["firstname":protected]=>
  string(5) "Admin"
  ["lastname":protected]=>
  string(3) "002"
  ["type"]=>
  string(13) "Administrator"
  ["adminAcces"]=>
  bool(true)
}
object(ModeratorPrototype)#9 (11) {
  ["typeId":protected]=>
  int(3)
  ["typeName":protected]=>
  NULL
  ["adminAccess":protected]=>
  NULL
  ["databaseAccess":protected]=>
  bool(true)
  ["securityLogAccess":protected]=>
  bool(true)
  ["permissions":protected]=>
  array(3) {
    [0]=>
    string(16) "moderate-content"
    [1]=>
    string(14) "create-content"
    [2]=>
    string(12) "read-content"
  }
  ["username":protected]=>
  string(22) "fancyModeratorUsername"
  ["firstname":protected]=>
  string(9) "Moderator"
  ["lastname":protected]=>
  string(9) "The Great"
  ["type"]=>
  string(9) "Moderator"
  ["adminAcces"]=>
  bool(false)
}
object(MemberPrototype)#10 (11) {
  ["typeId":protected]=>
  int(5)
  ["typeName":protected]=>
  NULL
  ["adminAccess":protected]=>
  NULL
  ["databaseAccess":protected]=>
  bool(false)
  ["securityLogAccess":protected]=>
  bool(false)
  ["permissions":protected]=>
  array(2) {
    [0]=>
    string(14) "create-content"
    [1]=>
    string(12) "read-content"
  }
  ["username":protected]=>
  string(22) "PlainOldMemberUsername"
  ["firstname":protected]=>
  string(8) "PlainOld"
  ["lastname":protected]=>
  string(6) "Member"
  ["type"]=>
  string(6) "Member"
  ["adminAcces"]=>
  bool(false)
}
object(GuestPrototype)#11 (11) {
  ["typeId":protected]=>
  int(6)
  ["typeName":protected]=>
  NULL
  ["adminAccess":protected]=>
  NULL
  ["databaseAccess":protected]=>
  bool(false)
  ["securityLogAccess":protected]=>
  bool(false)
  ["permissions":protected]=>
  array(1) {
    [0]=>
    string(12) "read-content"
  }
  ["username":protected]=>
  string(7) "JaneDoe"
  ["firstname":protected]=>
  string(4) "Jane"
  ["lastname":protected]=>
  string(3) "Doe"
  ["type"]=>
  string(5) "Guest"
  ["adminAcces"]=>
  bool(false)
}
milan.latinovic

Senior PHP Engineer and Enterprise Architect at apilayer GmbH. Topics of interest: Software development, PHP, Java, Python, REST API, OpenApi, MySQL, Microservices, Integrations, Interfaces, Interoperability, Processes, Solution Architecture, LDAP, Azure

Recent Posts

Code Review Best Practices: Code reviewing and being reviewed

This article is about the code review best practices. It explains code review from the… Read More

2 years ago

What are the best Practices in REST API design

API design is an important aspect of modern software development. It allows different systems to… Read More

2 years ago

Next Industrial revolution: What is ChatGPT? Will it replace jobs?

This article sheds some light related to the question will ChatGPT or AIs in general… Read More

2 years ago

What is new in PHP 8.2: What are new features, what is deprecated?

This article provides an overview of new features and deprecations in PHP 8.2. PHP 8.0… Read More

2 years ago

Automation and AI in Software Engineering

This article is about Automation and Artificial Intelligence in Software Engineering: Experiences, Challenges, and Opportunities.… Read More

4 years ago

Enumerations in PHP 8.1 – with code example and references

PHP is getting more and more features. Enumerations in PHP are one of the latest… Read More

4 years ago