Thanks to visit codestin.com
Credit goes to www.scribd.com

0% found this document useful (0 votes)
63 views89 pages

Design Patterns

Uploaded by

S M A
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)
63 views89 pages

Design Patterns

Uploaded by

S M A
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/ 89

DESIGN PATTERNS Semester 8

DESIGN PATTERNS

Chapter #1

Overview of Object-Oriented Design

Object-Oriented Design (OOD) is a method used in software engineering that


involves the planning of a system of interacting objects to solve a specific problem. It
is based on several core concepts and principles that aim to create modular, reusable,
and maintainable software. Here’s an in-depth look at OOD:

 Core Concepts

Class and Object

 Class: A blueprint for creating objects (instances). It defines a type of object according to its
attributes (data) and methods (functions).
 Object: An instance of a class. It contains data and behavior as defined by its class.

Encapsulation

 Bundling of data (attributes) and methods (functions) that operate on the data into a single unit
or class.
 Restricts direct access to some of an object’s components, which can prevent the accidental
modification of data.

Abstraction

 Simplifying complex reality by modeling classes appropriate to the problem.


 Hiding the complex implementation details and showing only the essential features of the object.

Inheritance

 A mechanism for creating a new class from an existing class.


 Allows a new class (subclass or derived class) to inherit attributes and methods from an existing
class (superclass or base class).
 Promotes code reuse and establishes a natural hierarchy.

Polymorphism

 Allows methods to do different things based on the object it is acting upon.


 Supports method overriding (runtime polymorphism) and method overloading (compile-time
polymorphism).

 Principles of Object-Oriented Design

SOLID Principles

1. Single Responsibility Principle: A class should have only one reason to change,
meaning it should only have one job or responsibility.
DESIGN PATTERNS Semester 8

2. Open/Closed Principle: Objects or entities should be open for extension but closed
for modification.
3. Liskov Substitution Principle: Objects of a superclass should be replaceable with
objects of a subclass without affecting the correctness of the program.
4. Interface Segregation Principle: No client should be forced to depend on methods it
does not use. Interfaces should be client-specific rather than general-purpose.
5. Dependency Inversion Principle: High-level modules should not depend on low-level
modules. Both should depend on abstractions.

