Department of Software Engineering
Mehran University of Engineering and Technology, Jamshoro
SW227 – SOFTWARE DESIGN AND ARCHITECTURE
Instructor Ms. Afifah Jalbani Practical/Lab No. 09
Date CLOs 3
Signature Assessment Score 1 Mark
Topic
Objectives - To implement Decorator Pattern
Lab Discussion: Theoretical concepts and Procedural steps
Theory:
The Decorator Design Pattern falls under the category of Structural Design Patterns in software
design. Decorator Design Pattern enables behavior to be added to individual objects, either statically
at compile time or dynamically at runtime, without affecting the behavior of other objects from the
same class.
Key Concepts:
1. Statically (Compile-time):
At compile-time, you can wrap objects with specific decorators. This gives you the
flexibility to add behaviors based on how you wrap the object in code.
For instance, in the example of cars, you might create a SportsCar or a LuxuryCar
object by wrapping a BasicCar. This is static because the decorators are added in code
and remain unchanged during execution.
2. Dynamically (Runtime):
During runtime, you can change how objects behave by wrapping them with different
decorators dynamically. This allows you to apply new behaviors to objects as the
program runs, based on conditions.
For example, you might decide at runtime to convert a BasicCar into a LuxuryCar by
wrapping it with a LuxuryCar decorator, or you can remove the SportsCar decorator if
needed, making the car behave like a regular BasicCar.
Intent: The intent of the Decorator design pattern is to add behavior to an individual object, either
statically or dynamically, without affecting the behavior of other objects from the same class. It
allows for more flexibility and extensibility than static inheritance because it allows behavior to be
added dynamically at runtime. The pattern follows the Open-Closed Principle, which states that
classes should be open for extension but closed for modification. The pattern also allows for a single
responsibility principle, which separates the responsibility for adding behavior from the
responsibility for implementing the behavior.
Also known as: Wrapper.
Motivation
1
The motivation for the Decorator design pattern is to provide a flexible and dynamic way of
adding behavior to an individual object, without modifying the original class or affecting the
behavior of other objects from the same class. This pattern is motivated by the need for extensibility
and flexibility in software design, especially when creating complex systems that require adding
new behavior to existing classes without breaking existing code..
Applicability
The Decorator design pattern is applicable when new behavior needs to be added
dynamically to an individual object, without modifying the original class or affecting other objects.
It is useful when creating complex systems that require multiple behavior combinations. The pattern
is also applicable when creating subclasses for every possible behavior combination is not feasible.
Additionally, it is suitable for adhering to the Open-Closed and Single Responsibility principles.
Overall, the Decorator pattern is an effective way to create flexible and extensible software designs.
Structure
We use inheritance or composition to extend the behavior of an object but this is done at
compile time and its applicable to all the instances of the class. We can’t add any new functionality
of remove any existing behavior at runtime - this is when Decorator pattern comes into picture.
Suppose we want to implement different kinds of cars - we can create interface Car to define the
assemble method and then we can have a Basic car, further more we can extend it to Sports car and
Luxury Car. The implementation hierarchy will look like below image.
But if we want to get a car at runtime that has both the features of sports car and luxury car,
then the implementation gets complex and if further more we want to specify which features should
be added first, it gets even more complex. Now imagine if we have ten different kinds of cars, the
implementation logic using inheritance and composition will be impossible to manage. To solve this
kind of programming situation, we apply decorator pattern in java. We need to have following types
to implement decorator design pattern.
Participants
1. Component - The Component is an interface (or abstract class) that defines the common
operations or behavior for objects that can be "decorated." It provides a common type that both
the ConcreteComponent and Decorators must implement or inherit from.
Role: The Component defines the interface that all concrete components and decorators must
adhere to, ensuring that they can be treated uniformly. In other words, both the basic objects and
2
decorated objects share the same interface, allowing the client to interact with either in the same
way.
Purpose: The Component is the abstraction for all objects that can have responsibilities added to
them, which is critical for the pattern. It allows decorators to extend the behavior of concrete
components without needing to modify their structure.
2. ConcreteComponent - The ConcreteComponent is the actual object that will be "decorated"
with additional responsibilities or features. It implements the Component interface and provides
the base behavior. This is the core object whose behavior is extended by decorators.
Role: This class provides the basic functionality and serves as the object to which decorators
will add responsibilities dynamically. It is an instance of a component that can be extended by
various decorators without changing its code.
Purpose: It defines the primary behavior and serves as the foundation on which decorators can
build. This is where the default (or core) behavior is defined, and it will be wrapped by
decorators to extend or enhance functionality.
3. Decorator - The Decorator is an abstract class or interface that implements the Component
interface. It maintains a reference to a Component object and forwards requests to the
ConcreteComponent. The Decorator adds additional behavior either before or after forwarding
the request to the ConcreteComponent.
Role: The Decorator class maintains a reference to a Component object, and through
delegation, it adds behavior on top of that component. It forwards any method calls to the
underlying component but may add extra logic to enhance or modify the behavior of the
component.
Purpose: The Decorator provides a flexible alternative to subclassing for extending
functionality. Instead of creating subclasses of a component to add behavior, decorators wrap
components and add behavior dynamically, allowing for more flexible and modular designs.
4. Concrete Decorators - Concrete Decorators are the classes that extend the Decorator class.
They add responsibilities (state or behavior) to the ConcreteComponent at runtime. Each
ConcreteDecorator provides additional functionality or modifies the behavior of the component
in a specific way.
Role: Concrete decorators extend the functionality of the component by adding their own
behavior either before or after delegating to the underlying Component object. Each concrete
decorator enhances the component in a unique way, such as adding additional state or modifying
method behavior.
Purpose: Concrete decorators are responsible for adding new functionality to the objects they
wrap. You can combine multiple concrete decorators to achieve more complex behavior by
layering decorators on top of one another.
3
Decorator Design Pattern - Class Diagram
The Decorator Design Pattern Class Diagram illustrates how the pattern works by adding
responsibilities to objects dynamically without altering their structure.
1. Car (Component Interface)
The Car interface represents the Component, which defines the basic contract (method) for
all car objects.
Method:
o assemble(): This method is the primary behavior of the Car that will be modified or
extended by decorators.
Role: It serves as the base interface that will be implemented by both concrete components
and decorators. All car objects will implement this interface and provide their version of the
assemble() method.
2. BasicCar (Concrete Component)
BasicCar is the Concrete Component that implements the Car interface. It provides the
core functionality that can be extended by decorators.
Method:
o assemble(): It implements the basic assembly behavior of the car.
4
Role: The BasicCar defines the base behavior (in this case, assembling a basic car) that can
be enhanced or modified by decorators. This class doesn’t offer any special features or
modifications but represents the default state of a car.
3. CarDecorator (Abstract Decorator)
The CarDecorator is the Decorator class that also implements the Car interface. It acts as a
wrapper around a Car object.
Attributes:
o #car: It holds a reference to a Car object (i.e., it is wrapping a car, which could be a
BasicCar or another decorated car).
Constructor:
o CarDecorator(Car): The constructor accepts a Car object and assigns it to the car
field. This is where the decorator wraps the component.
Method:
o assemble(): The decorator's version of assemble() calls the assemble() method of
the wrapped Car object. This allows decorators to extend or enhance the functionality
of the object while keeping the existing behavior intact.
Role: This class provides a base implementation for decorators. It forwards method calls to
the wrapped component (car) and can allow additional behavior to be added in concrete
decorator subclasses.
4. LuxuryCar (Concrete Decorator)
LuxuryCar is a Concrete Decorator that extends the CarDecorator and adds luxury
features to the car.
Constructor:
o LuxuryCar(Car): The constructor takes a Car object and passes it to the
CarDecorator superclass.
Method:
o assemble(): This method first calls the assemble() method of the CarDecorator
(which, in turn, calls the wrapped car's assemble() method) and then adds luxury
features to the car by appending behavior.
Role: LuxuryCar extends the functionality of the BasicCar (or any other car) by adding
luxury features (e.g., leather seats, advanced infotainment). It does this dynamically by
wrapping the component, not by subclassing it directly.
5. SportsCar (Concrete Decorator)
SportsCar is another Concrete Decorator that extends the CarDecorator and adds sports
car features.
Constructor:
o SportsCar(Car): This constructor wraps a Car object and passes it to the
CarDecorator superclass.
Method:
o assemble(): Similar to LuxuryCar, it first calls the assemble() method of the
wrapped car and then adds specific features of a sports car, such as better
aerodynamics or a sport-tuned suspension.
Role: This decorator adds behavior and characteristics specific to sports cars to any car
object it decorates, enhancing the base car with additional functionality.
5
How It Works (Collaborations)
1. BasicCar Creation: The BasicCar object is created, which is a simple car with no extra
features.
2. Decorating with SportsCar: A SportsCar decorator is wrapped around the BasicCar. This
adds sports features to the basic car.
3. Decorating with LuxuryCar: A LuxuryCar decorator is then wrapped around the
SportsCar-decorated car. Now, the car has both sports and luxury features.
This pattern allows you to stack multiple decorators on top of each other, dynamically adding more
features or behavior to the same car object, all without modifying the core component ( BasicCar).
Sample Code (Implementation)
Component Interface - The interface or abstract class defining the methods that will be
implemented. In our case Car will be the component interface.
Component Implementation - The basic implementation of the component interface. We can have
BasicCar class as our component implementation.s
Decorator - Decorator class implements the component interface and it has a HAS-A relationship
with the component interface. The component variable should be accessible to the child decorator
classes, so we will make this variable protected.
6
Concrete Decorator - Extending the base decorator functionality and modifying the component
behavior accordingly. We can have concrete decorator classes as LuxuryCar and SportsCar.
Decorator Design Pattern Test Program
7
Notice that Main program can create different kinds of Object at runtime and they can specify the
order of execution too.
Lab Tasks:
1. Develop an application for a pizza store by implementation of Decorator Pattern. Assume
they offer four types of pizzas namely Peppy Paneer, Farmhouse, Margherita and Chicken
Fiesta.
2. Implement a simple coffee ordering system that allows the user to choose from different
types of coffee and add various condiments to it using the Decorator Design Pattern.