0% found this document useful (0 votes)
6 views77 pages

Aula 9 - Design Patterns

This document provides an overview of design patterns in object-oriented programming, emphasizing their role as reusable solutions to common design problems. It discusses the benefits of design patterns, including shared knowledge, best practices, and improved communication among developers. The document also introduces the Model/View/Controller (MVC) pattern, detailing its components and advantages, while categorizing design patterns into creational, structural, and behavioral types.

Uploaded by

danielsfranco346
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
6 views77 pages

Aula 9 - Design Patterns

This document provides an overview of design patterns in object-oriented programming, emphasizing their role as reusable solutions to common design problems. It discusses the benefits of design patterns, including shared knowledge, best practices, and improved communication among developers. The document also introduces the Model/View/Controller (MVC) pattern, detailing its components and advantages, while categorizing design patterns into creational, structural, and behavioral types.

Uploaded by

danielsfranco346
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 77

MC322 - Object Oriented Programming

Lesson 9
Design Patterns

Prof. Marcos M. Raimundo


Instituto de Computação - UNICAMP
Introduction to Design Patterns in Object-Oriented Programming

• What Are Design Patterns?


• Design patterns are reusable solutions to common problems encountered in software design,
providing templates and guidelines for effective problem-solving in different contexts.
• They embody best practices derived from proven solutions, making them valuable for
developing efficient and maintainable object-oriented systems.
• Design Patterns and Reuse:
• Patterns fit seamlessly into the concept of reusable software development because they offer
a way to document and apply strategies that have been successful across different projects.
• This reusability aligns perfectly with the object-oriented paradigm’s focus on modularity,
encapsulation, and code reuse.

1
Introduction to Design Patterns in Object-Oriented Programming

• Benefits of Design Patterns:


• Shared Knowledge: By providing standard solutions, patterns facilitate knowledge sharing
among developers, ensuring that proven techniques are consistently applied across projects.
• Best Practices: Patterns highlight best practices for solving recurring challenges, helping
developers avoid common pitfalls and saving valuable time in the design process.
• Improved Communication: Patterns provide a common vocabulary that developers can use
to describe solutions, making design discussions more precise and collaborative.
• Key Takeaway:
• The influence of design patterns on object-oriented development is significant. They bridge
the gap between theory and practical application, allowing developers to implement robust,
scalable, and maintainable software based on successful precedents.

2
Why Design Patterns?

3
The Concept of Design Patterns

• The concept of design patterns did not necessarily start with the need for reusable
software.
• The seminal work on design patterns is about constructing buildings and cities.
• Christopher Alexander noted in A Pattern Language: Towns, Buildings,
Construction:
“Each pattern describes a problem that occurs over and over again in our environ-
ment, and then describes the core of the solution to that problem in such a way
that you can use the solution a million times over, without ever doing it the same
way twice.”

• Four Elements of a Pattern: Name, Problem, Solution, Consequences.

4
The Four Elements of a Pattern

• Pattern Name: A handle to describe a design problem, its solutions, and consequences
in just a word or two.
• Naming a pattern increases our design vocabulary and lets us design at a higher level of
abstraction.
• Having this vocabulary allows easier communication with colleagues, in documentation, and
even for personal use.
• Good pattern names make it easier to discuss designs and their tradeoffs with others.
• Finding suitable names has been one of the hardest parts of developing our catalog.

5
The Four Elements of a Pattern

• The Problem: Describes when to apply the pattern and provides context.
• Explains the design problem, such as how to represent algorithms as objects.
• May identify class or object structures that indicate inflexible design.
• Sometimes includes conditions that must be met before the pattern is applicable.

6
The Four Elements of a Pattern

• The Solution: Describes the elements that constitute the design, including their
relationships, responsibilities, and collaborations.
• Doesn’t specify a particular concrete design or implementation because a pattern serves as a
flexible template.
• Provides an abstract description of a design problem and how a general arrangement of
elements can solve it.
• The arrangement typically includes classes and objects to form a generalized solution.

7
The Four Elements of a Pattern

• The Consequences: Results and trade-offs of applying the pattern.


• Consequences are crucial for evaluating design alternatives and understanding the costs and
benefits.
• Software consequences often relate to space and time trade-offs, language, and
implementation issues.
• Patterns affect flexibility, extensibility, and portability, particularly in object-oriented design.
• Explicitly listing the consequences aids in understanding and evaluating them.

8
Smalltalk’s Model/View/Controller

9
Historical Perspective on MVC

• The Model/View/Controller (MVC) pattern, introduced in Smalltalk, is often used to


illustrate the origins of design patterns.
• The MVC paradigm was initially developed for creating user interfaces in Smalltalk.
• Smalltalk was one of the first popular object-oriented languages.
Smalltalk
Smalltalk is the result of several great ideas that emerged from Xerox PARC, including the
use of a mouse and a windowing environment. It is a wonderful language that provided
the foundation for subsequent object-oriented languages.
One of the criticisms of C++ is that it isn’t truly object-oriented, whereas Smalltalk is.
Although C++ had a larger following in the early days of OO, Smalltalk has always had a
dedicated core group of supporters. Java is also mostly OO and has embraced the C++
developer base.

10
MVC Components Defined by Design Patterns

• The Model is the application object.


• The View is the screen presentation.
• The Controller defines how the user interface reacts to user
input.

• Previous Paradigms: The Model, View, and Controller were


often combined into a single entity.
• MVC Paradigm: Constructing an object.
• Separates these components into distinct interfaces.
• Allows changing the user interface by modifying only the
View.

11
MVC: Core Components & Responsibilities
MVC divides application concerns into three key roles:

Model: Data & Logic View: Presentation


• Manages application data and business rules. • Displays Model data to the user.
• Independent of UI (View/Controller). • Forwards user actions to Controller.
• Notifies Views of state changes. • Can have multiple Views for one Model.
• Ex: To-Do list data, task operations. • Ex: Shows task list, input fields, buttons.

Controller: Intermediary
• Handles user input received from View.
• Updates Model based on this input.
• May select or update View after Model changes.
• Ex: Responds to ”Add Task” button click.

