To extend or modify the behavior of an instance at run-time decorator design pattern is used. Decorating can provide new behavior at run-time for individual objects. Inheritance is used to extend the functionalities of a class. But if you need to add behavior not without changing object, you can do it with decorator pattern.
Let's imagine a scenario. We have a burger shop. Customer can order burger with cheese, french fries, soft drinks etc. We have a component or base class named "Burger". If we call this, we will get only burger. If we also need cheese, french fries or soft drinks, we will decorate them with our burger component.
To do this we need a component class named "Burger" and Decorator class named "Cheese", "French fries" and "Soft drinks". This decorators class will extend the behavior of component "Burger" class and in run time add decorated behavior to components.
Declaration of component class "Burger" goes here:
#import <Foundation/Foundation.h>
@interface Burger : NSObject
-(NSString *)getDescription;
-(double)getPrice;
@end
Implementation of component class "Burger" goes here:
#define burgerPrice 120.0
#import "Burger.h"
@implementation Burger
-(id)init
{
self=[super init];
if (self) {
//do initialization
}
return self;
}
//returns the description of burger
-(NSString *)getDescription
{
return @"Burger";
}
//return price of burger
-(double)getPrice
{
return burgerPrice;
}
@end
//Declaration of Cheese decorator class
#import "Burger.h"
@interface Cheese : Burger
{
@private Burger *burger;
}
@property Burger *burger;
-(id)initWithBurger:(Burger *)theBurger;
@end
//implementation of Cheese class
#import "Cheese.h"
#define cheesePrice 30.0;
@implementation Cheese
@synthesize burger;
-(id)initWithBurger:(Burger *)theBurger
{
self=[super init];
if (self)
{
self.burger=theBurger;
}
return self;
}
//Adding cheese description
-(NSString *)getDescription
{
return [NSString stringWithFormat:@"%@ ,cheese",self.burger.getDescription];
}
//Adding cheese price
-(double)getPrice
{
return self.burger.getPrice+cheesePrice;
}
@end
//Declaration of French fries class
#import "Burger.h"
@interface FrenchFries : Burger
{
Burger *burger;
}
@property Burger *burger;
-(id)initWithBurger:(Burger *)theBurger;
@end
//Implementation of French fries decorators
#import "FrenchFries.h"
#define frenchFriesPries 20;
@implementation FrenchFries
@synthesize burger;
-(id)initWithBurger:(Burger *)theBurger
{
self=[super init];
if (self)
{
self.burger=theBurger;
}
return self;
}
//Add description of French fries
-(NSString *)getDescription
{
return [NSString stringWithFormat:@"%@, frenc fries",self.burger.getDescription];
}
//Add price of French fries
-(double)getPrice
{
return self.burger.getPrice+frenchFriesPries;
}
@end
//Declaration of Soft drinks decorators
#import "Burger.h"
@interface SoftDrinks : Burger
{
@private Burger *burger;
}
@property Burger *burger;
-(id)initWithBurger:(Burger *)theBurger;
@end
//Implementation of Soft drinks decorators
#import "SoftDrinks.h"
#define softDrinksPrice 50
@implementation SoftDrinks
@synthesize burger;
-(id)initWithBurger:(Burger *)theBurger
{
self=[super init];
if (self)
{
self.burger=theBurger;
}
return self;
}
//Add description of softdrinks
-(NSString *)getDescription
{
return [NSString stringWithFormat:@"%@, soft drinks",self.burger.getDescription];
}
//Add price of softdrinks
-(double)getPrice
{
return self.burger.getPrice+softDrinksPrice;
}
@end
Here's a test program that creates a Burger instance which is fully decorated (i.e., with cheese, french fries, soft drinks), and calculate cost of Burger:
Burger *burger=[[Burger alloc] init];
NSLog(@"Ordered item: %@ price: %lf",burger.getDescription,burger.getPrice);
Burger *burgerWithCheese=[[Cheese alloc] initWithBurger:burger];
NSLog(@"Ordered item: %@ price: %lf",burgerWithCheese.getDescription,burgerWithCheese.getPrice);
Burger *burgerWithCheeseNfrenchFries=[[FrenchFries alloc] initWithBurger:burgerWithCheese];
NSLog(@"Ordered item: %@ price: %lf",burgerWithCheeseNfrenchFries.getDescription,burgerWithCheeseNfrenchFries.getPrice);
Burger *BurgerWithAll=[[SoftDrinks alloc] initWithBurger:burgerWithCheeseNfrenchFries];
NSLog(@"Ordered item: %@ price %lf",BurgerWithAll.getDescription,BurgerWithAll.getPrice);
The output of this program is given below:
Ordered item: Burger price: 120.000000
Ordered item: Burger ,cheese price: 150.000000
Ordered item: Burger ,cheese, frenc fries price: 170.000000
Ordered item: Burger ,cheese, frenc fries, soft drinks price 220.000000