DRY (Don't Repeat Yourself)

1. Avoid duplication of code by abstracting out things that are common and placing
them in a single location.

KISS (Keep It Simple, Stupid)

1. Design systems that are as simple as possible but not simpler. Avoid unnecessary
complexity.

YAGNI (You Aren't Gonna Need It)

1. Don't add functionality until it is necessary. Avoid over-engineering.

 Benefits of Object-Oriented Design

Modularity

1. The source code for an object can be written and maintained independently of the
source code for other objects.

Reusability

1. Once a class is written, it can be used repeatedly in different programs.

Pluggability and Debugging Ease

1. If a particular object turns out to be problematic, it can be removed and replaced


with a better object.

Scalability

1. Makes it easier to manage large-scale software projects as it allows for incremental


development.

 Design Process

Requirement Analysis

 Understand what the system needs to do and identify key requirements.


DESIGN PATTERNS Semester 8

System Design

 Create high-level architecture and define the overall system structure.

Object Design

 Identify the classes, their relationships, and interactions.

Implementation

 Write the actual code for the classes and objects.

Testing

 Ensure that the system meets the requirements and works as intended.

Example

Consider designing a library management system. You might have the following
classes:

 Book: Attributes might include title, author, ISBN, and methods could include checkOut(),
returnBook().
 Member: Attributes might include name, memberID, and methods could include
borrowBook(), returnBook().
 Library: Manages collections of books and members, handles the borrowing and returning of
books.

By modeling the system using OOD principles, each class has a clear responsibility,
and the interactions between objects (such as a member borrowing a book) are well-
defined and easy to manage.

Object-Oriented Design provides a clear methodology to organize and structure


software programs, making them more robust, scalable, and easier to maintain.
DESIGN PATTERNS Semester 8

Overview of UML (Unified Modeling Language)

Unified Modeling Language (UML) is a standardized visual language used for


modeling the structure, behavior, and interactions of a software system. UML helps
developers, architects, and stakeholders visualize and document system design,
making it easier to understand, communicate, and develop complex systems.

Key UML Diagrams

Structural Diagrams:

1. Class Diagram: Shows the static structure of the system, including classes, attributes,
methods, and relationships between classes (inheritance, association, aggregation,
composition).

2. Object Diagram: Represents instances of classes and their relationships at a


particular moment in time.
DESIGN PATTERNS Semester 8

3. Component Diagram: Illustrates the organization and dependencies among


software components.
4. Deployment Diagram: Displays the physical arrangement of hardware and software
components in the system.

5. Package Diagram: Organizes classes and other elements into packages, showing
dependencies between packages.
DESIGN PATTERNS Semester 8

Behavioral Diagrams:

1. Use Case Diagram: Represents the functionality of the system from a user’s
perspective, showing actors, use cases, and their interactions.

2. Activity Diagram: Depicts the workflow or business processes, showing the


sequence of activities and decisions.

3. State Machine Diagram: Describes the states of an object and the transitions
between those states.
DESIGN PATTERNS Semester 8

4. Sequence Diagram: Shows how objects interact in a particular sequence,


highlighting the order of message exchanges.

5. Collaboration Diagram (Communication Diagram): Focuses on the interactions


between objects, showing how objects collaborate to achieve a goal.
6. Timing Diagram: Represents the change in state or condition of a class instance or
role over time.
7. Interaction Overview Diagram: Combines elements from activity and sequence
diagrams to provide an overview of control flow.

Other Diagrams:

1. Composite Structure Diagram: Represents the internal structure of a class and the
collaborations that this structure makes possible.
2. Profile Diagram: Used to create new types of UML diagrams or customize existing
ones.

Overview of OCL (Object Constraint Language)

Object Constraint Language (OCL) is a formal language used to describe


expressions on UML models. These expressions typically specify invariants,
preconditions, postconditions, and other constraints that cannot be captured by UML
diagrams alone. OCL enhances the precision and expressiveness of UML models,
ensuring that the specified rules and constraints are adhered to within the system.

Key Concepts in OCL


DESIGN PATTERNS Semester 8

Constraints:

1. Invariants: Conditions that must always hold true for instances of a class. Invariants
are used to ensure that the system's state remains consistent.
2. Preconditions: Conditions that must be true before an operation can be executed.
They define the valid states in which an operation can be invoked.
3. Postconditions: Conditions that must be true after an operation has been executed.
They define the expected state of the system after the operation completes.
4. Guard Conditions: Conditions that must be true for a transition to occur in a state
machine diagram. Guard conditions control the flow of execution in state machines.

Expressions:

1. OCL expressions are used to navigate models, access attributes, and perform
operations on collections of objects. They include logical operations (and, or, not),
arithmetic operations (+, -, *, /), and comparison operations (=, <, >).

Collection Types:

1. OCL supports various collection types like sets, bags, sequences, and ordered sets.
These collections allow for powerful operations on groups of objects.
2. Common operations on collections include select, collect, forAll, exists,
size, and sum.

Context:

1. OCL expressions are always written in a specific context, which defines the type of
object to which the constraint applies. The context is usually specified as a class or
operation in a UML model.

Introduction to design patterns

Design Patterns are reusable solutions to common problems that software developers encounter
during software design. They provide a template for how to solve problems in a way that has been
proven to work in the past. By using design patterns, developers can write more maintainable,
scalable, and robust code.
DESIGN PATTERNS Semester 8

 Why Use Design Patterns?

Reusability:

1. Design patterns provide a proven solution that can be reused in different parts of a
system or in different projects.

Communication:

1. They offer a common vocabulary for designers and developers, improving


communication and understanding.

Maintainability:

1. By using well-known patterns, code becomes easier to maintain and extend because
the design is familiar to other developers.

Efficiency:

1. Patterns provide a shortcut to solving complex design problems, reducing


development time and effort.

 Types of Design Patterns

As per the design pattern reference book Design Patterns - Elements


of Reusable Object-Oriented Software , there are 23 design patterns
which can be classified in three categories: Creational, Structural and
Behavioral patterns. We'll also discuss another category of design pattern:
J2EE design patterns.

Creational Patterns:

1. Deal with object creation mechanisms, trying to create objects in a manner suitable
to the situation. They abstract the instantiation process, making the system
independent of how its objects are created, composed, and represented.
2. Examples: Singleton, Abstract Factory, Builder, Prototype.

Structural Patterns:

1. Concerned with how classes and objects are composed to form larger structures.
They ensure that if one part of a system changes, the entire system doesn’t need to
change.
2. Examples: Facade, Composite, Bridge, Proxy, Adapter, Decorator.

Behavioral Patterns:
DESIGN PATTERNS Semester 8

1. Deal with object collaboration and the assignment of responsibilities between


objects. They help manage complex control flows that are difficult to follow at
runtime.
2. Examples: Chain of Responsibility, Visitor, Observer, Iterator, Command, Mediator,
Strategy, Interpreter, Memento.

 Example Design Patterns

Singleton Pattern:

1. Ensures a class has only one instance and provides a global point of access to it.
2. Use Case: Managing a single instance of a class like a configuration manager or
logging utility.
3. Example: A single database connection object used throughout an application to
ensure consistent access.

Factory Method Pattern:

1. Defines an interface for creating an object, but lets subclasses alter the type of
objects that will be created.
2. Use Case: When a class cannot anticipate the class of objects it must create.
3. Example: Document creation framework where the type of document (Word, PDF)
is determined at runtime.

Observer Pattern:

1. Defines a one-to-many dependency between objects so that when one object


changes state, all its dependents are notified and updated automatically.
2. Use Case: Implementing event handling systems or updating UI components in
response to data changes.
3. Example: A news application where multiple views (UI components) need to update
whenever there is a change in news data.

Decorator Pattern:

1. Allows behavior to be added to individual objects, either statically or dynamically,


without affecting the behavior of other objects from the same class.
2. Use Case: Adding responsibilities to objects dynamically and transparently.
3. Example: Adding scroll bars to a window, or adding borders or other decorations to
UI components.

Strategy Pattern:

1. Defines a family of algorithms, encapsulates each one, and makes them


interchangeable. Strategy lets the algorithm vary independently from the clients
that use it.
2. Use Case: Selecting an algorithm at runtime.
3. Example: A sorting framework where the sort algorithm can be chosen based on the
type of data or required performance.

Comparison of Coupling and Cohesion


DESIGN PATTERNS Semester 8

Coupling and cohesion are important concepts in software design that describe how
parts of a system relate to each other. Understanding these concepts helps in creating
better, more maintainable software.

 Coupling

 Definition: Coupling refers to how much one module (or component) depends on another
module.
 Goal: Aim for low coupling, which means modules should have few dependencies on each
other.
 Impact: High coupling means changes in one module can affect others, making the system
harder to maintain. Low coupling makes the system easier to maintain and modify.
 Examples:
o High Coupling: Module A directly accesses the data or functions of Module B.
o Low Coupling: Module A interacts with Module B through a well-defined interface,
minimizing direct dependencies.

 Cohesion

 Definition: Cohesion refers to how closely related and focused the responsibilities of a single
module are.
 Goal: Aim for high cohesion, which means a module should do one specific thing and do it
well.
 Impact: High cohesion makes a module easier to understand, maintain, and reuse. Low
cohesion means a module does many unrelated tasks, making it harder to manage.
 Examples:
o High Cohesion: A module that handles all user inputs.
o Low Cohesion: A module that handles user inputs, data processing, and file
operations.

 Comparison Table

Aspect Coupling Cohesion


The level of dependency The degree to which tasks in a
Definition
between modules. module are related.
Achieve low coupling (few Achieve high cohesion (focused
Goal
dependencies). tasks).
Impact on High coupling: Changes in one High cohesion: Changes are
Change module affect others. localized within the module.
Low coupling simplifies High cohesion simplifies
Maintenance
maintenance. understanding and maintenance.
Low coupling improves High cohesion improves
Reusability
reusability. reusability.
- Modules share a lot of data or - Modules are self-contained and
Examples
functions. handle specific tasks.

Practical Tips

Low Coupling:
DESIGN PATTERNS Semester 8

o Use interfaces or abstract classes to separate modules.


o Avoid direct access to the internal details of other modules.

High Cohesion:

o Ensure each module has a single responsibility.


o Group related functions and data together in one module.

Summary

 Low Coupling: Modules should have minimal dependencies on each other, making the
system easier to change and maintain.
 High Cohesion: Each module should perform a single, well-defined task, making it easier to
understand and reuse.

By aiming for low coupling and high cohesion, you can create software that is easier
to manage, extend, and scale.

Chapter # 2

Creational Design Patterns

Creational design patterns are focused on the process of object creation. They
provide various ways to create objects while hiding the creation logic, making the
system independent of how its objects are created, composed, and represented. This
approach can lead to more flexible and reusable code.

Creational patterns give a lot of flexibility in what gets created, who creates it, how it
gets created, and, when.

There are two recurring themes in these patterns:

 They all encapsulate knowledge about which concrete class the system uses.
 They hide how instances of these classes are created and put together.

Example of Creational Design Patterns


Building a maze for a computer game, the maze and the game will vary
slightly from pattern to pattern. We ignore what can be in a maze and
whether a maze game has single or multiple players. Instead, we just
focus on how the maze is created. We define a maze as a set of rooms, a
room knows its neighbors; possible neighbors are another room, a wall, or
a door to another room.
Sometimes the game will be simply to find your way out of a maze; in that
case player will probably only have a local view of the maze. Sometimes
DESIGN PATTERNS Semester 8

the maze contains problems to solve and dangers to overcome and these
games may provide a map of the part of the maze that has been explored.

Types of Creational Design Patterns


 Factory Method Design Patterns
 Abstract Factory Method Design Patterns
 Singleton Method Design Pattern
 Prototype Method Design Patterns
 Builder Method Design Patterns

Singleton Method Design Pattern

The Singleton method or Singleton Design pattern is one of the simplest


design patterns. It ensures a class only has one instance, and provides a
global point of access to it.

 Key Characteristics

1. Single Instance: Only one instance of the class is created.


2. Global Access Point: The single instance is globally accessible.
3. Controlled Access: The pattern controls the instantiation to ensure only one instance exists.

 Use Cases
DESIGN PATTERNS Semester 8

 Configuration Management: A single configuration manager that holds application settings.


 Logging: A single log manager that writes log messages to a file.
 Resource Management: Managing a single connection to a resource like a database or a
hardware device.

 When to use Singleton Method Design Pattern?


Use the Singleton method Design Pattern when:
 There must be exactly one instance of a class and it must be accessible to clients from
a well-known access point.
 When the sole instance should be extensible by subclassing and clients should be able
to use an extended instance without modifying
 Singleton classes are used for logging, driver objects, caching, and thread pool,
database connections.
 Initialization Types of Singleton
Singleton class can be instantiated by two methods:
 Early initialization : In this method, class is initialized whether it is to be used or not.
The main advantage of this method is its simplicity. You initiate the class at the time of
class loading. Its drawback is that class is always initialized whether it is being used or not.
 Lazy initialization : In this method, class in initialized only when it is required. It can
save you from instantiating the class when you don’t need it. Generally, lazy initialization is
used when we create a singleton class.

 Key Component of Singleton Method Design Pattern in


JavaScript:

Static Member:

The Singleton pattern or pattern Singleton employs a static member


within the class. This static member ensures that memory is allocated
only once, preserving the single instance of the Singleton class.
Java
// Static member to hold the single instanceprivate static Singleton
instance;
DESIGN PATTERNS Semester 8

Private Constructor:

The Singleton pattern or pattern singleton incorporates a private


constructor, which serves as a barricade against external attempts to
create instances of the Singleton class. This ensures that the class has
control over its instantiation process.
Java
// Private constructor to// prevent external instantiationclass
Singleton {

// Making the constructor as Private private Singleton()


{ // Initialization code here }}

Implementation of Singleton Method Design Pattern


The implementation of a Singleton Design Pattern or Pattern Singleton is
described in the following class diagram:

Abstract Factory Pattern

 Definition

The Abstract Factory pattern is a creational design pattern that provides an interface
for creating families of related or dependent objects without specifying their concrete
classes. It allows you to create objects that are part of a common theme or family
while promoting flexibility and scalability.

 Key Characteristics

 Interface for Families: Offers an interface for creating a set of related objects.
 Decouples Code: Reduces dependencies on specific implementations, allowing for easier
changes and extensions.
 Flexible: Supports the creation of different families of products without changing the code
that uses them.

 Use Cases
DESIGN PATTERNS Semester 8

 UI Frameworks: Creating UI elements that adapt to different operating systems (e.g.,


Windows vs. macOS).
 Game Development: Creating different types of characters, weapons, or environments that
share common features.

 Diagram

Here’s a simple diagram to illustrate the Abstract Factory pattern:

 AbstractFactory: Declares the methods for creating abstract products.


 ConcreteFactory: Implements the creation methods for a specific family of products.
 Product: Represents the various types of products created by the factories.

 Flow of the Abstract Factory Pattern

1. Client Code: The client code interacts with the abstract factory interface to create products
without needing to know their specific classes.
2. Concrete Factory Selection: Depending on the configuration (e.g., operating system or
environment), a specific concrete factory is selected to create the products.
3. Product Creation: The concrete factory creates the corresponding products that fit the
required family.

 Benefits of Abstract Factory Pattern

 Consistency: Ensures that products created within a family are compatible and consistent.
 Flexibility: Easily switch between different product families without changing existing code.
 Encapsulation: Encapsulates the creation logic, promoting cleaner code architecture.

 Drawbacks of Abstract Factory Pattern

 Complexity: Introduces additional layers of abstraction, which can complicate the design.
DESIGN PATTERNS Semester 8

 Overhead: If the system has many product families and variations, it can lead to a large
number of classes.

Summary

The Abstract Factory pattern is a powerful design pattern for creating families of
related objects without specifying their concrete classes. It promotes flexibility,
consistency, and decoupling in the design, making it easier to manage complex
systems.

Builder Pattern

Definition

The Builder pattern is a creational design pattern that separates the construction of a
complex object from its representation. This allows the same construction process to
create different representations of the object.

Key Characteristics

 Separation of Concerns: Focuses on constructing an object step by step, separating the


construction logic from the final object representation.
 Flexible Object Creation: Enables the creation of different types of objects using the same
building process.

Use Cases

 Complex Objects: Constructing complex objects like a multi-part document, a meal with
multiple courses, or a house with various components.
 Object Configuration: When objects need to be configured with various options that may
not be known at compile time.

Diagram

Here’s a simple diagram to illustrate the Builder pattern:


DESIGN PATTERNS Semester 8

 Builder: Defines methods for building parts of the product.


 ConcreteBuilder: Implements the builder interface to construct specific parts and assemble
the final product.
 Product: Represents the complex object being constructed.

Flow of the Builder Pattern

1. Client Code: The client interacts with a builder to create an object step by step.
2. Building Process: The builder constructs various parts of the product through defined
methods.
3. Final Assembly: Once all parts are built, the builder assembles and returns the final product.

Benefits of Builder Pattern

 Readability: Improves code readability by clearly separating the construction process from
the final object.
 Complex Object Creation: Facilitates the creation of complex objects that require multiple
steps or configurations.
 Flexibility: Allows for the creation of different representations of an object without
modifying the code that constructs it.
DESIGN PATTERNS Semester 8

Drawbacks of Builder Pattern

 Complexity: Introduces additional classes and interfaces, which can increase the overall
complexity of the design.
 Overhead: May be unnecessary for simple objects that can be created with simple
constructors.

Summary

The Builder pattern is an effective solution for constructing complex objects step by
step, promoting clarity and flexibility in object creation. It is particularly useful when
dealing with objects that have multiple attributes or require intricate setup processes.

Prototype Pattern

 Definition

The Prototype pattern is a creational design pattern that enables creating new objects
by copying an existing object, known as the prototype. This pattern is useful when the
cost of creating a new instance of an object is more expensive than copying an
existing one.

 Key Characteristics

 Object Cloning: Allows for creating new instances by duplicating a prototype rather than
creating new objects from scratch.
 Flexible Object Creation: Supports the dynamic creation of objects at runtime, facilitating
easier configurations.

 Use Cases

 Complex Objects: When objects have many configuration options and creating them from
scratch is costly.
 Object Pooling: When managing a large number of similar objects, such as in graphical
applications or game development.

 Diagram

Here’s a simple diagram to illustrate the Prototype pattern:


DESIGN PATTERNS Semester 8

 Prototype: Defines the interface for cloning itself.


 ConcretePrototype: Implements the cloning operation, creating copies of itself.

Flow of the Prototype Pattern

1. Prototype Creation: A prototype instance is created and configured with the necessary
properties.
2. Cloning: When a new instance is needed, the client calls the clone method on the
prototype to create a copy of it.
3. Modification: The cloned object can then be modified as needed without affecting the
original prototype.

Benefits of Prototype Pattern

 Performance: Reduces the overhead of creating new instances, especially for complex
objects.
DESIGN PATTERNS Semester 8

 Flexibility: Enables easy changes to object configurations without requiring complex


constructors or initialization logic.

Drawbacks of Prototype Pattern

 Complexity: Cloning can become complicated if the objects have circular references or need
deep copies of nested objects.
 Maintenance: Managing and maintaining prototypes can lead to increased complexity in the
design.

Summary

The Prototype pattern is a valuable design pattern for efficiently creating new objects
by copying existing ones. It promotes flexibility and performance in scenarios where
object creation is costly and allows for easy customization of instances.

Chapter# 3

Structural Patterns

Structural Patterns

Structural design patterns are concerned with how classes and objects are composed
to form larger structures. These patterns help ensure that if one part of a system
changes, the entire system does not need to do so. They facilitate the design by
identifying a simple way to realize relationships among entities.

Example for Structural Design Patterns


Consider an example, a drawing editor that lets users draw and arrange
graphical elements (lines, polygons, text, etc.) into pictures and diagrams.
The drawing editor’s key abstraction is the graphical object, which has an
editable shape and can draw itself.
The interface for graphical objects is defined by an abstract class
called Shape. The editor defines a subclass of the Shape for each kind of
graphical object: a LineShape class for lines, a PolygonShape class for
polygons, and so forth.

Types of Structural Design Patterns:


DESIGN PATTERNS Semester 8

 Facade Pattern

Definition

The Facade pattern is a structural design pattern that provides a simplified interface to
a complex subsystem. It hides the complexities of the subsystem and provides a
unified, easier-to-use interface for the client.

Key Characteristics

 Simplification: Simplifies interactions with a complex system.


 Encapsulation: Encapsulates the subsystem’s functionality behind a single interface.
 Client Convenience: Reduces the learning curve and interaction complexity for clients.

Use Cases

 Complex Libraries: Simplifying interactions with complex libraries or frameworks.


 Legacy Systems: Providing a simpler interface to legacy systems with complicated APIs.
 Subsystems: Managing interactions with multiple subsystems.
DESIGN PATTERNS Semester 8

In the above diagram,


 Structuring a system into subsystems helps reduce complexity.
 A common design goal is to minimize the communication and dependencies
between subsystems.
 One way to achieve this goal is to introduce a Facade object that provides a single
simplified interface to the more general facilities of a subsystem.

Flow of the Facade Pattern

1. Client Requests: The client interacts with the facade instead of directly interacting with the
subsystems.
2. Facade Methods: The facade provides high-level methods that encapsulate the complexity of
the subsystem interactions.
3. Delegation: The facade forwards the client requests to the appropriate subsystem objects.

Benefits of Facade Pattern

 Simplified Interface: Provides a straightforward interface to complex subsystems, making


the subsystem easier to use.
 Loose Coupling: Decouples the client from the complex subsystems, leading to a more
modular design.
 Improved Maintainability: Changes in the subsystem do not affect the client as long as the
facade interface remains unchanged.

Drawbacks of Facade Pattern

 Limited Functionality Exposure: The facade may not expose all the functionalities of the
subsystem, leading to limited access for the client.
 Over-simplification: There is a risk of oversimplifying the subsystem’s functionality, which
may lead to an inadequate interface for advanced clients.

Example: Home Theater System

Imagine a home theater system with complex subsystems like DVD player, projector,
sound system, and lights. The facade simplifies their operation for the user.

Subsystems

 DVD Player
 Projector
DESIGN PATTERNS Semester 8

 Sound System
 Lights

Facade Class

The facade class provides a simple interface to control the home theater system.

Composite Pattern

Definition

The Composite pattern is a structural design pattern that allows you to compose
objects into tree structures to represent part-whole hierarchies. This pattern enables
clients to treat individual objects and compositions of objects uniformly.
DESIGN PATTERNS Semester 8

Key Characteristics

 Hierarchy Representation: Represents part-whole hierarchies of objects.


 Uniform Treatment: Treats individual objects and compositions uniformly.
 Recursive Structure: The pattern is naturally recursive, as components can be composed of
other components.

Use Cases

 File Systems: Directories contain files and other directories.


 Graphic Editors: Shapes can be simple (like lines and circles) or complex (composed of
multiple shapes).
 GUI Components: Containers contain components, which can be other containers or leaf
elements.

Diagram

Here’s a simple diagram to illustrate the Composite pattern:

 Component: Declares the interface for objects in the composition.


 Leaf: Represents leaf objects in the composition. A leaf has no children.
 Composite: Represents a composite component that can have children. Implements child-
related operations (add, remove, getChild).

Flow of the Composite Pattern

1. Component Interface: Defines common operations for both simple and complex objects.
DESIGN PATTERNS Semester 8

2. Leaf Class: Implements the component interface and represents objects with no children.
3. Composite Class: Implements the component interface and contains children. Defines
methods to add, remove, and access children.
4. Client Code: Interacts with objects through the component interface, treating both leaf and
composite objects uniformly.

Benefits of Composite Pattern

 Uniformity: Simplifies client code as it can treat individual objects and compositions
uniformly.
 Extensibility: New types of components can be added without changing existing code.
 Hierarchy Management: Provides a clear structure for managing part-whole hierarchies.

Drawbacks of Composite Pattern

 Overhead: Can introduce overhead when managing the composite structure, especially if the
hierarchy is deep.
 Complexity: The recursive structure can lead to complex implementations and debugging.

Example: Graphic Editor

Imagine a graphic editor where shapes can be simple (like circles and rectangles) or
complex (combinations of shapes).

Components of Composite Design Pattern

 Bridge Design Pattern

Definition

The Bridge pattern is a structural design pattern that decouples an abstraction from its
implementation so that the two can vary independently. It allows you to change both
the abstractions and the concrete implementations independently without affecting
each other.
DESIGN PATTERNS Semester 8

There are 2 parts in Bridge design pattern :


1. Abstraction
2. Implementation
This is a design mechanism that encapsulates an implementation class inside of an
interface class.
 The bridge pattern allows the Abstraction and the Implementation to be
developed independently and the client code can access only the Abstraction
part without being concerned about the Implementation part.
 The abstraction is an interface or abstract class and the implementer is also
an interface or abstract class.
 The abstraction contains a reference to the implementer. Children of the
abstraction are referred to as refined abstractions, and children of the
implementer are concrete implementers. Since we can change the reference to
the implementer in the abstraction, we are able to change the abstraction’s
implementer at run-time. Changes to the implementer do not affect client code.
 It increases the loose coupling between class abstraction and it’s
implementation.
UML Diagram of Bridge Design Pattern

Key Characteristics

 Decoupling: Separates the interface from the implementation.


 Independence: Allows for independent development and evolution of both the interface and
the implementation.
 Abstraction and Implementation Hierarchies: Enables multiple implementations for a single
abstraction.

Use Cases

 Graphic Systems: Separating the shape (abstraction) from how it is drawn (implementation).
DESIGN PATTERNS Semester 8

 Cross-Platform Applications: Developing applications that need to work on different


platforms by decoupling the platform-specific code from the core functionality.
 Device Drivers: Separating the interface provided to the user from the implementation
details of how the device works.

Benefits of Bridge Pattern

 Flexibility: Enables the abstraction and implementation to vary independently.


 Scalability: Easier to extend the system by adding new abstractions and implementations.
 Decoupling: Reduces the coupling between the abstraction and its implementation,
promoting a cleaner code structure.

Drawbacks of Bridge Pattern

 Complexity: Introduces additional classes and interfaces, which can increase the system’s
complexity.
 Initial Setup: Requires careful planning to set up the abstraction and implementor
hierarchies.

Example: Remote Control and Devices

Imagine you are designing a remote control system for different devices (e.g., TVs,
Radios). The remote control's interface should be independent of the device it controls.

Components

 Abstraction: The remote control.


 RefinedAbstraction: A specific type of remote control (e.g., advanced remote).
 Implementor: The device interface.
 ConcreteImplementors: Specific devices (e.g., TV, Radio).

 Proxy Pattern

Definition

The Proxy pattern is a structural design pattern that provides a surrogate or


placeholder for another object to control access to it. The proxy object acts as an
intermediary and can add functionality such as access control, lazy initialization,
logging, or caching.

Key Characteristics

 Intermediary: Acts as an intermediary between the client and the target object.
 Control Access: Controls access to the real object.
 Enhance Functionality: Can add additional behavior like lazy initialization, logging, or security
checks.
DESIGN PATTERNS Semester 8

Types of Proxies

1. Virtual Proxy: Controls access to an object that is expensive to create. It creates and
initializes the real object only when it is needed.
2. Protection Proxy: Controls access to an object based on access rights.
3. Remote Proxy: Represents an object located in a different address space (e.g., in a different
server or over a network).
4. Smart Proxy: Adds additional behavior when the object is accessed, such as reference
counting, logging, or access control.

Use Cases

 Lazy Initialization: Deferring the creation and initialization of expensive objects until they are
actually needed.
 Access Control: Controlling access to sensitive objects based on user permissions.
 Logging and Monitoring: Keeping track of the number of times an object is accessed or
operations performed on it.
 Remote Access: Facilitating access to objects in remote locations (e.g., remote servers).

Diagram

Here’s a simple diagram to illustrate the Proxy pattern:

Flow of the Proxy Pattern

1. Client Requests: The client interacts with the proxy instead of directly with the real subject.
2. Proxy Control: The proxy controls access to the real subject and may perform additional
operations.
3. Real Subject Access: The proxy delegates the request to the real subject when necessary.

Benefits of Proxy Pattern

 Controlled Access: Provides a way to control access to the real subject.


 Lazy Initialization: Delays the creation and initialization of expensive objects.
DESIGN PATTERNS Semester 8

 Additional Behavior: Adds functionality such as logging, security checks, and caching.

Drawbacks of Proxy Pattern

 Overhead: Introduces additional layers that may add complexity and reduce performance.
 Complexity: Can make the system more complex due to the extra layer of indirection.

Example: Image Viewer with Virtual Proxy

Imagine you are designing an image viewer application where loading and displaying
images is expensive. You can use a virtual proxy to delay the loading of images until
they are actually needed.

Components

 Image: The common interface for displaying images.


 RealImage: The actual image that is expensive to load.
 ProxyImage: The proxy that controls access to the RealImage and loads it only when
necessary.

Summary

The Proxy pattern provides a surrogate or placeholder for another object to control
access to it. It is useful in scenarios where objects are expensive to create, need to be
accessed remotely, or require controlled access. By introducing a proxy, you can add
additional behavior and manage the life cycle of the real objects more effectively. The
pattern promotes controlled access, lazy initialization, and enhanced functionality
without changing the client code.

 Adapter Pattern

Definition

The Adapter pattern is a structural design pattern that allows incompatible interfaces
to work together. It acts as a bridge between two incompatible interfaces by
converting the interface of a class into another interface the client expects.
DESIGN PATTERNS Semester 8

Key Characteristics

 Interface Conversion: Converts one interface into another that a client expects.
 Compatibility: Enables classes with incompatible interfaces to work together.
 Wrapper: Acts as a wrapper that translates requests from the client to the target class.

Use Cases

 Legacy Code Integration: Allowing new systems to work with legacy code that has a different
interface.
 Third-Party Libraries: Integrating third-party libraries with an application when their
interfaces do not match the application's expected interfaces.
 Different Data Formats: Converting data from one format to another to make it compatible
with a target system.

Diagram

Here’s a simple diagram to illustrate the Adapter pattern:

Flow of the Adapter Pattern

1. Client Requests: The client interacts with the Target interface.


2. Adapter Conversion: The adapter translates the client’s request to a call to the Adaptee’s
specificRequest method.
3. Adaptee Execution: The adaptee executes its specificRequest method.

Benefits of Adapter Pattern

 Reusability: Allows reuse of existing classes even if their interfaces do not match the
expected one.
 Flexibility: Decouples the client from the implementation details of the adapted class.
 Compatibility: Makes it easier to integrate incompatible systems.

Drawbacks of Adapter Pattern

 Complexity: Can introduce additional complexity by adding another layer of indirection.


DESIGN PATTERNS Semester 8

 Overhead: May add some overhead due to the extra level of wrapping.

Example: Media Player

Imagine you are designing a media player application that can play different audio
formats. The existing media player can only play MP3 files, but you need to add
support for MP4 and VLC formats.

Components

 MediaPlayer: The target interface for playing audio.


 AudioPlayer: The concrete class implementing MediaPlayer for MP3 files.
 AdvancedMediaPlayer: The interface for playing advanced media formats (MP4, VLC).
 Mp4Player: The concrete class for playing MP4 files.
 VlcPlayer: The concrete class for playing VLC files.
 MediaAdapter: The adapter class that implements MediaPlayer and translates requests to
AdvancedMediaPlayer.
 Client: The entity that uses the MediaPlayer interface.

 Decorator Pattern

Definition

The Decorator pattern is a structural design pattern that allows behavior to be added to
individual objects, either statically or dynamically, without affecting the behavior of
other objects from the same class. It involves a set of decorator classes that are used to
wrap concrete components.
DESIGN PATTERNS Semester 8

Key Characteristics

 Flexible and Extendable: Adds new functionality to objects dynamically without altering their
structure.
 Composition over Inheritance: Promotes composition over inheritance, allowing for greater
flexibility in extending functionality.
 Single Responsibility Principle: Helps to adhere to the single responsibility principle by
dividing functionality among classes with unique areas of concern.

Use Cases

 Enhancing Functionality: Adding responsibilities to individual objects.


 User Interface Components: Adding features to GUI components, like scrollbars, borders, etc.
 Logging, Authentication, and Authorization: Adding these concerns to objects without
altering their core logic.

Diagram

Here’s a simple diagram to illustrate the Decorator pattern:

+-------------+
| Client |
+-------------+
|
v
+-------------+
| Component |
+-------------+
| + operation() |
+-------------+
^
|
+-------------+
| Concrete |
| Component |
+-------------+
| + operation() |
+-------------+
^
|
+------------------+
| Decorator |
+------------------+
| - component: Component |
+------------------+
| + operation() |
+------------------+
DESIGN PATTERNS Semester 8

^
|
+----------------------+
| ConcreteDecoratorA |
+----------------------+
| + operation() |
+----------------------+
^
|
+----------------------+
| ConcreteDecoratorB |
+----------------------+
| + operation() |
+----------------------+

 Component: Defines the interface for objects that can have responsibilities added to them
dynamically.
 ConcreteComponent: A class that implements the Component interface.
 Decorator: Maintains a reference to a Component object and defines an interface that
conforms to Component's interface.
 ConcreteDecorator: Extends the Decorator class and adds responsibilities to the component.

Flow of the Decorator Pattern

1. Client Interaction: The client interacts with the decorated component through the
Component interface.
2. Decorator Chain: The decorator wraps the component and adds new functionality by
delegating calls to the wrapped component and adding its own behavior.
3. Dynamic Composition: Multiple decorators can be stacked on top of each other, creating a
flexible and dynamic composition of behaviors.

Benefits of Decorator Pattern

 Flexible Functionality: Provides a flexible alternative to subclassing for extending


functionality.
 Runtime Behavior Modification: Allows behavior modification at runtime by wrapping
objects in different decorators.
 Adherence to SOLID Principles: Helps in adhering to the single responsibility and
open/closed principles.

Drawbacks of Decorator Pattern

 Complexity: Can lead to a system with many small objects that are hard to understand and
maintain.
 Overhead: Introduces additional layers of wrapping which might affect performance.

Example: Coffee Shop


DESIGN PATTERNS Semester 8

Imagine you are designing a coffee shop application where you can add various
condiments to your coffee. Each type of coffee and condiment can be represented as
objects.

Components

 Beverage: The base interface for all beverages.


 ConcreteBeverage: A concrete class implementing Beverage, like Espresso or HouseBlend.
 CondimentDecorator: The decorator class that extends Beverage and adds functionality.
 ConcreteDecorator: Concrete decorators like Milk, Mocha, and Soy.

Chapter# 6
 Antipatterns

Definition

Antipatterns are recurring solutions to common problems that often result in negative
consequences. Unlike design patterns, which offer effective solutions, antipatterns are
poor practices that should be avoided.

Key Characteristics

 Common Practice: Frequently encountered in real-world development.


 Negative Consequences: Lead to maintenance issues, reduced performance, and other
negative outcomes.
 Educational Value: Understanding antipatterns helps in recognizing and avoiding them.

Common Antipatterns and Solutions

Spaghetti Code

o Description: Code with a complex and tangled structure, making it hard to


understand and maintain.
o Consequence: Difficult to debug, test, and modify.
o Solution: Refactor to improve modularity and readability.

God Object/God Class

o Description: A class that has too many responsibilities.


o Consequence: Becomes a bottleneck; hard to maintain and test.
o Solution: Decompose into smaller, focused classes.

Golden Hammer

o Description: Over-reliance on a familiar solution for all problems.


o Consequence: Inefficiency and inappropriate solutions.
o Solution: Choose solutions based on specific problem requirements

Copy and Paste Programming


DESIGN PATTERNS Semester 8

o Description: Duplicating code by copying and pasting.


o Consequence: Increased risk of bugs; hard to maintain.
o Solution: Use abstraction to create reusable components.

Not Invented Here (NIH) Syndrome

o Description: Avoiding third-party solutions, preferring in-house development.


o Consequence: Wasted resources; potential quality issues.
o Solution: Evaluate and adopt well-proven third-party solutions.

Cargo Cult Programming

o Description: Following practices without understanding their purpose.


o Consequence: Ineffective solutions; wasted effort.
o Solution: Understand the principles behind practices and apply them thoughtfully.

Reinventing the Wheel

o Description: Developing custom solutions for already solved problems.


o Consequence: Increased development time; potential lower quality.
o Solution: Leverage existing solutions.

Recovering from Bad Designs

1. Refactoring: Improving the design of existing code without changing its behavior. This
includes breaking down large classes, improving method names, and simplifying complex
logic.
2. Testing: Writing unit tests to ensure functionality remains intact during refactoring. Test-
driven development (TDD) can help create clean, testable code.
3. Code Reviews: Conducting regular code reviews to catch antipatterns early and share
knowledge among the team.
4. Continuous Integration and Deployment (CI/CD): Automating testing and deployment to
ensure smooth integration and deployment of changes.
5. Training and Education: Providing ongoing training on best practices and design patterns.

Refactoring to Patterns

Refactoring to patterns involves identifying and replacing antipatterns with


appropriate design patterns to improve code quality and maintainability. For example:

 From Spaghetti Code to Layered Architecture: Refactor disorganized code into well-defined
layers (e.g., presentation, business logic, data access).
 From God Class to Smaller Classes: Break down a God class into multiple classes with single
responsibilities.
 From Copy and Paste to Inheritance or Composition: Replace duplicated code with
inheritance or composition to promote reuse.

Introduction to Aspect-Oriented Design

Aspect-oriented design addresses cross-cutting concerns that affect multiple parts of


an application, such as logging, security, and transaction management. It allows these
DESIGN PATTERNS Semester 8

concerns to be separated from the main business logic, promoting cleaner and more
maintainable code.

Key Concepts

 Aspects: Modules that encapsulate behaviors affecting multiple classes.


 Join Points: Points in the program where aspects can be applied.
 Advice: Code that is executed at a join point specified by an aspect.
 Pointcuts: Expressions that match join points, determining where advice should be applied.
 Weaving: The process of applying aspects to a target object to create an advised object.

Summary

Understanding antipatterns is crucial for identifying and avoiding common pitfalls in


software development. Recovering from bad designs through refactoring, testing, and
code reviews can significantly improve code quality. Refactoring to patterns provides
a structured approach to replacing antipatterns with effective design patterns. Aspect-
oriented design offers a way to manage cross-cutting concerns, enhancing modularity
and maintainability.

 Anti-Patterns: Common Pitfalls and Examples

Anti-patterns are common responses to recurring problems that are


counterproductive and may result in negative consequences. Understanding these
pitfalls can help in avoiding them and improving the quality of software design.

1. Spaghetti Code

Description: Code that is tangled and difficult to follow due to lack of structure.

Pitfall: This happens when developers do not adhere to principles of modularity and
separation of concerns.

Example:

java
Copy code
public void processOrder(Order order) {
// Process order details
if (order.getType().equals("Standard")) {
// Standard processing
// ...
} else if (order.getType().equals("Express")) {
// Express processing
DESIGN PATTERNS Semester 8

// ...
} else if (order.getType().equals("International")) {
// International processing
// ...
}
// More conditions and processing
// ...
}

Solution: Refactor code into separate methods or classes for each type of order
processing.

2. God Object

Description: An object that knows too much or does too much, often leading to high
coupling and low cohesion.

Pitfall: This occurs when a single class takes on multiple responsibilities.

Example:

java
Copy code
public class OrderManager {
public void createOrder() {
// Create order
}

public void updateOrder() {


// Update order
}

public void deleteOrder() {


// Delete order
}

public void processPayment() {


// Process payment
}

public void generateInvoice() {


// Generate invoice
}
}
DESIGN PATTERNS Semester 8

Solution: Use the Single Responsibility Principle (SRP) to break down the class into
smaller, more focused classes.

3. Lava Flow

Description: Dead or redundant code that has been left in the codebase.

Pitfall: Accumulating unused code can make the codebase difficult to understand and
maintain.

Example:

java
Copy code
public class OrderService {
public void createOrder() {
// Old method (no longer used)
// ...
}

public void createNewOrder() {


// New method
// ...
}
}

Solution: Regularly review and refactor the codebase to remove unused or obsolete
code.

4. Golden Hammer

Description: The tendency to use a familiar technology or concept to solve every


problem, even when it's not the best fit.

Pitfall: This can lead to suboptimal solutions and increased complexity.

Example: Using a relational database for storing unstructured data when a NoSQL
database would be more appropriate.

java
Copy code
// Using relational DB for unstructured data
CREATE TABLE UnstructuredData (
id INT PRIMARY KEY,
DESIGN PATTERNS Semester 8

data TEXT
);

Solution: Choose technologies and patterns that best fit the specific problem domain.

5. Copy and Paste Programming

Description: Reusing code by copying and pasting it rather than creating reusable
components.

Pitfall: This leads to code duplication and can cause maintenance headaches.

Example:

java
Copy code
public void sendEmail(String recipient) {
// Send email code
// ...
}
public void sendNotification(String recipient) {
// Copy-pasted send email code
// ...
}

Solution: Abstract common functionality into reusable methods or classes.

6. Magic Numbers

Description: Using literal numbers in code without explanation, making the code
hard to understand and maintain.

Pitfall: Magic numbers make the code less readable and harder to update.

Example:

java
Copy code
public void calculateTotal(int quantity) {
double total = quantity * 19.99; // 19.99 is a magic number
}

Solution: Use named constants to give meaning to these values.


DESIGN PATTERNS Semester 8

java
Copy code
public static final double ITEM_PRICE = 19.99;public void
calculateTotal(int quantity) {
double total = quantity * ITEM_PRICE;
}

7. Poltergeists

Description: Short-lived, unnecessary objects that only serve to pass information


between other objects.

Pitfall: This creates unnecessary complexity and clutter in the codebase.

Example:

java
Copy code
public class OrderProcessor {
public void processOrder(Order order) {
OrderData data = new OrderData(order);
data.process();
}
}
public class OrderData {
private Order order;

public OrderData(Order order) {


this.order = order;
}

public void process() {


// Process order data
}
}

Solution: Eliminate intermediate objects and allow direct communication between


objects.

java
Copy code
public class OrderProcessor {
public void processOrder(Order order) {
// Process order directly
}
DESIGN PATTERNS Semester 8

By identifying and understanding these anti-patterns, developers can take proactive


measures to avoid them and implement best practices in their software design, leading
to more maintainable, efficient, and effective code.

If you need more information or examples on specific anti-patterns or other topics


related to design patterns, feel free to ask!

 what is the difference between Recovering from bad designs and


Refactoring to patterns

The difference between "Recovering from bad designs" and "Refactoring to patterns"
lies primarily in their objectives and approaches within software development:

1.

Recovering from Bad Designs:

2.
1. Objective: The primary goal here is to fix or salvage a poorly designed or
implemented system.
2. Focus: It focuses on addressing immediate issues and deficiencies in the existing
system that hinder its performance, scalability, maintainability, or other critical
aspects.
3. Activities: This process often involves identifying and rectifying specific problems
such as performance bottlenecks, security vulnerabilities, or architectural flaws.
4. Outcome: The result is typically a more stable and reliable system that meets basic
functional requirements but may not necessarily adhere to best practices or design
patterns.
3.

Refactoring to Patterns:

4.

1. Objective: The main aim is to systematically improve the design of a software


system by applying well-established design patterns.
2. Focus: It emphasizes restructuring the codebase to incorporate recognized design
patterns like Singleton, Factory Method, or Observer.
3. Activities: Developers analyze the existing code, identify areas where patterns can
be applied, and refactor the code accordingly.
4. Outcome: The outcome is a more modular, maintainable, and extensible system
that leverages proven solutions to common design problems. It aligns closely with
software engineering best practices and patterns.

In essence, "Recovering from bad designs" focuses on fixing immediate issues


without necessarily following established patterns, whereas "Refactoring to patterns"
DESIGN PATTERNS Semester 8

aims to systematically enhance the software by integrating well-recognized design


patterns for improved structure and maintainability.

 Introduction to aspect-oriented design: Aspects, Themes,


Concerns

Introduction to Aspect-Oriented Design

Aspect-oriented design (AOD) is a programming paradigm that aims to increase


modularity by allowing the separation of cross-cutting concerns. This approach
complements object-oriented programming by addressing issues that are difficult to
decompose using traditional methods.

Key Concepts

1. Aspects
2. Themes
3. Concerns

1. Aspects

Definition: An aspect is a modular unit of a cross-cutting concern. Aspects


encapsulate behaviors that affect multiple classes into reusable modules.

Characteristics:

 Encapsulation: Aspects encapsulate the behavior that cuts across multiple classes or
modules.
 Reusability: Aspects can be reused across different parts of an application.
 Separation of Concerns: By isolating cross-cutting concerns, aspects improve modularity and
maintainability.

Example: Consider logging as a cross-cutting concern. Instead of adding logging


code to every method, an aspect can handle it.

java
Copy code
@Aspectpublic class LoggingAspect {
@Before("execution(* com.example.service.*.*(..))")
public void logBefore(JoinPoint joinPoint) {
System.out.println("Logging before method: " +
joinPoint.getSignature().getName());
}
}
DESIGN PATTERNS Semester 8

2. Themes

Definition: Themes represent broader concerns or goals that influence the design of a
system. They are higher-level than aspects and can encompass multiple aspects.

Characteristics:

 High-Level Goals: Themes are used to express high-level design goals such as performance,
security, or usability.
 Guiding Principles: Themes guide the selection and implementation of aspects to ensure
they align with overall system goals.

Example: A security theme may include multiple aspects like authentication,


authorization, and encryption, all working together to achieve the goal of a secure
system.

3. Concerns

Definition: Concerns are the different aspects of functionality that a program must
address. They are typically divided into core concerns and cross-cutting concerns.

Types of Concerns:

 Core Concerns: Primary functionalities that define the main purpose of the system.
 Cross-Cutting Concerns: Secondary functionalities that affect multiple modules, such as
logging, security, and error handling.

Example: In an e-commerce application:

 Core Concern: Processing orders.


 Cross-Cutting Concerns: Logging, transaction management, and security.

Handling Concerns in AOD: Aspect-oriented design separates cross-cutting


concerns from core concerns to improve code modularity and maintainability. Each
aspect handles a specific cross-cutting concern without cluttering the core business
logic.

Benefits of Aspect-Oriented Design

1. Improved Modularity: By separating cross-cutting concerns into aspects, AOD improves the
modularity of the codebase.
2. Enhanced Maintainability: Aspects can be modified independently of the core logic, making
the code easier to maintain and evolve.
DESIGN PATTERNS Semester 8

3. Reusability: Aspects can be reused across different parts of an application or even across
different projects.
4. Cleaner Code: Core business logic remains uncluttered by cross-cutting concerns, making the
codebase cleaner and more readable.

Challenges of Aspect-Oriented Design

1. Complexity: Introducing aspects can add complexity to the system, especially for developers
unfamiliar with the paradigm.
2. Tooling and Frameworks: Effective use of AOD often requires specific tools and frameworks,
such as AspectJ for Java.
3. Performance Overhead: Depending on the implementation, aspects can introduce
performance overhead, particularly if not used judiciously.

Conclusion

Aspect-oriented design is a powerful paradigm that addresses the limitations of


traditional object-oriented programming by separating cross-cutting concerns into
modular aspects. By understanding and applying the concepts of aspects, themes, and
concerns, developers can create more modular, maintainable, and reusable software
systems.

If you need further details on specific aspects, themes, or concerns, or examples in


different programming languages, feel free to ask!

Chapter# 5
 Patterns for Concurrent and Distributed Systems

1. Concurrency Patterns
2. Distributed Systems Patterns

1. Concurrency Patterns

Concurrency patterns deal with the challenges of running multiple threads or


processes simultaneously. They help manage the complexities of parallel execution,
synchronization, and resource sharing.

1.1. Thread Pool

Description: Manages a pool of worker threads to perform tasks concurrently,


reusing threads to limit the overhead of thread creation and destruction.
DESIGN PATTERNS Semester 8

Benefits:

 Reduces the overhead of thread creation.


 Manages the number of concurrent threads.
 Improves resource utilization.

Example: In Java, ExecutorService provides a thread pool implementation.

java
Copy code
ExecutorService executor = Executors.newFixedThreadPool(10);for (int
i = 0; i < 100; i++) {
executor.submit(new Task());
}
executor.shutdown();

1.2. Future/Promise

Description: Represents a value that may be available at some point in the future,
allowing asynchronous computation and retrieval of results.

Benefits:

 Simplifies asynchronous programming.


 Avoids blocking the main thread.
 Allows better resource utilization.

Example: Using CompletableFuture in Java for asynchronous computation.

java
Copy code
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(()
-> {
// Perform computation
return 42;
});
future.thenAccept(result -> System.out.println("Result: " + result));

1.3. Producer-Consumer

Description: Decouples the production of data from its consumption using a shared
buffer, allowing producers and consumers to operate at different rates.

Benefits:

 Balances load between producers and consumers.


 Improves throughput and resource utilization.
 Simplifies synchronization between threads.
DESIGN PATTERNS Semester 8

Example: Using BlockingQueue in Java to implement the producer-consumer


pattern.

java
Copy code
BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10);
// Producernew Thread(() -> {
try {
queue.put(1);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
// Consumernew Thread(() -> {
try {
Integer item = queue.take();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();

2. Distributed Systems Patterns

Distributed systems patterns address the complexities of communication, coordination,


and fault tolerance in systems that run on multiple machines.

2.1. Client-Server

Description: A foundational pattern where clients request services and resources


from servers.

Benefits:

 Centralized control and management.


 Scalability by adding more servers.
 Separation of concerns between client and server.

Example: A simple HTTP server and client interaction.

python
Copy code
# Serverfrom flask import Flask, jsonify
app = Flask(__name__)
@app.route('/data')def get_data():
return jsonify({'key': 'value'})
if __name__ == '__main__':
DESIGN PATTERNS Semester 8

app.run(port=5000)
# Clientimport requests
response =
requests.get('http://localhost:5000/data')print(response.json())

2.2. Publish-Subscribe

Description: A messaging pattern where publishers send messages to a channel and


subscribers receive messages from that channel.

Benefits:

 Decouples producers and consumers.


 Scalable and flexible communication.
 Supports many-to-many message distribution.

Example: Using Redis Pub/Sub in Python.

python
Copy code
import redis
# Publisher
r = redis.Redis()
r.publish('channel', 'message')
# Subscriberdef handler(message):
print(message)

p = r.pubsub()
p.subscribe(**{'channel': handler})
p.run_in_thread(sleep_time=1)

2.3. Service Registry

Description: A pattern where services register themselves and discover other services
via a central registry, facilitating dynamic service discovery and load balancing.

Benefits:

 Enables dynamic discovery of services.


 Supports load balancing and fault tolerance.
 Decouples service location from service usage.

Example: Using Eureka for service registry in a microservices architecture.

yaml
Copy code
# Eureka Server (application.yml)server:
port: 8761
DESIGN PATTERNS Semester 8

eureka:
client:
register-with-eureka: false
fetch-registry: false
# Eureka Client (application.yml)eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/

2.4. Circuit Breaker

Description: Protects against cascading failures in distributed systems by preventing


attempts to access a service that is likely to fail.

Benefits:

 Improves system resilience.


 Prevents cascading failures.
 Allows systems to recover gracefully.

Example: Using Hystrix for implementing a circuit breaker in Java.

java
Copy code
public class MyCommand extends HystrixCommand<String> {
protected String run() {
// Code that might fail
}

protected String getFallback() {


return "Fallback response";
}
}

Conclusion

Understanding and applying these patterns can significantly enhance the robustness,
maintainability, and scalability of concurrent and distributed systems. By leveraging
concurrency patterns, you can manage parallel execution efficiently, while distributed
systems patterns help handle the complexities of communication, coordination, and
fault tolerance across multiple machines.

If you need more detailed explanations or examples of specific patterns, feel free to
ask!
DESIGN PATTERNS Semester 8

 Event handling patterns

Event Handling Patterns

1. Observer Pattern
2. Event Bus Pattern
3. Reactor Pattern
4. Mediator Pattern
5. State Pattern
6. Command Pattern

1. Observer Pattern

Description: The Observer Pattern defines a one-to-many dependency between


objects so that when one object changes state, all its dependents are notified and
updated automatically.

Benefits:

 Decouples the subject and observers.


 Allows multiple observers to react to events.
 Promotes a flexible and dynamic system.

Example:

java
Copy code
// Subjectpublic class Subject {
private List<Observer> observers = new ArrayList<>();

public void addObserver(Observer observer) {


observers.add(observer);
}

public void notifyObservers() {


for (Observer observer : observers) {
observer.update();
}
}
}
DESIGN PATTERNS Semester 8

// Observerpublic interface Observer {


void update();
}
// Concrete Observerpublic class ConcreteObserver implements Observer
{
public void update() {
System.out.println("Observer notified.");
}
}
// Usagepublic static void main(String[] args) {
Subject subject = new Subject();
Observer observer = new ConcreteObserver();

subject.addObserver(observer);
subject.notifyObservers();
}

2. Event Bus Pattern

Description: The Event Bus Pattern uses a central bus to manage event publishing
and subscribing, allowing decoupled components to communicate with each other
through events.

Benefits:

 Decouples event producers and consumers.


 Simplifies event management.
 Enhances scalability and flexibility.

Example:

python
Copy code
# Using PyPubSub in Python
from pubsub import pub
# Subscriberdef listener(arg):
print(f'Listener received: {arg}')
# Registering listener
pub.subscribe(listener, 'topic')
# Publisher
pub.sendMessage('topic', arg='Hello World!')

3. Reactor Pattern
DESIGN PATTERNS Semester 8

Description: The Reactor Pattern handles service requests that are delivered
concurrently to an application by one or more clients. It demultiplexes incoming
requests and dispatches them to the appropriate request handlers.

Benefits:

 Efficiently manages I/O operations.


 Improves scalability and responsiveness.
 Suitable for networked applications.

Example: Using selectors module in Python for non-blocking I/O.

python
Copy code
import selectorsimport socket

sel = selectors.DefaultSelector()
def accept(sock, mask):
conn, addr = sock.accept()
print('Accepted', conn, 'from', addr)
conn.setblocking(False)
sel.register(conn, selectors.EVENT_READ, read)
def read(conn, mask):
data = conn.recv(1000)
if data:
print('Echoing', repr(data), 'to', conn)
conn.send(data)
else:
print('Closing', conn)
sel.unregister(conn)
conn.close()

sock = socket.socket()
sock.bind(('localhost', 1234))
sock.listen()
sock.setblocking(False)
sel.register(sock, selectors.EVENT_READ, accept)
while True:
events = sel.select()
for key, mask in events:
callback = key.data
callback(key.fileobj, mask)

4. Mediator Pattern
DESIGN PATTERNS Semester 8

Description: The Mediator Pattern defines an object that encapsulates how a set of
objects interact. This pattern promotes loose coupling by preventing objects from
referring to each other explicitly and allows their interaction to be varied
independently.

Benefits:

 Reduces the complexity of object communication.


 Centralizes control logic.
 Enhances maintainability.

Example:

java
Copy code
// Mediator Interfacepublic interface Mediator {
void notify(Component sender, String event);
}
// Concrete Mediatorpublic class ConcreteMediator implements Mediator
{
private ComponentA componentA;
private ComponentB componentB;

public void setComponentA(ComponentA componentA) {


this.componentA = componentA;
}

public void setComponentB(ComponentB componentB) {


this.componentB = componentB;
}

public void notify(Component sender, String event) {


if (event.equals("A")) {
componentB.react();
} else if (event.equals("B")) {
componentA.react();
}
}
}
// Component Interfacepublic interface Component {
void setMediator(Mediator mediator);
}
// Concrete Component Apublic class ComponentA implements Component {
private Mediator mediator;

public void setMediator(Mediator mediator) {


DESIGN PATTERNS Semester 8

this.mediator = mediator;
}

public void doA() {


mediator.notify(this, "A");
}

public void react() {


System.out.println("Component A reacting to event.");
}
}
// Concrete Component Bpublic class ComponentB implements Component {
private Mediator mediator;