12
Advantages and Drawbacks of MVC
• Following MVC concept and separate the user interface, business logic, and data:
• Your system will be more flexible and robust.
• Changing the GUI won’t affect business logic or data.
• Adjusting business logic won’t require GUI changes.
• Modifying how data is stored won’t affect GUI or business logic, assuming interfaces between
the three don’t change.
MVC Example
Consider a GUI with a list of phone numbers as an example involving a list box. The list
box is the view, the phone list is the model, and the controller is the logic binding the list
box to the phone list.

MVC Drawbacks
Although the MVC is a great design, it can become complex due to the required upfront
design attention. Object-oriented design has the challenge of balancing good and
cumbersome design. The key question is how much complexity should be included. 13
MVC: Component Interaction Flow
Components collaborate in a cycle to process user input and update views:

Interaction Steps:
1. User interacts with View. User
2. View notifies Controller of the user
1
⃝ Interacts
action.
3. Controller updates the Model

Forwards Action
View
(data/logic).
4. Model (if state changed) notifies
observing View(s).
Controller


⃝4 Notifies /

2
5. View(s) query Model and refresh their 5 View Updates
⃝ 3
⃝ Updates
display.
Model

Fig: MVC Interaction Cycle. Numbers match steps. 14


MVC: Practical Example - To-Do List
Model (TaskList)
• Data: Manages ‘ListTaskItem ‘ (task text, completion status).
• Logic: Core methods like ‘addTask()‘, ‘removeTask()‘, ‘toggleStatus()‘.
• Notifies: Informs registered Views of any data changes.

View (TaskDisplay)
• UI: Displays tasks, input field, ”Add” button, checkboxes.
• Refresh: Updates its display when the Model changes.
• Input: Forwards user actions (e.g., ”Add” click, checkbox toggle) to Controller.

Controller (TaskActionHandler)
• Handles Events: Listens to UI events from the View.
• Updates Model: E.g., on ”Add” click, calls ‘Model.addTask(textFromView)‘.
• Coordinates (loosely): Often relies on Model’s notification mechanism to update View.

This separation enhances modularity, testability, and maintainability. 15


Separation of Interfaces and Implementation

• Interface vs. Implementation:


• Object-oriented development focuses on separating the interface from the implementation as
much as possible.
• We should also aim to keep distinct interfaces separate.
• Avoid combining unrelated interfaces that are not linked to solving the problem at hand.
• MVC Paradigm:
• One of the early pioneers in interface separation.
• Explicitly defines interfaces between components to solve the common problem of creating
user interfaces.
• Provides a structure for connecting user interfaces to the business logic and data.

16
MVC: Leveraging Object-Oriented Principles

Encapsulation & Abstraction Delegation & Composition


Encapsulation: Each component (M, V, C) bundles Delegation: Responsibilities are clearly assigned and
its specific data and behaviors, hiding internal delegated to the appropriate component.
complexity. • View delegates user input handling to Controller.
• Model: Protects core data and business logic. • Controller delegates data-related tasks to Model.
• View: Manages display details internally. Composition: The application is constructed by
• Controller: Isolates input processing logic. assembling Model, View, and Controller.
Abstraction: Components are defined by their roles
Interfaces & Polymorphism
and responsibilities (their ”what”) rather than
specific implementations (their ”how”). Interfaces: Define clear contracts (APIs) for how
components interact, promoting loose coupling.
• Model abstracts the data source and its
manipulation. • Allows implementations to change without
affecting client components.
• View abstracts the specifics of the user interface
Polymorphism: Enables different concrete
technology.
implementations of these interfaces.
17
• Multiple types of Views (e.g., GUI, web, mobile)
Types of Design Patterns

18
Design Patterns: Categorization and Historical Context

• 23 Patterns Grouped into Three Categories:


• Most examples are in C++, with some in Smalltalk.
• The book’s publication period reflects the popularity of C++ and Smalltalk.
• Historical Context:
• Design Patterns was published in 1995, during the Internet revolution and Java’s rise.
• The value of design patterns spurred interest, leading to many books.
• Later publications were written largely in Java due to its growing popularity.

19
Categories of Patterns

• Language Irrelevance:
• The specific programming language is irrelevant, as the book is focused on design.
• Patterns can be implemented in any language.
• Three Categories of Patterns:
• Creational Patterns: Create objects for you, providing flexibility in object instantiation.
• Structural Patterns: Compose groups of objects into larger structures like complex UIs or
accounting data.
• Behavioral Patterns: Define communication and flow between objects in complex programs.

20
Creational Patterns

21
Creational Patterns Categories

• Creational Patterns consist of:


• Abstract Factory
• Builder
• Factory Method
• Prototype
• Singleton

Scope: This chapter aims to describe what a design pattern is rather than cover each
pattern in the GoF book.
Example Pattern: We’ll focus on three patterns: Singleton, Factory Method, Builder.

22
Creational Patterns

Singleton

23
The Singleton Design Pattern
Definition: The Singleton pattern is a creational pattern that ensures a class has only a
single instance and provides a global point of access to that instance.
• Purpose: Regulates object creation to one instance per class.
• Example Use Case:
• A website counter object tracks the hits on a page.
• The counter object should not be re-instantiated each time a page is loaded.
• A single instance is created with the first hit and reused to increment the count afterward.

The singleton model.


Taking Care of Business
Remember, one of the most important object-oriented rules is that an object should
take care of itself. This means that issues regarding the life cycle of a class should be
handled within the class itself, not delegated to language constructs like static. 24
UML Model for Singleton
Figure shows the UML model for the Singleton pattern as presented in Design Patterns.

• The uniqueinstance property is a static singleton object.


• The Instance() method provides access to the unique instance.
• Other properties and methods support the business logic of the class.

The singleton model.

25
Singleton Pattern Code Example
Access and Instance Control:
• Any class needing the singleton instance should use the getInstance() method.
• The constructor controls object creation like any OO design.
• The getInstance() method handles instantiation:
• Checks if the singleton instance is null.
• Instantiates a new object if required.
• Returns the singleton instance.

Java Code Example (Classic Singleton):


