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.
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:
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)
}
This article is about the code review best practices. It explains code review from the… Read More
API design is an important aspect of modern software development. It allows different systems to… Read More
This article sheds some light related to the question will ChatGPT or AIs in general… Read More
This article provides an overview of new features and deprecations in PHP 8.2. PHP 8.0… Read More
This article is about Automation and Artificial Intelligence in Software Engineering: Experiences, Challenges, and Opportunities.… Read More
PHP is getting more and more features. Enumerations in PHP are one of the latest… Read More