public void setMediator(Mediator mediator) {


this.mediator = mediator;
}

public void doB() {


mediator.notify(this, "B");
}

public void react() {


System.out.println("Component B reacting to event.");
}
}
// Usagepublic static void main(String[] args) {
ConcreteMediator mediator = new ConcreteMediator();
ComponentA componentA = new ComponentA();
ComponentB componentB = new ComponentB();

mediator.setComponentA(componentA);
mediator.setComponentB(componentB);

componentA.setMediator(mediator);
componentB.setMediator(mediator);

componentA.doA();
componentB.doB();
}

5. State Pattern
DESIGN PATTERNS Semester 8

Description: The State Pattern allows an object to alter its behavior when its internal
state changes. The object will appear to change its class.

Benefits:

 Localizes state-specific behavior.


 Simplifies state transitions.
 Promotes cleaner and more modular code.

Example:

java
Copy code
// State Interfacepublic interface State {
void handle(Context context);
}
// Concrete Statespublic class StateA implements State {
public void handle(Context context) {
System.out.println("Handling state A");
context.setState(new StateB());
}
}
public class StateB implements State {
public void handle(Context context) {
System.out.println("Handling state B");
context.setState(new StateA());
}
}
// Contextpublic class Context {
private State state;

public Context(State state) {


this.state = state;
}

