You are on page 1of 30

Module 6: Object-Oriented Design – Design Patterns II

Software Engineering
Computer Science
Academic Year 2023/2024

Gerard Albà Soler

1
Contents
1. Introduction
• What will we learn
2. Object Oriented Design
- Design principles
- Design patterns
- Operation contract and Class diagram normalization
- Object responsibilities assignment
- Behavioral modeling. Sequence diagram and State diagram
- Software Architecture
- Components and Packages
1. Introduction
What will we learn?
OOA/D
Design

Design
Exercises
Patterns

OOA/D

Structural Behavioral

Creational
1. Introduction
• Book References

ü Design Patterns: Elements of Reusable Object-Oriented Software. E Gamma


et al. Ed Addison Wesley professional computing series
ü Python: Master the Art of Design Pattern D Phillips et al. Ed Packt
Publishing
2.2 Design Patterns
• Patterns are classified under three main categories:
• Creational patterns
• Structural patterns
• Behavioral patterns

• The classification of patterns is done based primarily on how the objects get
created, how classes and objects are structured in a software application, and also
covers the way objects interact among themselves.
• It is not the aim of this course to learn about all the available patterns. But we will
show some examples of each category in the next sections.
2.2 Design Patterns
Pattern category Description
Creational patterns • They work on the basis of how objects can be created
• They isolate the details of object creation
• Code is independent of the type of object to be created

• They design the structure of objects and classes so that they can compose to
achieve larger results
• The focus is on simplifying the structure and identifying the relationship
Structural patterns between classes and objects
• They focus on class inheritance and composition

• They are concerned with the interaction among objects and responsibility of
Behavioral patterns objects
• Objects should be able to interact and still be loosely coupled
2.2 Design Patterns
Structural patterns: Decorator

• Problem: dynamically attach new behaviors to an object at runtime without


changing their implementation (its class).
• The decorator pattern (or wrapper) allows us to dynamically attach new
behaviors to objects without changing their implementation by placing these
objects inside the wrapper objects that contains the behaviors. It is not
equivalent to the inheritance because the new feature is added only to that
particular object, not to the entire subclass.
2.2 Design Patterns
Structural patterns: Decorator

• In other words, it enables you to “decorate” or enhance objects by adding new


functionalities without modifying their structure.
• It is much easier to implement decorator method in Python because of its built-in
feature. We will first study the classic decorator pattern, and later in this section
we will see a different way in Python of doing it.
2.2 Design Patterns
Structural patterns: Decorator
• A decorator allows us to dynamically add functionality and behavior to an object without
affecting the behavior of other existing objects within the same class. We use inheritance to
extend the behavior of the class.
ü Decorator patterns allow a user to add new functionality to an existing object without
altering its structure. So, there is no change to the original class.
ü The decorator is a structural pattern, which provides a wrapper to the existing class.
ü The decorator uses abstract classes or interfaces to implement the wrapper.
ü Decorators create decorator classes, which wrap the original class and supply additional
functionality by keeping the class methods’ signature (name and parameters) unchanged.
ü Decorator design patterns are most frequently used for applying single responsibility
principles since we divide the functionality into classes with unique areas of concern.
2.2 Design Patterns
Below are some examples of use:
ü Extending Functionality: when we have a base component with basic functionality,
but we need to add additional features or behaviors to it dynamically without altering
its structure. Decorators allow us to add new responsibilities to objects at runtime.
ü Multiple Combinations of Features: when we want to provide multiple combinations
of features or options to an object. Decorators can be stacked and combined in
different ways to create customized variations of objects, providing flexibility to users.
ü Legacy Code Integration: when working with legacy code or third-party libraries where
modifying the existing codebase is not feasible or desirable, decorators can be used to
extend the functionality of existing objects without altering their implementation.
ü GUI Components: in graphical user interface (GUI) development, decorators can be
used to add additional visual effects, such as borders, shadows, or animations, to GUI
components like buttons, panels, or windows.
2.2 Design Patterns
Example of a system problem:

