Decorator Design Pattern

Decorator Design pattern provides a way to add additional functionality to an object without modifying it. The additional functionality is added to the object at runtime and it does not require extending the class.

Decorators have the same interface as the object they are decorating. Their purpose is to provide additional features at runtime and they can also be chained. In other words you can combine multiple decorators and they can be in any order. To understand it further, lets see it in action.

Example of Decorator Design Pattern

Imagine a linkedin type system where each person has a profile. In our example the person can either be a college student, a fresher in a company or an experienced person in a company. Our aim is to provide ways to print the profile of the person. While printing profile we can use extra decorators that print some more information about the college that a student has gone to or a company that an employee is currently working with. For a senior employee we are not really concerned about the college but only the company that he is working for and the number of years of experience.

There are a number of ways to achieve this objective. We could keep on extending classes, but we really want this to be dynamic and we also want to minimize the number of classes. Lets see how we would do this with the decorator pattern.

Before we look at the classes though, lets see the various parts of the decorator pattern

  • component– This defines an interface that we are trying to decorate or add features to.
  • concrete component– This is the object to which we dynamically add functionalities
  • Decorator– This will be extended by the various decorators. It conforms to the Component interface and also stores the reference to the Concrete Component.
  • concrete Decorator– This are the actual implementations of the decorators.

Observe that by design we could keep on adding decorators and also pass one decorator to another. Each decorator would add some functionality before or after calling its parent method.

Here are the various classes.

The client creates the various profiles (student, fresher, employee). It also passes the profiles through various printer decorators to print additional information.

public class Client
{
	public static void main(String[] args)
	{
		Profile studentProfile = new StudentProfile("John", "john@stanfordd.com", "Stanford");
		Profile fresherProfile = new FresherEmployeeProfile("Darcy", "Darcy@ibmm.com", "harvard",
				"ibm");
		Profile seniorEmployeeProfile = new SeniorEmployeeProfile("Bill", "bill@microsoftt.com",
				"yale", 8, "microsoft");

		studentProfile = new CollegeInfoProfilePrinterDecorator(studentProfile);
		studentProfile.printProfile();

		fresherProfile = new CollegeInfoProfilePrinterDecorator(
				new CompanyInfoProfilePrinterDecorator(fresherProfile));
		fresherProfile.printProfile();

		seniorEmployeeProfile = new CompanyInfoProfilePrinterDecorator(seniorEmployeeProfile);
		seniorEmployeeProfile.printProfile();

	}
}
					

The student, fresher and experience employee profiles and the Profile interface


public abstract class Profile
{
	String name;
	String email;
	String currentEducation;
	String highestEducation;
	String currentCompany;
	int experience;

	public abstract void printProfile();
}

public class StudentProfile extends Profile
{

	public StudentProfile(String name, String email, String currentEducation)
	{
		this.name = name;
		this.email = email;
		this.currentEducation = currentEducation;
	}

	@Override
	public void printProfile()
	{
		System.out.println(name + "," + email + "," + currentEducation);
	}

}

public class FresherEmployeeProfile extends Profile
{

	public FresherEmployeeProfile(String name, String email, String highestEducation,
			String currentCompany)
	{
		this.name = name;
		this.email = email;
		this.highestEducation = highestEducation;
		this.currentCompany = currentCompany;
	}

	@Override
	public void printProfile()
	{
		System.out.println(name + "," + email + "," + highestEducation + "," + currentCompany);

	}

}

public class SeniorEmployeeProfile extends Profile
{

	public SeniorEmployeeProfile(String name, String email, String highestEducation,
			int experience, String currentCompany)
	{
		this.name = name;
		this.email = email;
		this.highestEducation = highestEducation;
		this.experience = experience;
		this.currentCompany = currentCompany;
	}

	@Override
	public void printProfile()
	{
		System.out.println(name + "," + email + "," + highestEducation + "," + experience + ","
				+ currentCompany);
	}

}
					

The various decorators. The abstract class is the ProfilePrinter that holds a reference to the profile. The company info and college info decorators get additional info. You can pass the profile through one or more decorators and in any order. You can also add more decorators if required.


public abstract class ProfilePrinter extends Profile
{
	Profile profile;
}

public class CollegeInfoProfilePrinterDecorator extends ProfilePrinter
{

	public CollegeInfoProfilePrinterDecorator(Profile profile)
	{
		this.profile = profile;
	}

	@Override
	public void printProfile()
	{
		profile.printProfile();
		// get college information from college directory
		if (profile.currentEducation != null)
			System.out.println("Enhanced info for " + profile.currentEducation);
	}
}

package com.studytrails.patterns.decorator;

public class CompanyInfoProfilePrinterDecorator extends ProfilePrinter
{


	public CompanyInfoProfilePrinterDecorator(Profile profile)
	{
		this.profile = profile;
	}

	@Override
	public void printProfile()
	{
		profile.printProfile();
		// get company information from company directory
		if (profile.currentCompany != null)
			System.out.println("Enhanced info for " + profile.currentCompany);
	}

}

					

Class Diagram of Decorator Design Pattern

Lets look at the class diagram

Decorator Design Pattern

Here’s an explanation of the example : The Profile class is the interface that all the other classes implement. It defines the profile of a person that is registered on our system. The person who is registering could be a student, a fresher or an experience person. We create classes for all three. We could then simply call the printProfile methods of these classes to print the profile, but want also want to add some more features to our system. We want to be able to print some info about the college or the company. We do this by creating decorators that take in the profiles, call the print method of the profile and then appends something more to what is already printed by the profiles (college or company info.)

Before we wrap up, here are the salient features of the decorator pattern.

  • Decorator pattern adds functionality to an object without modifying it.
  • Decorator pattern can be used when extending classes creates a profusion of classes, i.e. a lot of classes have to be created to cover all the use cases. The fact that decorators can be chained can be useful in achieving a high number of combinations of functionalities.
  • The decorators have to implement the interface that they are decorating and hence it would be good to keep those interfaces light weight.

Leave a Comment