public void setState(State state) {


this.state = state;
}

public void request() {


state.handle(this);
}
}
// Usagepublic static void main(String[] args) {
Context context = new Context(new StateA());
context.request(); // Handling state A
DESIGN PATTERNS Semester 8

context.request(); // Handling state B


}

6. Command Pattern

Description: The Command Pattern encapsulates a request as an object, thereby


allowing for parameterization of clients with different requests, queuing of requests,
and logging the requests.

Benefits:

 Decouples the sender and receiver.


 Supports undoable operations.
 Simplifies command management.

Example:

java
Copy code
// Command Interfacepublic interface Command {
void execute();
}
// Concrete Commandpublic class LightOnCommand implements Command {
private Light light;

public LightOnCommand(Light light) {


this.light = light;
}

public void execute() {


light.on();
}
}
// Receiverpublic class Light {
public void on() {
System.out.println("Light is on");
}
}
// Invokerpublic class RemoteControl {
private Command command;

public void setCommand(Command command) {


this.command = command;
}
DESIGN PATTERNS Semester 8

public void pressButton() {


command.execute();
}
}
// Usagepublic static void main(String[] args) {
Light light = new Light();
Command lightOn = new LightOnCommand(light);
RemoteControl remote = new RemoteControl();

remote.setCommand(lightOn);
remote.pressButton(); // Light is on
}