ü Suppose we are building a coffee shop application where customers can order
different types of coffee.
ü Each coffee can have various optional add-ons (toppings) such as milk, sugar,
whipped cream, etc.
ü We want to implement a system where we can dynamically add these add-ons
to a coffee order without modifying the coffee classes themselves.

• Using the decorator pattern allows us to add optional features (add-ons) to coffee
orders dynamically without altering the core coffee classes.
• This promotes code flexibility, scalability, and maintainability as new add-ons can
be easily introduced and combined with different types of coffee orders.
2.2 Design Patterns
Decorator UML diagram
1. Create an interface.
2. Create concrete classes implementing
the same interface.
3. Create an abstract decorator class
implementing the above same interface.
4. Create a concrete decorator class
extending the above abstract decorator
class.
5. Now use the concrete decorator class
created above to decorate interface
objects.
2.2 Design Patterns
• Component Interface: this is an abstract class or interface that defines the common
interface for both the concrete components and decorators. It specifies the operations
that can be performed on the objects.
• Concrete Component: these are the basic objects or classes that implement the
Component interface. They are the objects to which we want to add new behavior or
responsibilities.
• Decorator: this is an abstract class that also implements the Component interface and has
a reference to a Component object. Decorators are responsible for adding new behaviors
to the wrapped Component object.
• Concrete Decorator: These are the concrete classes that extend the Decorator class. They
add specific behaviors or responsibilities to the Component. Each Concrete Decorator can
add one or more behaviors to the Component.
2.2 Design Patterns
Decorator: coffee example (in Java)

Component interface
• This is the interface Coffee representing the component.
• It declares two methods getDescription() and getCost() which must be implemented
by concrete components and decorators.
2.2 Design Patterns
Decorator: coffee example (in Java)

Concrete Component
• PlainCoffee is a concrete class
implementing the Coffee interface.
• It provides the description and
cost of plain coffee by
implementing the
getDescription() and getCost()
methods.
2.2 Design Patterns
Decorator: coffee example (in Java)

Decorator
• CoffeeDecorator is an abstract class
implementing the Coffee interface.
• It maintains a reference to the
decorated Coffee object.
• The getDescription() and getCost()
methods are implemented to
delegate to the decorated coffee
object.
2.2 Design Patterns
Decorator: coffee example (in Java)

Concrete Decorators
• CoffeeDecorator is an abstract class
implementing the Coffee interface.
• MilkDecorator and SugarDecorator are
concrete decorators extending
CoffeeDecorator.
• They override getDescription() to add the
respective decorator description to the
decorated coffee’s description.
• They override getCost() to add the cost of
the respective decorator to the decorated
coffee’s cost.
2.2 Design Patterns
Decorator: coffee example (in Java)
2.2 Design Patterns
• Benefits of using decorator design patterns:
ü Open-Closed Principle: classes should be open for extension but closed for modification. This
means you can introduce new functionality to an existing class without changing its source code.
ü Flexibility: It allows to add or remove responsibilities (i.e., behaviors) from objects. This flexibility
makes it easy to create complex object structures with varying combinations of behaviors.
ü Reusable Code: Decorators are reusable components. You can create a library of decorator
classes and apply them to different objects and classes as needed, reducing code duplication.
ü Composition over Inheritance: unlike inheritance, that can lead to a large and les flexible class
hierarchy, the decorator uses composition. You can compose objects with decorators to achieve
the desired functionality, avoiding the drawbacks such as tight coupling and rigid hierarchies.
ü Dynamic Behavior Modification: Decorators can be applied or removed at runtime, providing
dynamic behavior modification for objects. This is particularly useful when you need to adapt an
object’s behavior based on changing requirements or user preferences.
ü Clear Code Structure: The Decorator pattern promotes a clear and structured design to
understand how different features and responsibilities are added to objects.
2.2 Design Patterns
ü The decorator design pattern can be useful in Python, but there are other better options.
ü In Python, there are more efficient ways of implementing the same solution.
ü Monkey-patching in Python refers to these dynamic (or run-time) modifications of a class or module.
In Python, we can actually change the behavior of code at run-time.

