Name: Decorator
Type: Structural Pattern
Decorator pattern allows to add state and behavior to an object at runtime.It’s natural to compare this pattern with inheritance.State and behaviour can also be added through inheritance.
Here we will see the use and example of Decorator Pattern in C#.
Suppose we have an existing class and we want to add many different features to this class.One way to implement this scenario is by deriving a new classes for every new feature possible.Though this approach will solve the problem but it will also add lots of new sub classes for every small feature desired.
We can solve this by implementing the decorator pattern
The main differences between inheritance and decorator pattern for adding new behaviour are:
- By implementing Decorator pattern we attach additional responsibilities to an object not to a class unlike inheritance.
- In the case of Decorator pattern the additional responsibilities can be added to an object at run-time or compile time.Whereas in the case of inheritance new behaviour is added to the derived class at compile time.
Following are the steps to implement this pattern
- Declare an abstract class or interface.This will be implemented or inherited by the actual Component class which needs to be decorated or wrapped.
- Declare the Component class which implements the Component abstraction.
- Declare abstract decorator class which implements the Component abstract class or interface.Add a field which will point to the Component base class.In the Decorator constructor or method pass the component as a parameter and Initialize this component field.
- Declare a concrete decorator and Override the required methods of the base Component class or interface.These are the methods which we want to use for adding responsibility to the component.
Decorator pattern can be represented with the following diagram
Example of Decorator Pattern
To understand the real world example of decorator pattern we can take the example of an e-commerce company.Ecommerce companies sell lots of different products and can also add new products from time to time.So using inheritance for adding new products may not be a good solution.To understand why suppose there is an interface defined for a Product as:
public interface Product { string Name(); string Type(); decimal Price(); }
Now a new class can be added for each new product being sold by the company.For example if there are Laptops being sold then a new class called Laptop can be added as:
public class Laptop:Product { private string name="Laptop"; public string Name() { return name; } private string type="Elctronic"; public string Type() { return type; } private readonly decimal price = 5000; public decimal Price() { return price; } }
This may seem to be appropriate solution but just consider how many different types of products might be sold by a company.Also many new types of products will be introduced so this will require lots of subclasses.
A good solution to this problem is using the decorator pattern.
We can define a Product interface.This is the interface which will be implemented by the actual product classes,which are the concrete components here.We have a declared a class ElectronicProduct which represents concrete component.ElectronicProduct class defines the common functionality for all the electronic products.For example it defines the minimum price of an electronic product.
ElectronicProductDecorator is an abstract class which represents an abstract decorator.It defines a field of type Product.The value of this field is passed as a constructor parameter.It also implements the methods of the Product interface.
LaptopDecorator is a specific type of decorator.Its a concrete decorator as it inherits from the ElectronicProductDecorator class.In its constructor it sets the values of ProductName, ProductType and ProductBasePrice defined by the base class ElectronicProductDecorator.These values are used by methods of the base class ElectronicProductDecorator to return the print the values of the respective methods.
Following example declares LaptopDecorator to add extra behaviour to the ElectronicProduct class.
public interface Product { string Name(); string Type(); decimal Price(); } public class ElectronicProduct : Product { public string Name() { return "Electronic Product"; } public string Type() { return "Electronic"; } public decimal Price() { return 5000.0M; } } public abstract class ElectronicProductDecorator:Product { private Product Product; public ElectronicProductDecorator(Product product) { Product = product; } protected string ProductName; public string Name() { return string.Format("Generic Name {0} ,Specific Name {1}", Product.Name() , ProductName); } protected string ProductType; public string Type() { return string.Format("Generic Type {0} ,Specific Type {1}", Product.Type(), ProductType); } protected decimal ProductBasePrice; public decimal Price() { return Product.Price() + ProductBasePrice; } } public class LaptopDecorator : ElectronicProductDecorator { public LaptopDecorator(Product product) : base(product) { ProductName = "Laptop"; ProductType = "Portable Computer"; ProductBasePrice = 20000; } }
The user of the class can create the LaptopDecorator object.The methods of LaptopDecorator will add the required behaviour required by a laptop.
class Program { static void Main(string[] args) { ElectronicProduct electronicProd = new ElectronicProduct(); string name= electronicProd.Name(); string type=electronicProd.Type(); decimal price= electronicProd.Price(); string productDetails = String.Format("ProductName is {0},\ntype is {1},\nPrice is {2} ", name, type, price); Console.WriteLine("Product details before decoration\n{0}", productDetails); LaptopDecorator laptopDecorator = new LaptopDecorator(electronicProd); name = laptopDecorator.Name(); type = laptopDecorator.Type(); price = laptopDecorator.Price(); string decoratedProductDetails = String.Format("{0},\n{1},\nPrice is {2} ", name, type, price); Console.WriteLine("Product details after decoration\n{0}", decoratedProductDetails); Console.ReadLine(); } }
On executing the above example we get the following result
Leave a Reply