Conclusion

Event handling patterns are essential for designing robust and maintainable systems
that respond to various events and changes. By leveraging these patterns, developers
can create systems that are more modular, flexible, and easier to maintain. Each
pattern addresses specific challenges and provides a structured way to handle events
in both concurrent and distributed environments.

 Synchronization and concurrency patterns

Synchronization and Concurrency Patterns

1. Mutex Pattern
2. Semaphore Pattern
3. Reader-Writer Lock Pattern
4. Barrier Pattern
5. Monitor Pattern
6. Thread-Specific Storage Pattern

1. Mutex Pattern

Description: The Mutex (Mutual Exclusion) Pattern ensures that only one thread can
access a resource or critical section at any given time.

Benefits:

 Prevents race conditions by serializing access to shared resources.


 Guarantees exclusive access, ensuring data integrity.
 Supports synchronization across multiple threads.
DESIGN PATTERNS Semester 8

Example: Using std::mutex in C++ for mutual exclusion.

cpp
Copy code
#include <iostream>#include <thread>#include <mutex>

std::mutex mtx;
void sharedResourceAccess() {
mtx.lock();
// Critical section
std::cout << "Accessing shared resource" << std::endl;
mtx.unlock();
}
int main() {
std::thread t1(sharedResourceAccess);
std::thread t2(sharedResourceAccess);

t1.join();
t2.join();

return 0;
}

2. Semaphore Pattern

Description: The Semaphore Pattern controls access to a shared resource through a


counter that determines the number of threads that can access the resource
concurrently.

Benefits:

 Limits concurrent access to a shared resource.


 Facilitates coordination among multiple threads.
 Supports both counting and binary semaphores.

Example: Using Semaphore in Java for controlling access.

