Structural pattern used to add new functionality to existing objects
- using JavaScript we can extend classes whenever we want, but the decorator pattern is still used to minimize the amount of sub-classes we will need
Use-Case
User class represents a member of staff at an organization. All members have read access to documents, but administrators have right access and other behaviors and executives need access to private docs. If we used sub-class method, we would already have a huge anough of sub classes with this simple example. So instead, we use decorators
We have a simple base class which we need to use to build different objects from. Some will need certain behaviors, some will need different ones, and some wont need any
- we can create different sub class for every combination of behaviors, but this is hard to maintain as project grows
- we will use decorators for each variation and then apply them to objects as required
Step 1 – Page: user.js
// JavaScript Document
define(function () {
'use strict';
var User = function (id) { //create a simple constructor
this.id = id;
this.getPermissions = function () { //user objects have a method called getPermissions, which details their level of access to the application
return 'public:read';
};
};
User.prototype.decoratePermissions = function (decorator) { //user objects also have method for user objects to inherit permissions
this.getPermissions = decorator.getPermissions; //this method is passed new decorator, and overrights getPermissions property
};
return User; //return the constructor
});
Step 2 – Page: exec.js
// JavaScript Document
define(function () {
'use strict';
return {
getPermissions: function () { //the exec decorator returns a method fo switch teh getPermissions param to confidential:read
return 'public:read, condidential:read';
}
};
});
Step 3 – Page: admin.js
// JavaScript Document
define(function () {
'use strict';
return {
getPermissions: function () { //the admin decorator returns a method fo switch the getPermissions param to confidential:write
return 'public:read, condidential:write';
}
};
});
Step 4 – Page: init.js
define(function(require) {
'use strict';
return {
init: function() {
var user1, user2, //set up 2 users
User = require('decorator/user'), //require in the User constructor and the 2 decorators
execDecorator = require('decorator/decorators/exec'),
adminDecorator = require('decorator/decorators/admin');
user1 = new User('user1'); //new user should have public read permissions
user1.decoratePermissions(execDecorator); //this will change the user status to exec, and give the confidential:read priviledges
user2 = new User('user2'); //set up new user2
user2.decoratePermissions(adminDecorator); //give admin privs
console.log(user1.getPermissions());
console.log(user2.getPermissions());
}
};
});
Step 5 – Page: main.js
// JavaScript Document
require (
['decorator/init'],
function (decorator) {
'use strict';
var examples = {
decorator:decorator
};
window.runExample = function (example) {
examples[example].init();
};
}
);
Conclusion
We looked at simple decorator pattern
- used to add new behavior to objects
- minimizes the number of sub-classes an application must maintain