CSE327
Software Engineering
AKM IQTIDAR NEWAZ [IQN]
LECTURE - 11
Decorator Design Pattern
Decorator is a structural design pattern that lets you attach new behaviors to objects by
placing these objects inside special wrapper objects that contains the behaviors.
Suppose, we have a base object where that
base object has some features/properties,
and if we want to add more features the
we need to use decorator for wrapping the
F1 and it is also an object and we can still F1
use a wrapper of decorator for adding new
features. F1 + F2
F1 + F2 + F3 + ... + Fn
UML Diagram of Decorator Pattern
Component Interface:
Defines the base functionality.
Represented as an interface in UML.
ConcreteComponent Class:
Implement the Component interface.
Provides the core functionality.
Decorator Abstract Class:
Implement the Component interface.
Contains a reference to a Component object.
Adds functionality by delegating calls to the wrapped component
ConcreteDecorator Class:
Extend the Decorator class.
Add specific behavior while maintaining the base functionality.
Basic Example - 1
• Consider a Window in a user interface as the Component.
• We want to add functionalities like scrollbars or a border without redesigning the window itself.
• In this example, SimpleWindow is a Concrete Component that can be decorated with additional
features.
• WindowDecorator is a decorator that holds a reference to a Window object and delegates the
draw operation to it.
• ScrollbarWindow is a Concrete Decorator that adds a scrollbar to the window.
Basic Example – 1 (Code)
class WindowDecorator : public Window // Decorator
#include <iostream> {
using namespace std; main.cpp public:
WindowDecorator(Window *window) : window_(window) {}
class Window // Component
{
void draw() override main.cpp
{
public: window_->draw(); // Delegate to the component
virtual void draw() = 0; }
virtual ~Window() = default;
}; protected:
Window *window_;
};
class SimpleWindow : public Window // Concrete
Component class ScrollbarWindow : public WindowDecorator // Concrete
{ Decorator
public: {
void draw() override public:
ScrollbarWindow(Window *window) : WindowDecorator(window)
{
{}
cout << "Drawing SimpleWindow()" << endl;
} void draw() override
}; {
WindowDecorator ::draw(); // Draw the window
drawScrollbar(); // Add scrollbar
}
private:
void drawScrollbar()
{
cout << "Drawing ScrollbarWindow()" << endl;
}
};
Basic Example – 1 (Code)
int main()
{ Output:
cout << "Concrete Component:\n"; Concrete Component:
Window *simpleWindow = new SimpleWindow(); Drawing SimpleWindow()
simpleWindow->draw();
cout << "\nDecorator or Wrapper object:\n";
Window *simpleWindow2 = new SimpleWindow(); Decorator or Wrapper object:
Window *scrollbar = new Drawing SimpleWindow()
ScrollbarWindow(simpleWindow2); Drawing ScrollbarWindow()
scrollbar->draw();
}
Basic Example – 2
Let’s consider a text editor application where users can write and format their text.
Initially, the editor offers basic text operations.
However, as an intermediate developer, we want to add functionalities like spell check, grammar
check, and auto-correct without altering the core text processing component.
In this example, BasicEditor provides fundamental text editing functionalities.
Decorators like SpellCheckerDecorator add spell-checking capabilities on top of the basic text-
writing functionality without changing the BasicEditor class.
Basic Example – 2 (Code) class EditorDecorator : public TextEditor // Decorator
{
#include <iostream> main.cpp public:
#include <string> EditorDecorator(TextEditor *editor) : editor_(editor) {}
using namespace std;
void write(const string &words) override main.cpp
class TextEditor // Component {
{ editor_->write(words); // Delegate to the component
public: }
virtual void write(const string &words) = 0;
protected:
virtual ~TextEditor() = default; TextEditor *editor_;
}; };
class BasicEditor : public TextEditor // Concrete Component class SpellCheckerDecorator : public EditorDecorator // Concrete Decorator
{ {
public: public:
void write(const string &words) override SpellCheckerDecorator(TextEditor *editor) : EditorDecorator(editor) {}
{
cout << "Performing Basic Editor Functionality!" << void write(const string &words) override
{
endl;
EditorDecorator::write(words); // Write text
} spellCheck(words); // Add spell checking
}; }
private:
void spellCheck(const string &words)
{
cout << "Performing the spell checking here!!" << endl;
}
};
Basic Example – 2 (Code)
int main()
main.cpp
{
TextEditor *editor = new BasicEditor();
editor->write(" CSE-327 "); Output:
cout << "\nAfter performing Decorator Performing Basic Editor Functionality!
wrapping:\n";
TextEditor *editor2 = new BasicEditor(); After performing Decorator wrapping:
SpellCheckerDecorator *spellChecker = new Performing Basic Editor Functionality!
SpellCheckerDecorator(editor2);
Performing the spell checking here!!
spellChecker->write(" CSE-327 ");
}
Example – 3 (Order Sandwich)
Suppose we’re building a sandwich shop where the user has the ability to
choose the base of the sandwich, then add sides and condiments onto it.
The base of the sandwich has a basic version, which uses regular bread
and meat, where as the deluxe version uses organic bread and meat, along
with cheese and lettuce in it.
After the customer choose the base, and selects the additional sides and
condiments, the sandwich shop will return the total cost and the various
ingredients that went into sandwich.
Example – 3 (Code)
#pragma once
#pragma once #include "sandwichOrder.h"
#include <string> sandwichOrder.h
#include <iostream> class BasicSandwich : public SandwichOrder // Concrete
using namespace std; Component
class SandwichOrder // Component
{ basicSandwich.h
public:
{ int cost = 5;
public: int GetCost() const override
virtual int GetCost() const = 0; {
virtual string GetIngredient() const = return cost;
0; }
}; string GetIngredient() const override
{
return "Basic Bread and Meat";
}
};
Example – 3 (Code)
#pragma once decorator.h
#pragma once
#include "sandwichOrder.h"
#include "sandwichOrder.h"
class Decorator : public SandwichOrder //
class DeluxeSandwich : public SandwichOrder // Concrete
Decorator
Component
{
{ deluxeSandwich.h public:
public:
SandwichOrder *order_;
int cost = 8;
int GetCost() const override
Decorator(SandwichOrder *order) :
{
order_(order) {}
return cost;
int GetCost() const override
}
{
string GetIngredient() const override
return order_->GetCost();
{
}
return "Organic Bread, Organic Meat, Cheese,
veggie";
string GetIngredient() const override
}
{
};
return order_->GetIngredient();
}
};
Example – 3 (Code)
#pragma once #pragma once
#include "decorator.h" #include "decorator.h"
sideDecorator.h
class CondimentDecorator : public Decorator class SideDecorator : public Decorator
{ {
public: public:
string condiment_;
condimentDecorator.h string side_;
int cost_ = 1; int cost = 2;
SideDecorator(string side, SandwichOrder *order) :
CondimentDecorator(string condiment, SandwichOrder side_(side), Decorator(order) {}
*order) : condiment_(condiment), Decorator(order) {}
int GetCost() const override
int GetCost() const override {
{ return cost + Decorator::GetCost();
return cost_ + Decorator ::GetCost(); }
}
string GetIngredient() const override
string GetIngredient() const override {
{ return side_ + " " + order_->GetIngredient();
return condiment_ + " " + order_->GetIngredient(); }
} };
};
Example – 3 (Code)
#include "condimentDecorator.h"
#include "sideDecorator.h" main.cpp
#include "deluxeSandwich.h"
#include "basicSandwich.h"
#include <iostream>
using namespace std;
void ServeOrder(SandwichOrder *order)
{
std::cout << "Total Cost: " << order->GetCost() << std::endl;
std::cout << "Ingredients: " << order->GetIngredient() <<
std::endl; Output:
} Total Cost: 14
int main() Ingredients: onion mustard pickle mayo Organic Bread, Organic Meat, Cheese, veggie
{
SandwichOrder *sandwich1 = new DeluxeSandwich;
SandwichOrder *decorated1 = new CondimentDecorator("mayo",
sandwich1);
SandwichOrder *decorated2 = new SideDecorator("pickle",
decorated1);
SandwichOrder *decorated3 = new CondimentDecorator("mustard",
decorated2);
SandwichOrder *final_order = new SideDecorator("onion",
decorated3);
ServeOrder(final_order);
return 0;
}