java
Copy code
import java.util.concurrent.Semaphore;
Semaphore semaphore = new Semaphore(1); // Binary semaphore
// Thread 1
semaphore.acquire();// Critical section
semaphore.release();
// Thread 2
DESIGN PATTERNS Semester 8

semaphore.acquire();// Critical section


semaphore.release();

3. Reader-Writer Lock Pattern

Description: The Reader-Writer Lock Pattern allows multiple readers to access a


shared resource concurrently but requires exclusive access for writers.

Benefits:

 Improves concurrency by allowing multiple readers.


 Prevents writer starvation.
 Enhances performance for read-heavy applications.

Example: Using ReadWriteLock in Java for reader-writer synchronization.

java
Copy code
import java.util.concurrent.locks.ReadWriteLock;import
java.util.concurrent.locks.ReentrantReadWriteLock;
ReadWriteLock rwLock = new ReentrantReadWriteLock();
// Reader
rwLock.readLock().lock();// Read operation
rwLock.readLock().unlock();
// Writer
rwLock.writeLock().lock();// Write operation
rwLock.writeLock().unlock();

4. Barrier Pattern

Description: The Barrier Pattern synchronizes a group of threads by forcing them to


wait at a predefined point until all threads in the group have reached the barrier.

Benefits:

 Coordinates parallel execution phases.


 Ensures all threads synchronize at specific points.
 Facilitates collective operations in concurrent algorithms.

Example: Using CyclicBarrier in Java for synchronization.

java
Copy code
import java.util.concurrent.CyclicBarrier;
CyclicBarrier barrier = new CyclicBarrier(3); // Waits for 3 parties
// Thread 1
DESIGN PATTERNS Semester 8

barrier.await();// Continue after barrier is reached


// Thread 2
barrier.await();// Continue after barrier is reached
// Thread 3
barrier.await();// Continue after barrier is reached

5. Monitor Pattern

Description: The Monitor Pattern synchronizes access to methods or data within an


object by associating a lock or condition variable with the object.

Benefits:

 Provides a higher-level abstraction for synchronization.


 Ensures mutual exclusion and thread safety.
 Supports synchronized access to critical sections.

Example: Using synchronized keyword in Java for method synchronization.

java
Copy code
public class Counter {
private int count = 0;

public synchronized void increment() {


count++;
}

public synchronized int getCount() {


return count;
}
}

6. Thread-Specific Storage Pattern

Description: The Thread-Specific Storage (ThreadLocal) Pattern allows each thread


to have its own instance of a variable, isolated from other threads.

Benefits:

 Provides thread-local variables that are not shared among threads.


 Simplifies concurrent programming by avoiding synchronization.
 Enhances performance in multithreaded applications.

Example: Using ThreadLocal in Java for thread-local variables.


DESIGN PATTERNS Semester 8

java
Copy code
ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(() -> 0);
// Thread 1
threadLocal.set(42);int value1 = threadLocal.get();
// Thread 2int value2 = threadLocal.get();

Conclusion

These synchronization and concurrency patterns provide structured solutions to


manage shared resources, coordinate concurrent execution, and ensure thread safety in
multi-threaded applications. By understanding and applying these patterns, developers
can design robust and efficient systems that handle concurrency challenges effectively.

 Concurrency controller pattern

Concurrency Controller Pattern

Intent

The Concurrency Controller Pattern aims to:

 Manage Concurrent Access: Control access to shared resources to prevent conflicts and
ensure thread safety.
 Coordinate Activities: Synchronize concurrent operations to maintain consistency and order.
 Optimize Resource Utilization: Efficiently allocate and manage resources among multiple
threads or processes.

Key Components

Controller: Central component responsible for managing access to shared


resources and coordinating concurrent activities.

Locking Mechanisms: Techniques such as mutexes, semaphores, or reader-


writer locks used by the controller to enforce mutual exclusion and manage
access to critical sections.

Synchronization Policies: Rules and strategies defined by the controller to


ensure safe and efficient concurrency management, including deadlock
avoidance, fair scheduling, and resource allocation.

Benefits

 Thread Safety: Ensures that shared resources are accessed in a controlled and safe manner,
preventing data corruption or inconsistent state.
DESIGN PATTERNS Semester 8

 Improved Performance: Optimizes resource usage by minimizing contention and efficiently


coordinating concurrent operations.
 Simplicity and Maintainability: Provides a centralized approach to concurrency management,
making the codebase cleaner and easier to maintain.

Example Scenario

Consider a web server application that handles multiple client requests concurrently.
The Concurrency Controller Pattern can be applied to manage access to a shared
cache or database, ensuring that only one thread modifies the data at a time while
allowing multiple threads to read concurrently. Here’s a simplified example:

java
Copy code
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ConcurrentCache {
private ReentrantReadWriteLock lock = new
ReentrantReadWriteLock();
private int data;

public int readData() {


lock.readLock().lock();
try {
return data;
} finally {
lock.readLock().unlock();
}
}

public void writeData(int newData) {


lock.writeLock().lock();
try {
data = newData;
} finally {
lock.writeLock().unlock();
}
}
}

In this example:

 ReentrantReadWriteLock is used as the locking mechanism controlled by the


Concurrency Controller (ConcurrentCache).
 readData() allows multiple threads to read data concurrently using a read lock.
 writeData() ensures exclusive access to modify the data using a write lock, preventing
concurrent writes that could lead to inconsistent state.
DESIGN PATTERNS Semester 8

Considerations

Performance Overhead: Implementing strict locking mechanisms can


introduce overhead due to synchronization, impacting throughput.

Scalability: Careful design is needed to ensure that the concurrency control


scales effectively with the number of threads or processes accessing the shared
resources.

Conclusion

The Concurrency Controller Pattern provides a structured approach to manage and


coordinate concurrent access to shared resources, ensuring thread safety and efficient
resource utilization in multi-threaded applications. By applying this pattern,
developers can design robust and scalable systems capable of handling complex
concurrency challenges effectively.

Chapter# 4

 Behavioral Design Patterns


Behavioral Patterns are concerned with algorithms and the
assignment of responsibilities between objects. Behavioral patterns
describe not just patterns of objects or classes but also the patterns of
communication between them. These patterns characterize complex
control flow that’s difficult to follow at run-time.
There are three recurring themes in these patterns:
 Behavioral class patterns use inheritance to distribute behavior between
classes.
 Behavioral object patterns use object composition rather than inheritance.
 Behavioral object patterns are concerned with encapsulating behavior in an
object and delegating requests to it.

Types of Behavioral Design Patterns


DESIGN PATTERNS Semester 8

 Chain of Responsibility Design Pattern


The Chain of Responsibility design pattern is a behavioral design
pattern that allows an object to pass a request along a chain of handlers.
Each handler in the chain decides either to process the request or to pass it
along the chain to the next handler.

Key Concepts

 Handler: Defines an interface for handling requests. It typically implements the method
handleRequest.
 Concrete Handler: Handles requests it is responsible for. It can access its successor and
forward the request if it can't handle it.
 Client: Initiates the request to a chain of handlers.

Structure

1. Handler:

o Has a method for handling the request.


o Holds a reference to the next handler in the chain.

2. Concrete Handler:

o Implements the handler's request method.


o Either processes the request or forwards it to the next handler in the chain.

3. Client:

o Creates and configures the chain of handlers.


o Initiates the request by passing it to the first handler in the chain.

Example
DESIGN PATTERNS Semester 8

Let's consider a support ticket system where a request can be handled by different
support levels: Level1Support, Level2Support, and Level3Support.

python
Copy code
class SupportHandler:
def __init__(self, successor=None):
self._successor = successor

def handle(self, request):