ü Not to be confused with Python decorators, which is a language feature for dynamically modifying a
function or class. Python Decorators are an example of monkey-patching.
ü Design patterns became famous in 1994 publication of GOF Design Patterns. In 2003, the Python core
developers re-used the term decorator for an unrelated feature they were adding (to Python 2.4).
2.2 Design Patterns
References
• The video explains the efficient implementation of the decorator design pattern in Python.

https://youtu.be/QH5fw9kxDQA?si=hZteiJjD5ICv0U-J
2.2 Design Patterns
Decorator: case study Game Characters abilities
• Consider a scenario in game design where different
characters possess a unique set of abilities.
• As the game evolves, we need to add new abilities.
• We’ll use game character abilities, such as
the DoubleDamageDecorator, FireballDecorator,
and InvisibilityDecorator, as examples.
• Characters can have different combinations of these
abilities at any given moment.
• We will implement the decorator design pattern to
enhance a character’s abilities. By dynamically
applying decorators, such as Double Damage and
Invisibility, to a basic character, we create a special
character with combined powers.
2.2 Design Patterns
Decorator: case study Game Characters abilities (in Python)

Component
• The Component is the base
interface that defines the core
behavior that can be enhanced.
• In our example, it represents the
game character with a basic
attack method.
2.2 Design Patterns
Decorator: case study Game Characters abilities (in Python)

Concrete Component
• Concrete Components are the basic game characters to which we can add
special abilities. In this step, we create the basic character class.
2.2 Design Patterns
Decorator: case study Game Characters abilities (in Python)
Decorator

• The Decorator is an abstract


class that also implements the
Component interface.
• It has a reference to a
Component object and adds its
specific behavior to the
component’s core.
• In this step, we create the
abstract decorator class.
2.2 Design Patterns
Decorator: case study Game Characters abilities (in Python)

Concrete Decorator
• Concrete Decorators are classes
that extend the Decorator
abstract class, implementing
specific behaviors.
• In this step, we create concrete
decorator classes for special
character abilities, such as
Double Damage.
2.2 Design Patterns
Decorator: case study Game Characters abilities (in Python)

Client
2.1 Design Principles
Activity 6.2.6
The UML diagram shows a design pattern for a system drawing shapes. We’re going to create a Shape
interface and concrete classes implementing the Shape interface. We will also create an abstract
decorator class ShapeDecorator implementing the Shape interface and having the Shape object as its
instance variable.
Which of the following are true:

A: It’s a decorator structural pattern


B: Rectangle class and Circle class Will be
concrete classes implementing Shape
C: RedShapeDecorator is a concrete class
implementing Shape
D: All the above are correct
2.1 Design Principles
Activity 6.2.7
Assume we are using a Decorator design pattern (a wrapper) for our system. Fill the table with the
corresponding task or function for each of the classes of objects in the Decorator pattern structure.

a) It declares the common interface for both


Class Task
wrappers and wrapped objects.
b) It defines the basic behavior, which can be Component a)
altered by decorators. Is the class of objects
being wrapped. Concrete Component
c) It defines extra behaviors that can be added
to components dynamically. It overrides
Decorator
methods of the base.
d) It can contain both concrete components
and decorators. It is an abstract class that
Concrete Decorator
delegates all operations to the wrapped
object.
2.1 Design Principles
Activity 6.2.8
Which of the following statements are true for
the next class diagram:

A: It’s a creational design pattern


B: Component defines an interface for the
objects to be dynamically extended
C: ConcreteDecorator adds responsibilities
to the component
D: It’s a pattern that allows for the creation
of objects without specifying their exact
class, making the code more flexible and
maintainable

You might also like