Design Patterns
Design Patterns
DESIGN PATTERNS
Chapter #1
Core Concepts
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
Inheritance
Polymorphism
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.
1. Avoid duplication of code by abstracting out things that are common and placing
them in a single location.
1. Design systems that are as simple as possible but not simpler. Avoid unnecessary
complexity.
Modularity
1. The source code for an object can be written and maintained independently of the
source code for other objects.
Reusability
Scalability
Design Process
Requirement Analysis
System Design
Object Design
Implementation
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.
Structural Diagrams:
1. Class Diagram: Shows the static structure of the system, including classes, attributes,
methods, and relationships between classes (inheritance, association, aggregation,
composition).
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.
3. State Machine Diagram: Describes the states of an object and the transitions
between those states.
DESIGN PATTERNS Semester 8
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.
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.
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
Reusability:
1. Design patterns provide a proven solution that can be reused in different parts of a
system or in different projects.
Communication:
Maintainability:
1. By using well-known patterns, code becomes easier to maintain and extend because
the design is familiar to other developers.
Efficiency:
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
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.
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:
Decorator Pattern:
Strategy Pattern:
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
Practical Tips
Low Coupling:
DESIGN PATTERNS Semester 8
High Cohesion:
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 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.
They all encapsulate knowledge about which concrete class the system uses.
They hide how instances of these classes are created and put together.
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.
Key Characteristics
Use Cases
DESIGN PATTERNS Semester 8
Static Member:
Private Constructor:
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
Diagram
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.
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.
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
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
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.
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
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
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.
Performance: Reduces the overhead of creating new instances, especially for complex
objects.
DESIGN PATTERNS Semester 8
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.
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
Use Cases
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.
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.
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
Use Cases
Diagram
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.
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.
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.
Imagine a graphic editor where shapes can be simple (like circles and rectangles) or
complex (combinations of shapes).
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
Key Characteristics
Use Cases
Graphic Systems: Separating the shape (abstraction) from how it is drawn (implementation).
DESIGN PATTERNS Semester 8
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.
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
Proxy Pattern
Definition
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
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.
Additional Behavior: Adds functionality such as logging, security checks, and caching.
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.
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
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
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.
Overhead: May add some overhead due to the extra level of wrapping.
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
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
Diagram
+-------------+
| 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.
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.
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.
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
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
Spaghetti Code
Golden Hammer
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
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.
concerns to be separated from the main business logic, promoting cleaner and more
maintainable code.
Key Concepts
Summary
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.
Example:
java
Copy code
public class OrderManager {
public void createOrder() {
// Create order
}
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)
// ...
}
Solution: Regularly review and refactor the codebase to remove unused or obsolete
code.
4. Golden Hammer
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.
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
// ...
}
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
}
java
Copy code
public static final double ITEM_PRICE = 19.99;public void
calculateTotal(int quantity) {
double total = quantity * ITEM_PRICE;
}
7. Poltergeists
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;
java
Copy code
public class OrderProcessor {
public void processOrder(Order order) {
// Process order directly
}
DESIGN PATTERNS Semester 8
The difference between "Recovering from bad designs" and "Refactoring to patterns"
lies primarily in their objectives and approaches within software development:
1.
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.
Key Concepts
1. Aspects
2. Themes
3. Concerns
1. Aspects
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.
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.
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.
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.
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
Chapter# 5
Patterns for Concurrent and Distributed Systems
1. Concurrency Patterns
2. Distributed Systems Patterns
1. Concurrency Patterns
Benefits:
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:
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:
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.1. Client-Server
Benefits:
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
Benefits:
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)
Description: A pattern where services register themselves and discover other services
via a central registry, facilitating dynamic service discovery and load balancing.
Benefits:
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/
Benefits:
java
Copy code
public class MyCommand extends HystrixCommand<String> {
protected String run() {
// Code that might fail
}
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
1. Observer Pattern
2. Event Bus Pattern
3. Reactor Pattern
4. Mediator Pattern
5. State Pattern
6. Command Pattern
1. Observer Pattern
Benefits:
Example:
java
Copy code
// Subjectpublic class Subject {
private List<Observer> observers = new ArrayList<>();
subject.addObserver(observer);
subject.notifyObservers();
}
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:
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:
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:
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;
this.mediator = mediator;
}
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:
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;
6. Command Pattern
Benefits:
Example:
java
Copy code
// Command Interfacepublic interface Command {
void execute();
}
// Concrete Commandpublic class LightOnCommand implements Command {
private Light light;
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.
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:
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
Benefits:
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
Benefits:
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
Benefits:
java
Copy code
import java.util.concurrent.CyclicBarrier;
CyclicBarrier barrier = new CyclicBarrier(3); // Waits for 3 parties
// Thread 1
DESIGN PATTERNS Semester 8
5. Monitor Pattern
Benefits:
java
Copy code
public class Counter {
private int count = 0;
Benefits:
java
Copy code
ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(() -> 0);
// Thread 1
threadLocal.set(42);int value1 = threadLocal.get();
// Thread 2int value2 = threadLocal.get();
Conclusion
Intent
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
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
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;
In this example:
Considerations
Conclusion
Chapter# 4
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:
2. Concrete Handler:
3. Client:
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
# 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.
Key Concepts
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
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.
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:
2. Concrete Subject:
3. Observer Interface:
4. Concrete Observer:
o Implements the update method to synchronize its state with the subject's state.
Example
DESIGN PATTERNS Semester 8
python
Copy code
class Subject:
def __init__(self):
self._observers = []
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.
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
Structure
Example
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
# 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
Advantages
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.
Key Concepts
Structure
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 = {}
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)
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
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.
Key Concepts
Colleague: Represents objects that communicate with each other through the mediator.
Structure
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
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.
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
Example
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 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)
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 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
Structure
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 = {}
# 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
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
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
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.
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 save_to_memento(self):
return EditorMemento(self._content)
def print_content(self):
print(f"Current Content: {self._content}")
# CareTakerclass History:
def __init__(self):
self._mementos = []
self._mementos.append(memento)
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.