public class ClassicSingleton {
private static ClassicSingleton instance = null ;
p r o t e c t e d C l a s s i c S i n g l e t o n ( ) {}
public s t a t i c ClassicSingleton getInstance () {
i f ( i n s t a n c e == n u l l ) {
i n s t a n c e = new C l a s s i c S i n g l e t o n ( ) ;
}
return instance ;
}
}
26
Multiple References to a Singleton
Managing Multiple References:

• Each reference in the application pointing to the singleton must be managed properly.
• Multiple references can coexist but should refer to the same singleton instance.
public class Singleton {
p u b l i c s t a t i c v o i d main ( S t r i n g [ ] a r g s ) {
Counter counter1 = Counter . g e t I n s t a n c e () ;
System . o u t . p r i n t l n ( ” C o u n t e r : ” + c o u n t e r 1 . g e t C o u n t e r ( ) ) ;
Counter counter2 = Counter . g e t I n s t a n c e () ;
System . o u t . p r i n t l n ( ” C o u n t e r : ” + c o u n t e r 2 . g e t C o u n t e r ( ) ) ;
}
}

Explanation of the Example:


• Both counter1 and counter2 point to the same Counter instance.
• The constructor is not directly used; object creation is managed by the getInstance()
method.
27
Proving Singleton References
Purpose: Demonstrate that both counter1 and counter2 refer to the same instance.
package Counter ;

public class Singleton {


p u b l i c s t a t i c v o i d main ( S t r i n g [ ] a r g s ) {
Counter counter1 = Counter . g e t I n s t a n c e () ;
counter1 . incrementCounter () ;
counter1 . incrementCounter () ;
System . o u t . p r i n t l n ( ” C o u n t e r : ” + c o u n t e r 1 . g e t C o u n t e r ( ) ) ;

Counter counter2 = Counter . g e t I n s t a n c e () ;


counter2 . incrementCounter () ;
System . o u t . p r i n t l n ( ” C o u n t e r : ” + c o u n t e r 2 . g e t C o u n t e r ( ) ) ;
}
}

Explanation:
• counter1 increments twice before displaying its value.
• counter2 then increments once and displays its value.
• Both references output the same, cumulative count, proving that they’re pointing to the
same instance. 28
Singleton: Advantages and Disadvantages

Advantages Disadvantages
• Ensured Unique Instance: Guarantees only one • Global State: Can make code harder to test and
object of its kind. reason about due to widespread dependencies on a
• Global Access Point: Provides a well-known way to single instance.
access this instance. • Testability Issues: Difficult to mock or replace the
• Lazy Initialization (Optional): The instance can be singleton instance in unit tests.
created only when first needed (in some • Violates Single Responsibility Principle (SRP): The
implementations). class often manages its own lifecycle *and* its
• Resource Control: Useful for managing shared primary business logic.
resources like configurations, loggers, or hardware • Inflexibility: Can be hard to adapt if multiple
interfaces. instances are needed later.

Consider alternatives like Dependency Injection for managing shared instances, especially in complex
applications.

29
Singleton: Core Object-Oriented Mechanisms

Controlled Instantiation Encapsulated & Global Access


• Private Constructor: This is the cornerstone. • Encapsulated Static Instance: The single
By declaring the constructor ‘private‘, the instance is stored in a ‘private static‘ field within
Singleton class prevents any other class from the class. This encapsulates the instance,
directly creating new instances using the ‘new‘ protecting it from outside modification.
operator.
• Public Static Access Method: A ‘public static‘
• Self-Creation Logic: The class itself contains method (commonly named ‘getInstance()‘) acts
the logic to create its own unique instance. This as the sole, global point of access to the unique
often involves checking if an instance already instance. It returns the cached instance or
exists. creates it on first call (lazy initialization).

Key Principle Employed


The Singleton class takes full responsibility for its own lifecycle and instance management. It uses
encapsulation (hiding the instance and constructor) and class-level static members (for the instance and
access method) to strictly control its instantiation and ensure uniqueness. 30
Creational Patterns

Factory Method

31
Factory Method: Concept, Intent & UML
Definition: A creational design pattern that provides an interface for creating objects in a
superclass, but allows subclasses to alter the type of objects that will be created.

Typical UML Structure:


Intent & Problem Solved
• Lets subclasses decide which concrete class to
instantiate.
• Decouples client code (often in the superclass
methods) from concrete product classes.
• Used when a class cannot anticipate the exact
types of objects it needs to create.
• Localizes the logic for creating different product
types.
32
Factory Method: Java Implementation Example - UI framework
Product ConcreteProducts (Buttons): Creator ConcreteCreators (Dialogs):