raise NotImplementedError('Must provide implementation in
subclass')
class Level1Support(SupportHandler):
def handle(self, request):
if request == 'basic':
print('Level 1 support handling basic request')
elif self._successor:
self._successor.handle(request)
class Level2Support(SupportHandler):
def handle(self, request):
if request == 'intermediate':
print('Level 2 support handling intermediate request')
elif self._successor:
self._successor.handle(request)
class Level3Support(SupportHandler):
def handle(self, request):
if request == 'advanced':
print('Level 3 support handling advanced request')
elif self._successor:
self._successor.handle(request)
# Clientdef main():
# Create the chain of handlers
level3 = Level3Support()
level2 = Level2Support(level3)
level1 = Level1Support(level2)

# Handling requests
level1.handle('basic')
level1.handle('intermediate')
level1.handle('advanced')
level1.handle('unknown')
if __name__ == "__main__":
main()

Output
DESIGN PATTERNS Semester 8

mathematica
Copy code
Level 1 support handling basic requestLevel 2 support handling
intermediate requestLevel 3 support handling advanced request

Use Cases

 Logging Systems: Different log levels (info, debug, error) can be handled by different
handlers.
 Event Handling: GUI frameworks where different components handle different types of
events.
 Approval Workflows: Different levels of authorization can handle various types of requests.

Advantages

 Decouples Sender and Receiver: The sender of a request doesn't need to know which object
will handle the request.
 Flexibility in Assigning Responsibilities: You can change the chain dynamically by adding or
removing handlers.
 Enhances Scalability: Adding new handlers is straightforward and doesn't affect existing
handlers.

Disadvantages

 Request Might Go Unhandled: If no handler in the chain handles the request, it might get
lost or remain unprocessed.
 Debugging Complexity: The flow of requests can become difficult to track, especially in long
chains.

The Chain of Responsibility pattern is a powerful way to handle requests dynamically


and flexibly, allowing for scalable and maintainable code design. By using this pattern,
you can create systems where the responsibility for handling requests can be easily
extended and modified without changing the core logic.

 Visitor design pattern


Visitor design pattern is one of the behavioral design patterns. It is used
when we have to perform an operation on a group of similar kind of
Objects. With the help of visitor pattern, we can move the operational logic
from the objects to another class. The visitor pattern consists of two parts:
 a method called Visit() which is implemented by the visitor and is
called for every element in the data structure
 visitable classes providing Accept() methods that accept a visitor
DESIGN PATTERNS Semester 8

UML Diagram Visitor design pattern

Key Concepts

 Visitor: Defines an interface for visiting each type of element.


 Concrete Visitor: Implements the visitor interface and defines the operations to be
performed on the elements.
 Element: Defines an interface for accepting a visitor.
 Concrete Element: Implements the element interface and accepts a visitor.

Structure

1. Visitor Interface: Declares a visit operation for each type of element in the object structure.
2. Concrete Visitor: Implements the visit operations, providing the specific logic for each type
of element.
3. Element Interface: Declares an accept method that takes a visitor as an argument.
4. Concrete Element: Implements the accept method, calling the appropriate visit method on
the visitor.

Example

Consider a simple example of a shopping cart where different types of items need to
be processed for generating invoices and calculating total prices.

python
Copy code
DESIGN PATTERNS Semester 8

from abc import ABC, abstractmethod


# Visitor Interfaceclass ShoppingCartVisitor(ABC): @abstractmethod
def visit_book(self, book):
pass
@abstractmethod
def visit_fruit(self, fruit):
pass
# Concrete Visitorclass ShoppingCartVisitorImpl(ShoppingCartVisitor):
def visit_book(self, book):
cost = book.price
if book.price > 50:
cost *= 0.9 # Apply 10% discount
print(f"Book ISBN:{book.isbn} cost = {cost}")
return cost

def visit_fruit(self, fruit):


cost = fruit.price_per_kg * fruit.weight
print(f"{fruit.name} cost = {cost}")
return cost
# Element Interfaceclass ItemElement(ABC): @abstractmethod
def accept(self, visitor):
pass
# Concrete Element: Bookclass Book(ItemElement):
def __init__(self, price, isbn):
self.price = price
self.isbn = isbn

def accept(self, visitor):


return visitor.visit_book(self)
# Concrete Element: Fruitclass Fruit(ItemElement):
def __init__(self, price_per_kg, weight, name):
self.price_per_kg = price_per_kg
self.weight = weight
self.name = name

def accept(self, visitor):


return visitor.visit_fruit(self)
# Client Codedef calculate_price(items):
visitor = ShoppingCartVisitorImpl()
total = 0
for item in items:
total += item.accept(visitor)
return total
if __name__ == "__main__":
DESIGN PATTERNS Semester 8

items = [
Book(60, "1234"),
Book(30, "5678"),
Fruit(2, 3, "Apple"),
Fruit(1.5, 2, "Banana")
]

total = calculate_price(items)
print(f"Total cost = {total}")

Output

java
Copy code
Book ISBN:1234 cost = 54.0
Book ISBN:5678 cost = 30Apple cost = 6Banana cost = 3.0Total cost =
93.0

Use Cases

 Compilers: Performing operations on abstract syntax trees (ASTs) such as type checking,
optimization, and code generation.
 Object Structures: Performing operations like printing, validating, or exporting in different
formats without changing the objects themselves.
 Graphics Systems: Applying operations to various shapes or graphical components.

Advantages

 Single Responsibility Principle: Separates operations from the objects they operate on.
 Open/Closed Principle: Allows adding new operations without modifying the existing object
structure.
 Flexibility: New visitors can be created to perform different operations without changing the
element classes.

Disadvantages

 Complexity: Adding new element classes requires changes to the visitor interface and all
concrete visitors.
 Encapsulation: Elements need to expose their internal state to the visitor, which can violate
encapsulation.

The Visitor pattern is a powerful way to separate concerns and add new operations to
existing object structures without modifying them. It provides flexibility and
maintainability, especially in systems that frequently require new operations.

 Observer Design Pattern


DESIGN PATTERNS Semester 8

The Observer Design Pattern is a behavioral design pattern that defines a


one-to-many dependency between objects so that when one object (the
subject) changes state, all its dependents (observers) are notified and
updated automatically.

Components of Observer Design Pattern

Key Concepts

 Subject: Maintains a list of observers and provides methods to attach and detach observers.
 Observer: Defines an interface for objects that should be notified of changes in the subject.
 Concrete Subject: Implements the subject interface and maintains state of interest to
observers.
 Concrete Observer: Implements the observer interface to keep its state consistent with the
subject's.

Structure

1. Subject Interface:

o Methods to attach, detach, and notify observers.

2. Concrete Subject:

o Stores state and notifies observers when its state changes.

3. Observer Interface:

o Method to update the observer with subject changes.

4. Concrete Observer:

o Implements the update method to synchronize its state with the subject's state.

Example
DESIGN PATTERNS Semester 8

Consider a simple example of a weather station where different displays (observers)


need to be updated with the latest weather data from the station (subject).

python
Copy code
class Subject:
def __init__(self):
self._observers = []

def attach(self, observer):


self._observers.append(observer)

def detach(self, observer):


self._observers.remove(observer)

def notify(self):
for observer in self._observers:
observer.update(self)
class Observer:
def update(self, subject):
pass
class WeatherStation(Subject):
def __init__(self):
super().__init__()
self._temperature = 0
@property
def temperature(self):
return self._temperature
@temperature.setter
def temperature(self, value):
self._temperature = value
self.notify()
class TemperatureDisplay(Observer):
def update(self, subject):
if isinstance(subject, WeatherStation):
print(f"Temperature Display: The current temperature is
{subject.temperature}°C")
class FanControl(Observer):
def update(self, subject):
if isinstance(subject, WeatherStation):
if subject.temperature > 25:
print("Fan Control: It's hot! Turning the fan ON.")
else:
print("Fan Control: It's cool! Turning the fan OFF.")
# Client Codeif __name__ == "__main__":
DESIGN PATTERNS Semester 8

weather_station = WeatherStation()

temp_display = TemperatureDisplay()
fan_control = FanControl()

weather_station.attach(temp_display)
weather_station.attach(fan_control)

weather_station.temperature = 22
weather_station.temperature = 30

Output

mathematica
Copy code
Temperature Display: The current temperature is 22°CFan Control:
It's cool! Turning the fan OFF.Temperature Display: The current
temperature is 30°CFan Control: It's hot! Turning the fan ON.

Use Cases

 GUI Systems: To update multiple views of a model in response to changes in the model.
 Event Management Systems: For event handling and notifications.
 Distributed Systems: To update subscribers about changes in the state of an object.

Advantages

 Loose Coupling: The subject and observers are loosely coupled; they interact only through
the observer interface.
 Dynamic Relationships: Observers can be added and removed at runtime.
 Flexibility: Multiple observers can respond to changes in the subject's state in different ways.

Disadvantages

 Memory Leaks: If observers are not correctly removed, it can lead to memory leaks.
 Notification Overhead: Sending notifications to all observers can become a performance
issue if there are many observers.

The Observer pattern is a powerful way to implement event-driven systems and create
a dynamic relationship between objects. It enhances flexibility and maintainability by
promoting loose coupling and enabling multiple observers to react to state changes in
a subject.

 Integrator Design Pattern

The Integrator pattern isn't a standard design pattern found in traditional design
pattern catalogs like the Gang of Four. However, integrating different components,
services, or systems is a common task in software development, often requiring its
DESIGN PATTERNS Semester 8

own patterns and best practices. Below is a conceptual framework that you might
refer to as the "Integrator Pattern," aimed at managing the integration of various
subsystems or services in a cohesive and flexible manner.

Key Concepts

 Subsystems/Services: These are the individual components or services that need to be


integrated. Each subsystem performs a specific set of functions independently.
 Integrator: This component is responsible for managing interactions between the
subsystems, ensuring they work together harmoniously.
 Interfaces: Define standard methods for communication between the integrator and
subsystems, ensuring loose coupling and interoperability.

Structure

1. Subsystem Interface: Defines the contract for communication with subsystems.


2. Concrete Subsystem: Implements the subsystem interface and provides specific functionality.
3. Integrator Interface: Defines methods for managing and coordinating subsystems.
4. Concrete Integrator: Implements the integrator interface and contains logic for coordinating
subsystems.

Example

Consider a scenario where an e-commerce platform needs to integrate with various


services like payment processing, inventory management, and shipping.

python
Copy code
# Subsystem Interfaceclass PaymentService:
def process_payment(self, amount):
raise NotImplementedError
class InventoryService:
def check_stock(self, product_id):
raise NotImplementedError
class ShippingService:
def arrange_shipping(self, order_id):
raise NotImplementedError
# Concrete Subsystemsclass PayPalService(PaymentService):
def process_payment(self, amount):
print(f"Processing payment of ${amount} through PayPal.")
class InventoryManagementService(InventoryService):
def check_stock(self, product_id):
print(f"Checking stock for product {product_id}.")
return True # Assume the product is always in stock for
simplicity
class FedExShippingService(ShippingService):
def arrange_shipping(self, order_id):
DESIGN PATTERNS Semester 8

print(f"Arranging shipping for order {order_id} with FedEx.")


# Integrator Interfaceclass ECommerceIntegrator:
def __init__(self):
self.payment_service = None
self.inventory_service = None
self.shipping_service = None

def set_payment_service(self, service):


self.payment_service = service

def set_inventory_service(self, service):


self.inventory_service = service

def set_shipping_service(self, service):


self.shipping_service = service

def process_order(self, order_id, product_id, amount):


if self.inventory_service.check_stock(product_id):
self.payment_service.process_payment(amount)
self.shipping_service.arrange_shipping(order_id)
else:
print(f"Product {product_id} is out of stock.")
# Client Codeif __name__ == "__main__":
integrator = ECommerceIntegrator()

# Setting up services
integrator.set_payment_service(PayPalService())
integrator.set_inventory_service(InventoryManagementService())
integrator.set_shipping_service(FedExShippingService())

# Processing an order
integrator.process_order(order_id=123, product_id=456,
amount=99.99)

Output

javascript
Copy code
Checking stock for product 456.Processing payment of $99.99 through
PayPal.Arranging shipping for order 123 with FedEx.

Use Cases

 Enterprise Application Integration (EAI): Integrating various enterprise systems like ERP,
CRM, and HR systems.
DESIGN PATTERNS Semester 8

 Microservices Architecture: Managing interactions between microservices in a cohesive


manner.
 API Gateways: Acting as an entry point and integrator for various backend services in a
distributed system.

Advantages

 Loose Coupling: Subsystems can be developed, tested, and deployed independently.


 Scalability: New subsystems can be integrated easily by implementing the appropriate
interface and updating the integrator.
 Flexibility: Allows for easy swapping of subsystems without affecting the overall system
functionality.

Disadvantages

 Complexity: The integrator can become a complex central point that needs to manage many
interactions and dependencies.
 Performance Overhead: Additional layers of integration can introduce latency and
performance bottlenecks.

The Integrator pattern is a conceptual approach to managing the integration of


multiple subsystems or services. By defining clear interfaces and responsibilities, it
promotes loose coupling, flexibility, and scalability, making it easier to develop and
maintain complex systems.

 Command Design Pattern

The Command pattern is a behavioral design pattern that encapsulates a request as an


object, thereby allowing for parameterization of clients with queues, requests, and
operations. It allows for decoupling the sender of a request from its receiver by giving
different objects the ability to process the request.

Key Concepts

 Command: Declares an interface for executing an operation.


 Concrete Command: Implements the command interface, typically linking it with the
receiver.
 Receiver: Knows how to perform the operations associated with carrying out a request.
 Invoker: Asks the command to carry out the request.
 Client: Creates the command and sets its receiver.

Structure

1. Command Interface: Declares the execute method.


2. Concrete Command: Implements the execute method, invoking the corresponding
operations on the receiver.
3. Receiver: Performs the actual work needed for the request.
4. Invoker: Stores the command and invokes the execute method.
5. Client: Configures the command and its receiver.

Example
DESIGN PATTERNS Semester 8

Consider a home automation system where you can control various appliances like
lights and fans using commands.

python
Copy code
# Command Interfaceclass Command:
def execute(self):
raise NotImplementedError
# Concrete Commandsclass LightOnCommand(Command):
def __init__(self, light):
self._light = light

def execute(self):
self._light.turn_on()
class LightOffCommand(Command):
def __init__(self, light):
self._light = light

def execute(self):
self._light.turn_off()
class FanOnCommand(Command):
def __init__(self, fan):
self._fan = fan

def execute(self):
self._fan.turn_on()
class FanOffCommand(Command):
def __init__(self, fan):
self._fan = fan

def execute(self):
self._fan.turn_off()
# Receiverclass Light:
def turn_on(self):
print("Light is ON")

def turn_off(self):
print("Light is OFF")
class Fan:
def turn_on(self):
print("Fan is ON")

def turn_off(self):
print("Fan is OFF")
# Invokerclass RemoteControl:
DESIGN PATTERNS Semester 8

def __init__(self):
self._commands = {}

def set_command(self, button, command):


self._commands[button] = command

def press_button(self, button):


if button in self._commands:
self._commands[button].execute()
else:
print("No command assigned to this button")
# Client Codeif __name__ == "__main__":
light = Light()
fan = Fan()

light_on = LightOnCommand(light)
light_off = LightOffCommand(light)
fan_on = FanOnCommand(fan)
fan_off = FanOffCommand(fan)

remote = RemoteControl()
remote.set_command("light_on", light_on)
remote.set_command("light_off", light_off)
remote.set_command("fan_on", fan_on)
remote.set_command("fan_off", fan_off)

# Using the remote control


remote.press_button("light_on")
remote.press_button("fan_on")
remote.press_button("light_off")
remote.press_button("fan_off")

Output

vbnet
Copy code
Light is ON
Fan is ON
Light is OFF
Fan is OFF

Use Cases

 GUI Buttons and Menu Items: Each button or menu item can be associated with a command
object that executes the appropriate action.
DESIGN PATTERNS Semester 8

 Transactional Behavior: Commands can be stored in a queue and executed sequentially,


allowing for undo/redo functionality.
 Macro Recording: Storing a sequence of commands to be executed later, possibly multiple
times.

Advantages

 Decoupling: Decouples the object that invokes the operation from the one that knows how
to perform it.
 Flexibility: Allows for easy addition of new commands without changing existing code.
 Undo/Redo: Commands can be stored and executed later, providing undo and redo
capabilities.

Disadvantages

 Complexity: The pattern can introduce additional classes and complexity to the system.
 Overhead: For simple operations, using the command pattern might be overkill and add
unnecessary overhead.

The Command pattern is a versatile and powerful design pattern that promotes loose
coupling and flexibility. It is particularly useful in scenarios where operations need to
be parameterized, queued, or undone, making it an essential pattern for creating
robust and maintainable software systems.

 Mediator design pattern


The Mediator design pattern is a behavioral pattern that defines an object,
the mediator, to centralize communication between various components
or objects in a system. This promotes loose coupling by preventing direct
interactions between components, instead of having them communicate
through the mediator, facilitating better maintainability and flexibility in
the system architecture.

Key Concepts

 Mediator: Defines an interface for communication between colleague objects.


 Concrete Mediator: Implements the mediator interface and coordinates communication
between colleague objects.
DESIGN PATTERNS Semester 8

 Colleague: Represents objects that communicate with each other through the mediator.

Structure

1. Mediator Interface: Declares methods for communication between colleagues.


2. Concrete Mediator: Implements the mediator interface, coordinating the interactions
between colleagues.
3. Colleague Interface: Declares a method to set the mediator.
4. Concrete Colleague: Implements the colleague interface and communicates with other
colleagues through the mediator.

Example

Consider a chat room application where users (colleagues) communicate with each
other through a central chat room (mediator).

python
Copy code
# Mediator Interfaceclass ChatRoomMediator:
def show_message(self, user, message):
raise NotImplementedError
# Concrete Mediatorclass ChatRoom(ChatRoomMediator):
def show_message(self, user, message):
print(f"[{user.name}]: {message}")
# Colleague Interfaceclass User:
def __init__(self, name, mediator):
self.name = name
self.mediator = mediator

def send_message(self, message):


self.mediator.show_message(self, message)
# Client Codeif __name__ == "__main__":
chat_room = ChatRoom()

user1 = User("Alice", chat_room)


user2 = User("Bob", chat_room)

user1.send_message("Hi Bob!")
user2.send_message("Hello Alice!")

Output

markdown
Copy code
[Alice]: Hi Bob!
[Bob]: Hello Alice!
DESIGN PATTERNS Semester 8

Use Cases

 GUI Applications: Managing complex interactions between GUI components (e.g., dialogs,
buttons, and text fields).
 Communication Systems: Coordinating message passing between multiple objects or
components.
 Air Traffic Control Systems: Mediating communication between airplanes to prevent direct
communication and potential conflicts.

Advantages

 Loose Coupling: Reduces direct dependencies between colleague objects, making the system
easier to maintain and extend.
 Centralized Control: Centralizes the logic for interaction between colleagues in one place,
making it easier to understand and modify.
 Simplified Object Communication: Colleague objects do not need to reference each other
directly, reducing the complexity of object interactions.

Disadvantages

 Complexity: The mediator can become a complex and large component if it handles many
interactions, potentially becoming a bottleneck.
 Single Point of Failure: The mediator can become a single point of failure, and any issues
with the mediator can impact the entire system.

The Mediator pattern is a powerful tool for managing interactions between objects in
a system, promoting loose coupling and enhancing maintainability. By centralizing
communication logic, it simplifies object relationships and reduces the complexity of
the system.

 Strategy Design Pattern


The Strategy Design Pattern defines a family of algorithms, encapsulates
each one, and makes them interchangeable, allowing clients to switch
algorithms dynamically without altering the code structure.

Components of the Strategy Design Pattern


DESIGN PATTERNS Semester 8

Key Concepts

 Strategy: Defines an interface common to all supported algorithms. The context uses this
interface to call the algorithm defined by a concrete strategy.
 Concrete Strategy: Implements the algorithm using the strategy interface.
 Context: Configured with a concrete strategy object and maintains a reference to a strategy
object. It can be provided a strategy object at runtime and uses it to perform the algorithm.

Structure

1. Strategy Interface: Declares methods common to all concrete strategies.


2. Concrete Strategy: Implements the strategy interface with a specific algorithm.
3. Context: Maintains a reference to a strategy object and interacts with the strategy interface
to execute the algorithm.

Example

Consider a payment processing system where different payment methods (strategies)


like credit card, PayPal, and Bitcoin are used.

python
Copy code
from abc import ABC, abstractmethod
# Strategy Interfaceclass PaymentStrategy(ABC): @abstractmethod
def pay(self, amount):
pass
# Concrete Strategiesclass CreditCardPayment(PaymentStrategy):
def __init__(self, card_number):
self.card_number = card_number

def pay(self, amount):


print(f"Paying {amount} using credit card {self.card_number}")
class PayPalPayment(PaymentStrategy):
DESIGN PATTERNS Semester 8

def __init__(self, email):


self.email = email

def pay(self, amount):


print(f"Paying {amount} using PayPal account {self.email}")
class BitcoinPayment(PaymentStrategy):
def __init__(self, wallet_address):
self.wallet_address = wallet_address

def pay(self, amount):


print(f"Paying {amount} using Bitcoin wallet
{self.wallet_address}")
# Contextclass ShoppingCart:
def __init__(self):
self.items = []
self.total = 0
self.payment_strategy = None

def add_item(self, item, price):


self.items.append((item, price))
self.total += price

def set_payment_strategy(self, strategy):


self.payment_strategy = strategy

def checkout(self):
if not self.payment_strategy:
raise Exception("Payment strategy not set!")
self.payment_strategy.pay(self.total)
# Client Codeif __name__ == "__main__":
cart = ShoppingCart()
cart.add_item("Book", 12.99)
cart.add_item("Pen", 1.99)

# Using Credit Card Payment


cart.set_payment_strategy(CreditCardPayment("1234-5678-9876-
5432"))
cart.checkout()

# Using PayPal Payment


cart.set_payment_strategy(PayPalPayment("[email protected]"))
cart.checkout()

# Using Bitcoin Payment


DESIGN PATTERNS Semester 8

cart.set_payment_strategy(BitcoinPayment("1A2B3C4D5E6F"))
cart.checkout()

Output

sql
Copy code
Paying 14.98 using credit card 1234-5678-9876-5432
Paying 14.98 using PayPal account [email protected]
Paying 14.98 using Bitcoin wallet 1A2B3C4D5E6F

Use Cases

 Sorting Algorithms: Allowing different sorting strategies (quick sort, merge sort, bubble sort)
to be applied dynamically.
 Payment Processing Systems: Enabling multiple payment methods to be chosen at runtime.
 Compression Algorithms: Switching between different compression strategies (ZIP, RAR, TAR)
based on the context.

Advantages

 Open/Closed Principle: New strategies can be added without changing the context or
existing strategies.
 Flexibility: Allows the algorithm to be selected at runtime, making the system more flexible
and adaptable to changes.
 Reusability: Promotes reusability of the algorithms, as they are encapsulated in their own
classes.

Disadvantages

 Increased Complexity: Adding multiple strategies can increase the number of classes and
complexity of the system.
 Context Dependency: The context must be aware of all the strategies to use them, which can
lead to tighter coupling.

The Strategy pattern is a powerful way to manage algorithms dynamically, providing


flexibility and promoting reusable code. It is particularly useful in systems where
multiple algorithms can be applied to achieve a goal, allowing the best algorithm to be
selected based on the context.

 Interpreter Design Pattern

The Interpreter pattern is a behavioral design pattern that specifies how to evaluate
sentences in a language. It defines a representation for grammar as well as a
mechanism to interpret sentences in that grammar. This pattern involves defining a
language grammar, representing sentences in the language, and interpreting these
sentences to perform some action.

Key Concepts
DESIGN PATTERNS Semester 8

 Abstract Expression: Declares an abstract interpret operation that is common to all


nodes in the abstract syntax tree (AST).
 Terminal Expression: Implements the interpret operation for terminal symbols in the
grammar.
 Non-terminal Expression: Implements the interpret operation for non-terminal symbols
in the grammar, typically by recursively interpreting other expressions.
 Context: Contains global information that is maintained throughout the interpretation
process.
 Client: Builds or creates an abstract syntax tree (AST) of the grammar and then invokes the
interpret operation on the tree.

Structure

1. Abstract Expression: Defines an interface for interpreting expressions.


2. Terminal Expression: Implements the interface for terminal symbols in the grammar.
3. Non-terminal Expression: Implements the interface for non-terminal symbols in the
grammar.
4. Context: Contains information that is global to the interpreter and is used by the terminal
and non-terminal expressions during interpretation.
5. Client: Builds the abstract syntax tree (AST) representing a particular sentence in the
language and invokes the interpret operation to perform the interpretation.

Example

Consider an example where we interpret and evaluate arithmetic expressions using the
Interpreter pattern.

python
Copy code
from abc import ABC, abstractmethod
# Contextclass Context:
def __init__(self):
self.variables = {}

def set_variable(self, variable, value):


self.variables[variable] = value

def get_variable(self, variable):


return self.variables.get(variable, 0)
# Abstract Expressionclass Expression(ABC): @abstractmethod
def interpret(self, context):
pass
# Terminal Expressionclass Number(Expression):
def __init__(self, number):
self.number = number

def interpret(self, context):


return self.number
DESIGN PATTERNS Semester 8

# Non-terminal Expressionclass Plus(Expression):


def __init__(self, left, right):
self.left = left
self.right = right

def interpret(self, context):


return self.left.interpret(context) +
self.right.interpret(context)
# Non-terminal Expressionclass Minus(Expression):
def __init__(self, left, right):
self.left = left
self.right = right

def interpret(self, context):


return self.left.interpret(context) -
self.right.interpret(context)
# Client Codeif __name__ == "__main__":
context = Context()
context.set_variable('x', 10)
context.set_variable('y', 5)

# x + y - (x - y)
expression = Minus(Plus(Number('x'), Number('y')),
Minus(Number('x'), Number('y')))

result = expression.interpret(context)
print(f"Result: {result}") # Output: Result: 10

Output

makefile
Copy code
Result: 10

Use Cases

 Query Languages: Interpreting queries in database query languages.


 Symbolic Mathematics: Evaluating expressions in symbolic mathematics systems.
 Scripting Languages: Interpreting scripts written in domain-specific languages (DSLs).

Advantages

 Flexibility: Easily extendable to add new expressions without changing the client code.
 Complex Grammar Handling: Provides a clear and efficient way to handle complex
grammatical rules.
DESIGN PATTERNS Semester 8

 Separation of Concerns: Separates grammar from the interpretation logic, making the
system easier to understand and maintain.

Disadvantages

 Performance Overhead: Can lead to performance issues when interpreting complex


grammatical rules due to the recursive nature of interpretation.
 Complexity: Handling large grammars or languages with intricate rules can lead to complex
interpreter implementations.

The Interpreter pattern is useful for interpreting and evaluating expressions in a


language, providing a structured way to parse and execute complex grammatical rules.
It promotes flexibility, separation of concerns, and ease of extending the language
grammar and interpretation logic.

 Memento Design Pattern

The Memento pattern is a behavioral design pattern that allows capturing and
restoring an object's internal state without violating encapsulation. It encapsulates the
object's state in a memento object, which can be stored externally, and later restores
the object to that state.

Key Concepts

 Originator: The object whose state needs to be saved and restored.


 Memento: A snapshot of the originator's state at a particular moment.
 CareTaker: Manages and keeps track of mementos.
 Client: Initiates requests to save and restore the originator's state using mementos.

Structure

1. Originator: Creates a memento containing a snapshot of its current state and uses
mementos to restore its state.
2. Memento: Stores the internal state of the originator object. It should be immutable to
ensure that its state cannot be altered once it's set.
3. CareTaker: Responsible for keeping the mementos safe and can only pass them back to the
originator.
4. Client: Initiates the save and restore operations.

Communication between the components


DESIGN PATTERNS Semester 8

Example

Consider an example where we implement a text editor that allows undo functionality
using the Memento pattern.

python
Copy code
# Mementoclass EditorMemento:
def __init__(self, content):
self._content = content

def get_saved_content(self):
return self._content
# Originatorclass TextEditor:
def __init__(self):
self._content = ""

def write(self, text):


self._content += text

def save_to_memento(self):
return EditorMemento(self._content)

def restore_from_memento(self, memento):


self._content = memento.get_saved_content()

def print_content(self):
print(f"Current Content: {self._content}")
# CareTakerclass History:
def __init__(self):
self._mementos = []

def add_memento(self, memento):


DESIGN PATTERNS Semester 8

self._mementos.append(memento)

def get_memento(self, index):


return self._mementos[index]
# Client Codeif __name__ == "__main__":
editor = TextEditor()
history = History()

editor.write("Hello, ")
history.add_memento(editor.save_to_memento())
editor.print_content()

editor.write("world!")
history.add_memento(editor.save_to_memento())
editor.print_content()

# Undo
editor.restore_from_memento(history.get_memento(0))
editor.print_content()

Output

sql
Copy code
Current Content: Hello, Current Content: Hello, world!Current Content:
Hello,

Use Cases

 Undo Mechanisms: Allowing users to undo changes in applications such as text editors,
graphics editors, and IDEs.
 Transactional Systems: Restoring objects to a previous state in transactions or workflows.
 Checkpoint Systems: Saving and restoring game states in video games.

Advantages

 Encapsulation: Protects the originator's state from being accessed by other objects.
 Simplicity: Provides a straightforward way to implement undo and restore functionality.
 Flexibility: Supports multiple undo levels by storing a history of mementos.

Disadvantages

 Overhead: Storing and managing mementos can consume memory, especially if the
originator's state is large.
 Complexity: Implementing mementos for complex objects or large states can be challenging.
DESIGN PATTERNS Semester 8

The Memento pattern is essential for implementing undo functionality and restoring
object states to a previous state without exposing the internal details of the object. It
promotes encapsulation, flexibility, and simplicity in managing object states
effectively.

You might also like