interface Button { abstract class Dialog {


void render () ; public void renderWindow () {
void onClick ( String event ) ; Button okButton = createButton () ;
} okButton . render () ;
class WindowsButton implements Button { }
public void render () {} public abstract Button createButton () ;
public void onClick ( String e ) {} }
} class WindowsDialog extends Dialog {
class HTMLButton implements Button { @Override
public void render () {} public Button createButton () {
public void onClick ( String e ) {} return new WindowsButton () ; // Creates
} Windows button
}
}
Client Code / Demonstration: class WebDialog extends Dialog {
@Override
public class Application { public Button createButton () {
public static void main ( String [] args ) { return new HTMLButton () ; // Creates HTML
String configOS = " Web " ; // or " Windows " button
if ( configOS . equals ( " Windows " ) ) { }
dialog = new WindowsDialog () ; }
} else if ( configOS . equals ( " Web " ) ) {
dialog = new WebDialog () ;
}
dialog . renderWindow () ; 33
}
Factory Method: Advantages & Disadvantages

Advantages Disadvantages
• Loose Coupling: The client code in the Creator (or the • Increased Number of Classes: Can lead to a
client using the Creator) works with the ‘Product‘ larger class hierarchy, as each
interface and is unaware of concrete ‘Product‘ classes. ‘ConcreteProduct‘ often requires a dedicated
• Extensibility (Open/Closed Principle): New product ‘ConcreteCreator‘ subclass.
types can be introduced by adding new • Subclassing Requirement: The pattern relies
‘ConcreteProduct‘ classes and corresponding on creating subclasses for the ‘Creator‘ class
‘ConcreteCreator‘ subclasses without modifying existing to implement different factory methods,
Creator logic or client code. which might be an overhead.
• Single Responsibility Principle (SRP): Moves the • Complexity for Simple Cases: If only a few
responsibility of product creation into specific factory product types exist and they don’t change
methods within concrete creator classes, isolating this often, a simpler approach (like a Simple
logic. Factory) might be sufficient.
• Flexibility for Subclasses: Subclasses have full control
over the type of product created.
34
Factory Method: Core Object-Oriented Mechanisms

Abstraction & Hierarchy Polymorphism & Encapsulation


• Abstract Product: Defines an interface or • Polymorphic Factory Method: Each
abstract class for the objects the factory method ‘ConcreteCreator‘ subclass overrides the
will create (e.g., ‘Button‘). ‘factoryMethod‘ to return a different type of
• Abstract Creator: Declares the ‘factoryMethod‘ ‘ConcreteProduct‘. The ‘Creator‘’s code can then
(often abstract, e.g., ‘createButton()‘), which work with the ‘Product‘ polymorphically.
returns a ‘Product‘. It may also contain business
• Encapsulated Creation Logic: The precise
logic that uses the product.
details of how a ‘ConcreteProduct‘ is instantiated
• Inheritance: are hidden (encapsulated) within the
• ‘ConcreteProduct‘ classes ‘factoryMethod‘ of its corresponding
implement/extend the ‘Product‘ ‘ConcreteCreator‘.
interface/class.
• ‘ConcreteCreator‘ classes extend the
‘Creator‘ and provide specific
implementations for the ‘factoryMethod‘.
35
Creational Patterns

Builder

36
Builder: Concept, Intent UML

Intent & Problem Solved

• Construct complex objects step-by-step with fine-grained control.


• Different representations of an object created using same build process.
• Avoids ”telescoping constructors” (numerous optional parameters).
• Useful when object creation is complex, involves many optional parts, or
requires a specific sequence of steps.

Core Idea & Participants

• Product: The complex object that is being built.


• Builder (Interface/Abstract Class): Declares abstract steps for building parts
of the Product (e.g., ‘buildPartA()‘, ‘buildPartB()‘) and a method to retrieve
the result (e.g., ‘getResult()‘).
• ConcreteBuilder: Implements the ‘Builder‘ interface, constructs and assembles
parts to create a specific representation of the Product.
• Director (Optional): Orchestrates the construction process using a ‘Builder‘
37
instance by calling its building steps in a specific order.
Builder: Java Implementation Example - Computer Config
Product (‘Computer‘): Builder Interface ConcreteBuilder:

class Computer { interface C omp ute rBu ild er {


private String cpu , ram , storage , gpu ; void buildCpu ( String cpu ) ;
public void setCpu ( String s ) { this . cpu = s ; } void buildRam ( String ram ) ;
public void setRam ( String s ) { this . ram = s ; } void buildStorage ( String storage ) ;
public void setStorage ( String s ) { this . storage void buildGpu ( String gpu ) ; // GPU is optional
= s ;} Computer getResult () ;
} }
class S t a n d a r d C o m p u t e r B u i l d e r implements
Com put erB uil der {
Director (‘ComputerDirector‘): private Computer computer ;
public S t a n d a r d C o m p u t e r B u i l d e r () {
class C o m p u t erDirector { this . computer = new Computer () ; }
public Computer constructGamePC ( ComputerBuilder public void buildCpu ( String c ) {
builder ) { computer . setCpu ( c ) ; }
builder . buildCpu ( " Intel Core i9 " ) ; public void buildRam ( String r ) {
builder . buildRam ( " 32 GB DDR5 " ) ; computer . setRam ( r ) ; }
builder . buildStorage ( " 2 TB NVMe SSD " ) ; public void buildStorage ( String s ) {
builder . buildGpu ( " NVIDIA RTX 4090 " ) ; computer . setStorage ( s ) ; }
return builder . getResult () ; public void buildGpu ( String g ) {
} computer . setGpu ( g ) ; }
public Computer public Computer getResult () {
c o ns tru ct Off ic ePC ( ComputerBuilder builder ) Computer product = this . computer ;
{ this . computer = new Computer () ;
builder . buildCpu ( " Intel Core i5 " ) ; return product ;
builder . buildRam ( " 16 GB DDR4 " ) ; }
}
38
builder . buildStorage ( " 512 GB SATA SSD " ) ;
Builder: Advantages & Disadvantages

Advantages Disadvantages
• Step-by-Step Construction: Allows creating • Increased Complexity: Requires creating several
objects with fine-grained control over the assembly new classes: the Builder interface, one or more
process. Parts can be built incrementally. ConcreteBuilders, and potentially a Director.
• Avoids Telescoping Constructors: Leads to • Mutability During Construction: The object
cleaner code when an object has many optional being built is often mutable and in an incomplete
parameters or configurations. state until the final ‘getResult()‘ (or ‘build()‘)
• Reusable Construction Logic: The same method is called. This needs to be managed
construction process (managed by a Director) can carefully.
be used to create different representations of the • Verbosity: Can be more verbose for simple objects
Product by using different ConcreteBuilders. where direct construction is straightforward.
• Single Responsibility Principle (SRP): Isolates • Director’s Role: If a Director is not used, the
complex construction logic from the Product’s client code becomes responsible for orchestrating
business logic and from the client code. the build steps, which might re-introduce
complexity to the client.
39
Builder: Core Object-Oriented Mechanisms

Abstraction & Polymorphism Encapsulation & Composition


• Abstract Builder (‘Builder‘ Interface): Defines a • Encapsulation of Construction Logic: Each
common interface with steps to construct parts of ‘ConcreteBuilder‘ encapsulates the complex logic
a complex object (e.g., ‘buildPartA()‘, and specific knowledge required to create and
‘buildPartB()‘). This abstracts the specific assemble a particular representation of the
construction details from the client or ‘Director‘. ‘Product‘.
• Polymorphism: A ‘Director‘ (or client) can work • Composition for Product Assembly: The final
with different ‘ConcreteBuilder‘ instances through ‘Product‘ is typically a complex object composed of
the common ‘Builder‘ interface. The same multiple distinct parts. The Builder pattern
construction process can thus lead to different facilitates the incremental construction and
representations of the ‘Product‘. assembly of these parts.

Key Outcome: Enhanced Separation of Concerns


The Builder pattern effectively separates the algorithm for constructing a complex object from how its
individual parts are built and assembled (‘ConcreteBuilder‘) and from the final representation of the
‘Product‘ itself. This promotes flexibility and control over the construction process. 40
Structural Patterns

41
Structural Design Patterns
Purpose of Structural Patterns:
• Structural patterns are used to create larger structures from groups of objects.
• These patterns help separate interfaces from implementations.
Members of the Structural Category: Adapter Pattern:
• Adapter • One of the most important design patterns.
• Bridge • Effectively separates the implementation
from the interface.
• Composite
• Facilitates compatibility between different
• Decorator
interfaces.
• Façade
• Flyweight
• Proxy
42
Structural Patterns

Adapter

43
Adapter: Concept, Intent & UML
A structural design pattern that allows objects with incompatible interfaces to collaborate. It acts as a bridge
or wrapper between two incompatible interfaces, making them work together. Also Known As: Wrapper.

Intent & Problem Solved

• Convert the interface of an existing class (Adaptee) into another


interface (Target) that a client expects.
• Enable interface-incompatible classes to work together.
• Integrate existing or legacy code with new systems.

Core Idea & Participants (Object Adapter)

• Target: The desired interface that the ‘Client‘ code uses.


• Adaptee: The existing class with an incompatible interface that
needs to be used.
• Adapter: It translates requests from the ‘Client‘ (made via ‘Target‘
methods) into calls on the ‘Adaptee‘.
44
• Client: Interacts with objects conforming to the ‘Target‘ interface.
Adapter: Java Implementation Example (Object Adapter)
Scenario: A new application system expects a ‘ModernPaymentGateway‘ interface, but we need to integrate
an existing ‘OldPayPalAPI‘ class.

Target Interface (‘ModernPaymentGateway‘): Adapter (‘PayPalAdapter‘):

interface M o d e r n P a y m e n t G a t e w a y { class PayPalAdapter implements M o d e r n P a y m e n t G a t e w a y


void processPayment ( double amount ) ; {
String g e t T r a n s a c t i o n S t a t u s ( String private OldPayPalAPI oldPayPal ; // Composition
transactionId ) ;
} public PayPalAdapter ( OldPayPalAPI oldPayPal ) {
this . oldPayPal = oldPayPal ;
}
Adaptee (‘OldPayPalAPI‘): @Override
public void pr ocess Payme nt ( double amount ) {
class OldPayPalAPI { oldPayPal . sendPayment ( amount , " USD " ) ; //
public void sendPayment ( double value , String Assume USD for simplicity
currency ) { }
System . out . println ( " OldPayPal : Payment of " @Override
+ value + " " + currency + " sent . " ) ; public String g e t T r a n s a c t i o n S t a t u s ( String
} transactionId ) {
public boolean checkStatus ( String txId ) { return oldPayPal . checkStatus ( transactionId )
System . out . println ( " OldPayPal : Checking ? " COMPLETED " : " PENDING " ;
status for " + txId ) ; }
return true ; // Simplified }
}
} 45
Adapter: Types, Advantages & Disadvantages

Advantages Disadvantages
• Interoperability: Allows classes with • Increased Complexity: Introduces an additional
incompatible interfaces to seamlessly work class (the Adapter), which adds a layer of
together. indirection to the system.
• Reusability of Existing Code: Enables the use • Overhead (Class Adapter): Tightly couples the
of existing (Adaptee) classes without needing to Adapter to a specific Adaptee class
modify their source code. implementation, making it less flexible.
• Flexibility (Object Adapter): An Adapter can • Potential for ”Fat” Adapters: If the Target
work with many Adaptees (the Adaptee instance interface is extensive, the Adapter might become
can be passed at runtime). complex with numerous forwarding methods and
• Single Responsibility Principle (SRP): The translation logic.
responsibility of interface conversion is • Translation Effort: The logic to translate data
encapsulated within the Adapter class. or calls between interfaces can sometimes be
non-trivial.

46
Adapter: Core Object-Oriented Mechanisms
Interfaces & Polymorphism
• Target Interface Implementation: The Adapter class implements the ‘Target‘ interface, which is the
specific interface the client code expects and is designed to work with.

• Polymorphic Usage: This allows clients to interact with the Adapter object polymorphically through the
‘Target‘ interface. The client treats the Adapter just like any other object that conforms to the ‘Target‘
interface, remaining unaware of the underlying ‘Adaptee‘.

Composition & Delegation (Object Adapter)


• Composition over Inheritance: The Adapter holds a private instance (a reference) to an ‘Adaptee‘
object—the class with the incompatible interface. This is a form of object composition.

• Delegation of Requests: When a client calls a method on the Adapter (via the ‘Target‘ interface), the
Adapter delegates this request by translating it into one or more calls on the methods of the wrapped
‘Adaptee‘ instance.

47
Structural Patterns

Decorator

48
Decorator: Concept, Intent & UML
Definition: A structural design pattern that allows behavior to be added to individual objects dynamically,
without affecting the behavior of other objects from the same class. Also Known As: Wrapper.

Typical UML Structure:


Intent & Problem Solved
• Attach additional responsibilities or behaviors to an object
dynamically and transparently.
• Provide a flexible alternative to subclassing for extending.
• Avoid a ”combinatorial explosion” of subclasse.

Core Idea & Participants


• Component (Interface/Abstract Class).
• ConcreteComponent.
• Decorator (Abstract Class).
• ConcreteDecorator. 49
Decorator: Java Implementation Example
Scenario: Dynamically adding optional features (like milk, sugar) to different types of coffee.

Component Interface (‘Coffee‘): ConcreteComponent (‘SimpleCoffee‘):

interface Coffee { class SimpleCoffee implements Coffee {


String g etDescription () ; @Override
double getCost () ; public String getD escri ptio n () { return " Simple
} Coffee " ; }
@Override
public double getCost () { return 2.00; }
Abstract Decorator (‘CoffeeDecorator‘): }

abstract class CoffeeDecorator implements Coffee {


protected Coffee decoratedCoffee ; ConcreteDecorators (‘MilkDecorator‘):
public CoffeeDecorator ( Coffee coffee ) {
this . decoratedCoffee = coffee ; class MilkDecorator extends Cof fee Dec ora tor {
} public MilkDecorator ( Coffee coffee ) {
super ( coffee ) ; }
@Override // Delegate to wrapped component @Override public String g etDe scrip tion () {
public String getDescription () { return super . ge tDesc ript ion () + " , Milk " ;
return decoratedCoffee . getDescription () ; }
} @Override public double getCost () {
@Override // Delegate to wrapped component return super . getCost () + 0.50; // Add cost
public double getCost () { }
return decoratedCoffee . getCost () ; }
}
}
50
Decorator: Advantages & Disadvantages

Advantages Disadvantages
• Flexibility for Adding Responsibilities: Behaviors can • Many Small Objects: Can lead to a system with a
be added to (or removed from) objects dynamically large number of small decorator objects, which might
at runtime by adding or removing decorators. be slightly harder to manage or understand.
• Avoids Feature Bloat in Superclasses: Functionality • Complexity in Understanding Decorator Chain: The
is added by small, composable decorator objects, structure of deeply nested decorators can sometimes
rather than creating many subclasses. be complex to configure correctly or debug, as
• Open/Closed Principle Adherence: New decorators behavior is distributed among many small classes.
can be introduced without modifying existing • Tight Coupling to Component Interface: Decorators
component or decorator classes. are tightly coupled to the specific ‘Component‘
• Composable Functionality: Multiple decorators can interface they decorate. A significant change in this
be nested (stacked) to combine several layers of interface can require changes in all concrete
functionality for a single component. decorators.

• Interface Consistency: Decorated objects retain the • Object Identity Issues: A decorated object is not
same interface as the original component, making identical to the original object (e.g.,
them transparent to clients that expect the original ‘decoratorInstance != originalInstance‘). This might
component’s type. be an issue for client code that relies on strict object
identity (‘==‘ checks). 51
Decorator: Core Object-Oriented Mechanisms
Shared Interface & Polymorphism (‘Component‘)
• Both the original objects (‘ConcreteComponent‘) and all ‘Decorator‘ classes implement a common ‘Component‘
interface (or extend an abstract class).
• This ensures that clients can treat both original and decorated objects polymorphically through the same interface,
making decorators transparent to client code.

Wrapping via Composition & Delegation


• Each ‘Decorator‘ object holds a reference to (i.e., ”wraps”) another ‘Component‘ object through object
composition. This wrapped component can be a ‘ConcreteComponent‘ or another, already decorated, ‘Decorator‘.
• Responsibilities are added by having the ‘Decorator‘ delegate method calls to its wrapped component, and then
performing its own additional actions before or after this delegation.

Flexible Extension (Decorator Hierarchy)


• An abstract ‘Decorator‘ class often mirrors the ‘Component‘ interface and manages the reference to the wrapped
component.
• Specific ‘ConcreteDecorator‘ classes inherit from this abstract ‘Decorator‘ to introduce distinct additional
functionalities. This allows for an open-ended way to add new features.
52
Structural Patterns

Facade

53
Facade: Concept, Intent & UML
Definition: A structural design pattern that provides a simplified, unified interface to a complex subsystem of classes,
a library, or a framework. It effectively hides the system’s underlying complexity from the client.

Typical UML Structure:


Intent & Problem Solved
• Provide a single, higher-level interface that makes a
subsystem easier to use for common tasks.
• Reduce the complexity when clients need to interact
with numerous interdependent classes.
• Decouple client code from the internal components and
structure of a subsystem.

Core Idea & Participants


• Facade: A class that provides the simplified interface.
• Subsystem Classes. A collection of classes that
implement the complex underlying functionality.
• Client: Uses the Facade to interact with the subsystem.
54
Facade: Java Implementation Example
Scenario: Simplifying the startup and shutdown sequence of a complex Car system.
Subsystem Classes (Simplified): Facade (‘CarFacade‘):

// Subsystem components class CarFacade {


class Engine { private Engine engine ;
public void start () { private Lights lights ;
System . out . println ( " Engine ON " ) ; } private AirC ondi tione r ac ;
public void stop () { System . out . println ( " Engine
OFF " ) ; } public CarFacade () {
} this . engine = new Engine () ;
class Lights { this . lights = new Lights () ;
public void turnOn () { this . ac = new Air Cond ition er () ;
System . out . println ( " Lights ON " ) ; } }
public void turnOff () { public void startCar () {
System . out . println ( " Lights OFF " ) ; } engine . start () ;
} lights . turnOn () ;
class AirCo nditioner { ac . start () ;
public void start () { System . out . println ( " AC ac . se tTem perat ure (22) ;
ON " ) ; } }
public void stop () { System . out . println ( " AC public void stopCar () {
OFF " ) ; } engine . stop () ;
public void setTemperature ( int temp ) { lights . turnOff () ;
System . out . println ( " AC set to " + temp + ac . stop () ;
"C"); System . out . println ( " Car is off . " ) ;
} }
} } 55
Facade: Advantages & Disadvantages

Advantages Disadvantages
• Simplified Interface: Provides a much simpler, • Potential ”God Object”: The Facade itself can
higher-level way to interact with a complex become a large and complex class if it tries to
subsystem. expose too much functionality from the subsystem
• Decoupling Clients from Subsystem: Clients are or becomes coupled to too many subsystem
shielded from the internal complexity and changes components.
within the subsystem. • May Hide Useful Features (if not designed
• Improved Readability & Usability: Client code well): While simplifying, a Facade might
becomes cleaner and more focused on its primary inadvertently hide lower-level features or flexibility.
tasks, rather than on managing the intricacies of • Doesn’t Fix Poor Subsystem Design: A Facade
subsystem interactions. provides a simpler interface but does not inherently
• Layering and Reduced Dependencies: Promotes improve a poorly designed underlying subsystem.
better software layering by providing a controlled • Single Point of Entry Concerns: If the Facade is
entry point to a subsystem, thus reducing direct the only way to access the subsystem, it can
dependencies from clients other subsystem classes. become a bottleneck or a single point of failure.
56
Facade: Core Object-Oriented Mechanisms
Encapsulation & Abstraction
• Encapsulation of Subsystem Complexity: The Facade class effectively hides the intricate details, internal
structure, and interdependencies of the numerous classes within a subsystem from the client.
• Simplified Abstract Interface: The Facade provides a single, unified, and higher-level interface (an abstraction) to
the subsystem.

Delegation & Composition


• Delegation of Responsibility: When a client makes a request to the Facade, the Facade delegates this request to
the appropriate objects within the subsystem that are actually responsible for performing the specific parts of the
work.
• Composition of Subsystem Components: The Facade object typically holds references to (i.e., composes or
aggregates) the various objects from the subsystem that it needs to interact with and coordinate to fulfill client
requests.

Key Outcome: Decoupling & Simplified Usage


By acting as this single, simplified entry point, the Facade decouples client code from the complex internal workings
and evolution of the subsystem. This reduces dependencies, improves maintainability, and makes the subsystem
much easier to use. 57
Behavioral Patterns

58
Behavioral Patterns: Iterator Example
Behavioral Patterns Categ: Iterator Pattern Overview:
• Chain of Responsibility • Provides a way to sequentially access elements of a collection
• Command without exposing its internal structure.
• Implemented by several programming languages, offering a
• Interpreter
consistent way to navigate through data structures.
• Iterator
• Used to iterate through lists, arrays, and other collections.
• Mediator
Key Features:
• Memento
• Separates traversal logic from collection logic, providing
• Observer flexibility.
• State • Supports different types of traversal, like forward or backward
• Strategy iteration.

• Template Method • Allows multiple traversals simultaneously on the same


collection. 59
• Visitor
Behavioral Patterns

Iterator

60
The Iterator Design Pattern

Iterator Pattern Overview:

• Provides a standard mechanism for traversing a collection like a vector or list.


• Facilitates accessing each item of a collection sequentially.
• Offers information hiding to keep the internal structure secure.
• Allows multiple iterators to coexist without interference.

Key Features of Java Iterator:

• The iterate() method uses an enhanced for loop to traverse and print elements.
• The iterator pattern makes this traversal easy and secure.

61
Java Iterator Implementation
public class Iterator {
p u b l i c s t a t i c v o i d main ( S t r i n g a r g s [ ] ) {
// I n s t a n t i a t e an A r r a y L i s t
A r r a y L i s t <S t r i n g > names = new A r r a y L i s t <>() ;
// Add v a l u e s t o t h e A r r a y L i s t
names . add ( ” J o e ” ) ;
names . add ( ” Mary ” ) ;
names . add ( ”Bob” ) ;
names . add ( ” Sue ” ) ;

// Now i t e r a t e t h r o u g h t h e names
System . o u t . p r i n t l n ( ” Names : ” ) ;
i t e r a t e ( names ) ;
}

p r i v a t e s t a t i c v o i d i t e r a t e ( A r r a y L i s t <S t r i n g > a r l ) {
for ( String listItem : arl ) {
System . o u t . p r i n t l n ( l i s t I t e m . t o S t r i n g ( ) ) ;
}
}
}

Better example: Guru:link


62
Behavioral Patterns

Observer

63
Observer: Concept, Intent & UML
Typical UML Structure:
Intent & Problem Solved
• Define a one-to-many dependency between objects so that when
one object (Subject) changes state, all its dependents (Observers)
are notified and updated automatically.
• Keep objects synchronized without creating tight interdependencies.
• Allow an object to notify an open-ended number of other objects.

Core Idea Participants


• Subject (Interface/Abstract Class). Manages observers.
• ConcreteSubject: Stores state of interest to ‘ConcreteObserver‘.
• Observer (Interface/Abstract Class): Defines an updating
interface (e.g., ‘update()‘) for objects that should be notified.
• ConcreteObserver: Implements the ‘Observer‘ interface to react to
changes in the ‘ConcreteSubject‘. 64
Observer: Java Implementation Example
Scenario: A ‘StockMarket‘ (Subject) notifies various ‘StockTrader‘s (Observers).

Observer Interface (‘Trader‘): Subject Interface (‘Stock‘):

interface Trader { interface Stock {


void update ( String stockSymbol , double price ) ; void reg ister Trad er ( Trader trader ) ;
} void removeTrader ( Trader trader ) ;
void notifyTraders () ;
}
ConcreteSubject (‘SpecificStock‘):

class SpecificStock implements Stock { ConcreteObserver (‘MobileTraderApp‘):


private List < Trader > traders = new List < >() ;
private String symbol ; // Concrete Observer
private double price ; class Mob ile Tra der App implements Trader {
public SpecificStock ( String symbol , double private String appName ;
price ) { public Mo bil eTr ade rAp p ( String name ) {
this . symbol = symbol ; this . price = price ; this . appName = name ; }
}
public void setPrice ( double newPrice ) { @Override
this . price = newPrice ; public void update ( String stockSymbol , double
notifyTraders () ; // Notify on price change price ) {
} System . out . println ( appName + " Notified : " +
@Override public void registerTrader ( Trader t ) stockSymbol + " is now
{ traders . add ( t ) ; } $ " + price ) ;
@Override public void removeTrader ( Trader t ) { // Could trigger buy / sell logic here
traders . remove ( t ) ; } }
65
Observer: Advantages & Disadvantages

Advantages Disadvantages
• Loose Coupling: The Subject and its Observers are • Unexpected Updates / Update Storm: If Observers,
loosely coupled. The Subject only knows that its upon receiving an update, trigger further state changes
observers implement the ‘Observer‘ interface. Observers in the Subject (or other observed objects), it can lead to
can be added or removed without affecting the Subject a cascade of updates, potentially infinite loops if not
or other Observers. designed carefully.
• Dynamic Relationships: The relationship between • Order of Notification Not Guaranteed: The sequence
Subjects and Observers can be established and modified in which Observers are notified is often not defined or
dynamically at runtime. guaranteed.
• Broadcast Communication: The Subject can notify any • Dangling References / Memory Leaks: If Observers are
number of registered Observers simultaneously without not explicitly detached from the Subject when they are
needing to know their concrete types or even how many no longer needed, it can lead to memory leaks.
there are. • Performance Issues with Many Observers: Notifying a
• Open/Closed Principle Adherence: New types of very large number of observers sequentially can
Observers can be introduced without modifying the sometimes impact performance.
Subject’s existing code. • Data Transmission Strategy (Push vs. Pull): Deciding
• Reusability: Both Subject and Observer classes can whether the Subject should push all relevant state data
often be reused independently in different contexts. with the notification or if Observers should pull data. 66
Behavioral Patterns

Strategy

67
Strategy: Concept, Intent & UML
Definition: A behavioral design pattern that defines a family of algorithms, encapsulates each algorithm, and
makes them interchangeable. Strategy lets the algorithm vary independently from the clients that use it.

Typical UML Structure:


Intent & Problem Solved

• Enable selecting an algorithm from a family of


algorithms at runtime.
• Allow easy addition of new algorithms or variations
without modifying existing client code.

Core Idea & Participants


• Strategy (Interface/Abstract Class): Declares a
common interface for all supported algorithms.
Client configures Context with a ConcreteStrategy.
• ConcreteStrategy: Implements a specific Context delegates behavior to the chosen Strategy.
algorithm adhering to the ‘Strategy‘ interface.
68
Strategy: Java Implementation Example
Scenario: A text formatting application that can apply different formatting strategies (e.g.,
Markdown, HTML) to a piece of text.

Strategy Interface: Context (‘TextEditor‘):


interface T e x t F o r m a t t i n g S t r a t e g y { // Context
String format ( String text ) ; class TextEditor {
} private T e x t F o r m a t t i n g S t r a t e g y formatter ;
private String currentText ;

ConcreteStrategies: // Set strategy via constructor or setter


public TextEditor ( T e x t F o r m a t t i n g S t r a t e g y
class M a r k d o w nFo rm att er implements initialFo r matter ) {
TextFormattingStrategy { this . formatter = i nitialFormatter ;
@Override public String format ( String text ) { }
return " _Markdown : _ " + text . replace ( " " , public void setFormatter ( T e x t F o r m a t t i n g S t r a t e g y
"_") + "_"; newFormatter ) {
} this . formatter = newFormatter ;
} }
class HtmlFormatter implements public void setText ( String text ) {
TextFormattingStrategy { this . currentText = text ;
@Override public String format ( String text ) { }
return " <p > <b > HTML : </b > " + text + " </p > " ; // Context delegates formatting to the current
} strategy
public String g etFormattedText () {
69
}
Strategy: Advantages & Disadvantages

Advantages Disadvantages
• Interchangeable Algorithms: Families of related • Increased Number of Objects: Can lead to a
algorithms can be defined and easily swapped at proliferation of small strategy objects if there are
runtime. This provides high flexibility. many variations of algorithms, potentially
• Open/Closed Principle Adherence: New increasing the overall object count in the system.
strategies (algorithms) can be added without • Client Awareness of Strategies: Clients often
modifying the ‘Context‘ class or other existing need to be aware of the different available
strategy classes. strategies to choose and configure the ‘Context‘
• Reduces Conditional Logic: Eliminates complex appropriately. (This can sometimes be mitigated by
‘if-else‘ or ‘switch‘ statements for selecting different using a Factory to create/select strategies for the
behaviors within the ‘Context‘, leading to cleaner client).
code. • Communication Overhead: There’s a minor
• Isolation of Algorithm Details: Specific algorithm overhead due to the delegation of work from the
implementations are encapsulated within their own ‘Context‘ to the ‘Strategy‘ object (an extra method
‘ConcreteStrategy‘ classes, improving code call). This is usually negligible in most applications.
70
organization, readability, and testability.
Antipatterns

71
Antipatterns
Definition:
• Collections of past negative experiences where design solutions went wrong.
• Serve as practices to avoid, opposite to proactive design patterns.
Antipattern Characteristics:
• Most software projects face failure due to poor design decisions.
• Highlight problematic approaches, often leading to outright project cancellations.
• Derived from reactionary, flawed experiences in software development.
Facets of Antipatterns (Koenig, 1995):
• Bad Solutions: Describe ineffective approaches that lead to adverse situations.
• Recovery Paths: Outline how to escape these situations and reach a better solution.
Reference: Johnny Johnson’s article, “Creating Chaos,” discusses the prevalence of project
cancellations. 72
The Utility of Antipatterns
Why Antipatterns Are Useful:
• They help identify root causes of design issues that have already occurred.
• The concept of root-cause analysis allows the examination of failed design patterns.
• Antipatterns emerge from the failure of previous solutions, providing valuable hindsight.
Scott Ambler’s Reuse Patterns and Antipatterns:
• Robust Artifact Pattern:
• Well-documented and thoroughly tested item that meets general needs.
• Multiple examples show how to work with it.
• Easy to understand and work with, making it more reusable.
• Reuseless Artifact Antipattern:
• An artifact mistakenly declared reusable but never actually reused.
• Requires reworking to become a robust artifact that meets wider requirements.

Continuous Refactoring: Antipatterns promote revising and refactoring designs to find


73
workable solutions.
Conclusion

74
Chapter Summary

Concept of Design Patterns:

• Design patterns are rooted in everyday life and should shape how we think about
object-oriented design.
• Solutions are often derived from real-life situations, providing practical relevance.

Further Exploration:

• This chapter covered design patterns briefly but highlighted their importance.
• To delve deeper into design patterns and their practical applications, consult the
recommended books listed at the chapter’s end.

More Patterns: refactoring.guru

75
MC322 - Object Oriented Programming
Lesson 9
Design Patterns

Prof. Marcos M. Raimundo


Instituto de Computação - UNICAMP

You might also like