Design Patterns With Java
Design Patterns With Java
Design Patterns With Java
WITH Java
Design patterns are essential tools in software engineering, providing proven solutions to
common design problems. They represent best practices refined over time, enabling developers
to create more maintainable, scalable, and efficient software. Understanding and applying design
patterns can significantly enhance the quality and robustness of your code.
Design patterns are typical solutions to common problems in software design. They are like
blueprints that you can customize to solve a particular design problem in your code. Patterns are
not specific pieces of code but rather general reusable solutions that can be applied in various
contexts to address recurring issues.
1. Reusability: Design patterns promote code reuse by providing templates for solving
common problems. This reduces the need to reinvent the wheel and allows developers to
leverage existing solutions.
2. Maintainability: By providing a clear structure and common vocabulary, design patterns
make it easier to maintain and understand code. This is especially beneficial in large
projects or teams.
3. Scalability: Patterns help design flexible and scalable software architectures that can
adapt to changing requirements and growth over time.
4. Efficiency: Leveraging well-known patterns can accelerate the development process, as
developers can rely on established solutions rather than creating new ones from scratch.
Categories of Design Patterns
Design patterns are generally categorized into three main types: creational, structural, and
behavioral.
1. Creational Patterns
o Purpose: Deal with object creation mechanisms, trying to create objects in a
manner suitable for the situation.
o Examples: Singleton, Factory Method, Abstract Factory, Builder, Prototype.
2. Structural Patterns
o Purpose: Concerned with how classes and objects are composed to form larger
structures.
o Examples: Adapter, Bridge, Composite, Decorator, Facade, Flyweight, Proxy.
3. Behavioral Patterns
o Purpose: Focus on communication between objects and the delegation of
responsibilities.
o Examples: Chain of Responsibility, Command, Interpreter, Iterator, Mediator,
Memento, Observer, State, Strategy, Template Method, Visitor.
These patterns deal with object creation mechanisms, trying to create objects in a manner
suitable to the situation.
• Singleton: Ensures that a class has only one instance and provides a global point of
access to it.
• Factory Method: Defines an interface for creating an object, but lets subclasses alter the
type of objects that will be created.
• Abstract Factory: Provides an interface for creating families of related or dependent
objects without specifying their concrete classes.
• Builder: Separates the construction of a complex object from its representation, allowing
the same construction process to create various representations.
• Prototype: Specifies the kind of objects to create using a prototypical instance and
creates new objects by copying this prototype.
These patterns deal with object composition or the structure of classes and objects to form larger
structures.
These patterns deal with object collaboration and responsibility, focusing on communication
between objects.
• Chain of Responsibility: Passes a request along a chain of handlers. Each handler can
either process the request or pass it to the next handler in the chain.
• Command: Encapsulates a request as an object, thereby allowing for parameterization of
clients with queues, requests, and operations.
• Interpreter: Defines a grammatical representation for a language and provides an
interpreter to interpret sentences in that language.
• Iterator: Provides a way to access the elements of an aggregate object sequentially
without exposing its underlying representation.
• Mediator: Defines an object that encapsulates how a set of objects interact, promoting
loose coupling by keeping objects from referring to each other explicitly.
• Memento: Captures and externalizes an object’s internal state, so the object can be
restored to this state later without violating encapsulation.
• Observer: Defines a one-to-many dependency between objects so that when one object
changes state, all its dependents are notified and updated automatically.
• State: Allows an object to alter its behavior when its internal state changes. The object
will appear to change its class.
• Strategy: Defines a family of algorithms, encapsulates each one, and makes them
interchangeable. The strategy allows the algorithm to vary independently from clients
that use it.
• Template Method: Defines the skeleton of an algorithm in a method, deferring some
steps to subclasses without changing the algorithm’s structure.
• Visitor: Represents an operation to be performed on elements of an object structure. It
allows you to define a new operation without changing the classes of the elements on
which it operates.
Applying Design Patterns
Applying design patterns involves understanding the problem at hand and then selecting the
appropriate pattern that addresses the specific challenge. It's essential to have a solid grasp of the
various patterns and their use cases to make informed decisions.
1. Identify the Problem: Clearly define the problem you are facing in your software
design. Understand the requirements and constraints.
2. Choose the Appropriate Pattern: Select a design pattern that best fits the problem.
Consider the pattern's intent, structure, and applicability.
3. Implement the Pattern: Apply the pattern to your codebase, customizing it as needed to
fit your specific context.
4. Refactor and Optimize: Once the pattern is implemented, refactor your code to ensure it
is clean, efficient, and maintainable. Test the implementation to verify it solves the
problem effectively.
Creational design patterns are a category of design patterns in software development that deal with
object creation mechanisms. They abstract the instantiation process, helping to make a system
independent of how its objects are created, composed, and represented. The goal is to control object
creation in a way that is suitable for the situation, often to ensure the right type of object is created or to
manage the complexity of object creation.
Benefits of Creational Patterns
• Flexibility: They provide flexibility in deciding which objects need to be created for a
given instance.
• Encapsulation: They encapsulate the instantiation logic, which can hide complex
creation logic from the client.
• Reuse: Patterns like Singleton and Prototype promote reuse of instances, which can be
beneficial for resource management.
Creational patterns are fundamental to object-oriented design, helping to manage the complexity
and scalability of creating objects in software systems.
Singleton Pattern
The Singleton pattern is one of the simplest and most commonly used design patterns in software
development. It ensures that a class has only one instance and provides a global point of access
to that instance. This is particularly useful when exactly one object is needed to coordinate
actions across the system.
• When you need exactly one instance of a class to coordinate actions across the system.
• When you need to control access to shared resources, such as a configuration object, log
file, or database connection.
• When a single instance of a class needs to be accessible from multiple places in your
application.
1. Private Constructor:
o The constructor of the Singleton class should be private to prevent direct
instantiation from outside the class. This ensures that the only way to create an
instance is through the class itself.
2. private Singleton() {
3. // Private constructor
4. }
5.
o
6. Static Instance Variable:
o A static variable should be used to hold the single instance of the class. This static
variable should be initialized to null (or using a lazy initialization approach) and
should only be assigned a value once.
7. private static Singleton instance;
o
8. Public Static Method for Access:
o Provide a public static method that returns the single instance of the class. This
method is responsible for creating the instance if it does not already exist and
returning it. This method is often named getInstance().
9. public static Logger getInstance() {
return instance;
}
10. Thread Safety:
o Ensure that the Singleton pattern is thread-safe, especially in multi-threaded
environments. This can be achieved through synchronization or other concurrency
mechanisms.
o Lazy Initialization with Synchronization:
o Double-Checked Locking:
o Bill Pugh Singleton Design (Initialization-on-demand holder idiom):
11. Safety:
o Ensure that the Singleton class handles serialization properly to maintain its single
instance property. This can be achieved by implementing readResolve() in the
class to prevent creation of a new instance during deserialization.
12. Avoid Reflection:
oPrevent the Singleton instance from being created through reflection, which can
break the Singleton property. This can be achieved by throwing an exception in
the constructor if an instance already exists.
13. Lazy Initialization (if needed):
o If the Singleton instance is not immediately needed, use lazy initialization to
delay the creation of the instance until it is actually required. This helps in saving
resources if the instance is never used.
14. Enum Singleton (Recommended Approach):
o For most cases, using an enum is the simplest and most robust way to implement
the Singleton pattern. It is inherently thread-safe, ensures a single instance, and
handles serialization automatically.
Implementation
The Singleton pattern can be implemented in various ways. Here are some common
implementations in Java:
1. Eager Initialization
private Singleton() {
// Private constructor to prevent instantiation
}
2. Lazy Initialization
private Singleton() {
// Private constructor to prevent instantiation
}
3. Double-Checked Locking
An optimization of the lazy initialization method that reduces the overhead of acquiring a lock
by first checking if the instance is already created.
private Singleton() {
// Private constructor to prevent instantiation
}
Uses a static inner helper class to create the Singleton instance. This approach leverages the class
loading mechanism to ensure that the instance is created only when it is first used.
public class Singleton {
private Singleton() {
// Private constructor to prevent instantiation
}
5. Enum Singleton
An implementation using an enum, which is the simplest and most robust way to implement the
Singleton pattern. It ensures thread safety and serialization handling.
ava's enum type provides a convenient way to implement singletons. The enum type automatically
handles the instantiation, thread-safety, and serialization of the singleton instance.
• Prevents Multiple Instantiation: The enum type inherently ensures that only one
instance is created.
• Thread-Safe: Enum singletons are thread-safe by default without requiring explicit
synchronization.
• Serialization Guaranteed: Enum instances are serialized correctly, preventing the
creation of a new instance during deserialization.
• Simple and Clear Syntax: Using enums for singletons is straightforward and concise.
Considerations
• Not Suitable for All Scenarios: Enum singletons are ideal for simple singletons but may
not be suitable for more complex scenarios where lazy initialization or dependency
injection is required.
Factory Design Patterns are creational design patterns that provide a way to create objects while
hiding the creation logic from the client. They are used to instantiate objects without exposing
the creation logic to the client and refer to the newly created object through a common interface.
Types of Factory Design Patterns
1. Simple Factory
2. Factory Method
3. Abstract Factory
1. Simple Factory
The Simple Factory pattern is not a standard GoF (Gang of Four) design pattern but is widely
used. It encapsulates the object creation process.
2. Factory Method
The Factory Method pattern defines an interface for creating an object but allows subclasses to
alter the type of objects that will be created. It relies on inheritance: the object creation is
delegated to subclasses that implement the factory method to create objects.
3. Abstract Factory
The Abstract Factory pattern provides an interface for creating families of related or dependent
objects without specifying their concrete classes. It involves multiple factories each producing
different sets of products.
@Override
public ProductB createProductB() {
return new ConcreteProductB1();
}
}
@Override
public ProductB createProductB() {
return new ConcreteProductB2();
}
}
• Simple Factory: Encapsulates object creation in a factory method but does not conform to
GoF patterns.
• Factory Method: Defines an interface for creating objects and lets subclasses decide which
class to instantiate. It promotes extensibility.
• Abstract Factory: Provides an interface for creating families of related or dependent objects.
It abstracts the creation process and promotes consistency among products in a family.
Builder Pattern
The Builder Pattern is a creational design pattern used to construct complex objects step by step.
It allows for the creation of different representations of an object using the same construction
process. The Builder Pattern is particularly useful when an object needs to be created with many
optional components or when the construction process is complex and needs to be separated
from the representation.
Key Characteristics
Components
@Override
public String toString() {
return "Product [partA=" + partA + ", partB=" + partB + ", partC=" +
partC + "]";
}
}
The Builder interface declares the methods for creating different parts of the Product.
The ConcreteBuilder class implements the Builder interface and constructs the Product.
@Override
public void buildPartA() {
product.setPartA("Part A");
}
@Override
public void buildPartB() {
product.setPartB("Part B");
}
@Override
public void buildPartC() {
product.setPartC("Part C");
}
@Override
public Product getResult() {
return product;
}
}
Advantages
1. Encapsulation of Construction Logic: Separates the construction logic from the object
representation, making the code more modular.
2. Control Over Object Construction: Provides fine control over the construction process,
allowing for different configurations.
3. Immutability: Can be used to create immutable objects by ensuring that once built, the
object cannot be modified.
Disadvantages
1. Complexity: Introduces additional classes and interfaces, which can increase the
complexity of the codebase.
2. Overhead: May involve additional overhead when only simple object creation is
required.
Prototype Design Pattern
The Prototype Design Pattern is a creational design pattern that allows an object to create a copy
(clone) of itself. This pattern is useful when the cost of creating a new instance of an object is
high, or when an object needs to be duplicated with minor modifications. Instead of creating a
new instance from scratch, a prototype object is cloned.
Key Characteristics
1. Cloning: The core idea is to use an existing object as a prototype and clone it to create
new objects.
2. Performance: It can improve performance when creating an object is expensive or
complex.
3. Flexibility: Allows for easy modification of the cloned object without affecting the
original.
Participants
The ConcretePrototype class implements the Prototype interface and provides the cloning
logic.
@Override
public Prototype clone() {
try {
// Shallow copy using Object's clone method
return (ConcretePrototype) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
@Override
public String toString() {
return "ConcretePrototype [name=" + name + ", age=" + age + "]";
}
The client code uses the clone() method to create a copy of an object.
• Shallow Copy: The clone() method performs a shallow copy by default. This means
that the cloned object and the original object share references to the same objects for any
fields that are objects themselves.
• Deep Copy: If you want to create a deep copy (where all objects are fully cloned), you
need to implement this manually, ensuring that all nested objects are also cloned.
DeepCopy
@Override
public Prototype clone() {
try {
// Shallow copy
ConcretePrototype cloned = (ConcretePrototype) super.clone();
// Deep copy of mutable field
cloned.hobbies = new ArrayList<>(this.hobbies);
return cloned;
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
@Override
public String toString() {
return "ConcretePrototype [name=" + name + ", age=" + age + ", hobbies="
+ hobbies + "]";
}
Disadvantages
1. Cloning Complexity: Cloning complex objects with deep copy requirements can be
tricky and error-prone.
2. Deep Copy: Implementing deep copies can lead to performance overhead if not managed
carefully.
Key Structural Design Patterns
1. Adapter (Wrapper):
o Converts the interface of a class into another interface that a client expects. The
Adapter allows classes to work together that couldn't otherwise because of
incompatible interfaces.
o Useful when you want to use an existing class, but its interface does not match the
one you need.
2. Bridge:
o Decouples an abstraction from its implementation, allowing them to vary
independently.
o Useful when both the abstractions and their implementations should be extensible
by subclassing, and you need to avoid a permanent binding between the two.
3. Composite:
o Composes objects into tree structures to represent part-whole hierarchies.
Composite lets clients treat individual objects and compositions of objects
uniformly.
o Useful when you need to treat individual objects and compositions of objects
uniformly, like in a file directory system where files and folders are treated
similarly.
4. Decorator:
o Adds behavior to individual objects, either statically or dynamically, without
affecting the behavior of other objects from the same class.
o Useful when you want to add responsibilities to objects dynamically and
transparently, without affecting other objects.
5. Facade:
o Provides a simplified interface to a complex subsystem, making the subsystem
easier to use.
o Useful when you want to provide a simple interface to a complex subsystem, like
providing a unified interface for a library of functions.
6. Flyweight:
o Reduces the cost of creating and manipulating a large number of similar objects
by sharing as much data as possible with similar objects.
o Useful when you have a large number of objects that are almost identical, like
characters in a text editor, where most characters share the same font and size.
7. Proxy:
o Provides a surrogate or placeholder for another object to control access to it.
o Useful when you need to control access to an object, like in lazy initialization,
access control, or remote proxies that represent objects in different address
spaces.
Structural design patterns are a vital aspect of software design, addressing how objects and
classes are composed to form larger, more complex structures. These patterns focus on
simplifying relationships between entities, promoting code reuse, and ensuring that the system is
adaptable to change.
At the core of structural design patterns is the principle of composition over inheritance. While
inheritance is a powerful feature in object-oriented programming, it can lead to tightly coupled
systems where changes in one part of the hierarchy ripple through the entire structure. Structural
patterns, by emphasizing composition, encourage the building of systems where objects are
composed or assembled to perform complex tasks, rather than relying on deep inheritance
hierarchies.
This approach to design supports the creation of flexible and maintainable systems. By focusing
on the composition of objects, structural patterns allow for different parts of a system to evolve
independently. This means that changes to one part of the system are less likely to require
changes to other parts, thereby reducing the potential for bugs and making the system easier to
maintain.
In the broader context of system design, structural design patterns are employed to address
specific challenges related to object composition and interaction. These patterns are not isolated
solutions but are part of a holistic approach to building systems that are easy to understand,
maintain, and extend.
Structural patterns are particularly useful in large, complex systems where the interactions
between objects can become difficult to manage. By introducing clear, well-defined structures,
these patterns help to manage complexity, ensuring that the system remains robust and adaptable
as it evolves over time.
Conclusion
Structural design patterns are a foundational concept in software design, providing strategies for
composing objects and classes into larger, more complex structures. By focusing on composition
over inheritance, these patterns promote flexibility, reusability, and maintainability. They
simplify complex interactions, encapsulate implementation details, and facilitate scalability,
making them essential tools in the design of robust, adaptable software systems.
Adapter Design Pattern
The Adapter Design Pattern is a structural 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 that a client expects. The pattern is often used when you want to use a
class that doesn't fit into the existing class structure.
Key Characteristics
1. Interface Compatibility: The primary function of the Adapter Pattern is to allow two
incompatible interfaces to work together.
2. Reusability: It promotes the reuse of existing classes by adapting their interface to fit the
needs of the client.
3. Flexibility: Allows for the integration of legacy or third-party classes into a new system
without modifying their source code.
4. Decoupling: Decouples the client code from the implementation details of the
incompatible class by using the adapter as an intermediary.
Participants
1. Target: This is the interface that the client expects. The adapter must implement this
interface.
2. Adapter: This is the class that implements the Target interface and adapts the Adaptee
to the Target.
3. Adaptee: This is the existing class with an incompatible interface that needs to be
adapted.
4. Client: The client interacts with the Target interface, unaware of the presence of the
Adapter.
UML Diagram
Implementation in Java
The Adapter class implements the Target interface and adapts the Adaptee to the Target.
@Override
public void request() {
// Delegates the call to the adaptee's specificRequest method
adaptee.specificRequest();
}
}
The client interacts with the Target interface, which is implemented by the Adapter.
1. Web Development
In web development, the Adapter pattern can be useful in the following scenarios:
• Legacy Code Integration: If you are working on a new web application but need to
integrate functionality from an older system with a different API, an adapter can be
created to allow the new application to interact with the old system seamlessly.
• API Wrapping: When integrating third-party APIs that do not fit the interface required
by your application, an adapter can be used to wrap the third-party API and expose a
consistent interface to your application.
Example:
Suppose you're integrating a payment gateway into your e-commerce site. The payment
gateway has a different interface than what your system uses. You can create an adapter
that translates your system's payment request format into the format expected by the
payment gateway.
2. Android Development
In Android development, the Adapter pattern is commonly used in the following contexts:
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
View view = inflater.inflate(R.layout.my_text_view, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
String item = mData.get(position);
holder.myTextView.setText(item);
}
@Override
public int getItemCount() {
return mData.size();
}
Example:
Advantages
1. Increased Reusability: Allows for the reuse of existing classes even if their interfaces
are incompatible.
2. Flexibility: Adapts an interface to meet new requirements without changing the existing
code.
3. Decoupling: Reduces the dependency between client code and the underlying class
structure.
Disadvantages
1. Complexity: Introduces additional layers, which can increase the complexity of the
codebase.
2. Overhead: The use of adapters may introduce a slight performance overhead.
Conclusion
The Adapter Design Pattern is a powerful tool for making incompatible interfaces work together.
It is widely used in both web and Android development to ensure that different parts of a system
1. Bridge Design Pattern
The Bridge Pattern is a structural design pattern that decouples an abstraction from its
implementation so that the two can vary independently. This pattern is useful when you want to
avoid a permanent binding between an abstraction and its implementation.
UML Diagram
Implementation in Java
2. Implement ConcreteImplementors
Implement RefinedAbstraction
@Override
public void operation() {
implementor.operationImpl();
}
}
Client Code
Intent: The Composite pattern allows you to compose objects into tree structures to represent
part-whole hierarchies. It lets clients treat individual objects and compositions of objects
uniformly.
Benefits:
• Uniformity: Clients can treat individual objects and compositions of objects in a uniform
manner.
• Flexibility: Easily add new types of components (leaf or composite) without changing
existing code.
• Ease of Use: Simplifies client code by allowing it to work with both single objects and
groups of objects in the same way.
Properties:
• Component: Defines the interface for all objects in the composition, including both leaf
and composite objects.
• Leaf: Represents leaf objects in the composition. A leaf object does not have any
children.
• Composite: Defines a behavior for components having children and stores child
components.
Example:
In a graphic design application, both individual shapes (like circles and squares) and groups of
shapes can be treated uniformly. This allows for hierarchical composition, such as a group of
shapes being treated as a single entity.
Code Implementation
// Component
interface Graphic {
void draw();
}
// Leaf
class Circle implements Graphic {
@Override
public void draw() {
System.out.println("Drawing a Circle");
}
}
// Composite
class CompositeGraphic implements Graphic {
private List<Graphic> graphics = new ArrayList<>();
@Override
public void draw() {
for (Graphic graphic : graphics) {
graphic.draw();
}
}
}
// Client code
public class CompositePatternDemo {
public static void main(String[] args) {
Circle circle1 = new Circle();
Square square1 = new Square();
Explanation:
• Graphic: The Component interface declares the draw method that is implemented by
both Leaf (simple shapes like Circle and Square) and Composite (a group of shapes).
• Circle and Square: The Leaf objects that implement the Graphic interface.
• CompositeGraphic: The Composite class that can contain Graphic objects (both Leaf
and Composite).
• Client: The client interacts with the objects through the Graphic interface, allowing for
uniform treatment of individual and composite objects.
2. Decorator Design Pattern
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. This pattern is often used to adhere to the Single Responsibility
Principle (SRP) by allowing functionality to be divided between classes with unique areas of
concern.
Key Concepts
• Component: The interface or abstract class that defines the common behavior for both
the concrete component and decorators.
• Concrete Component: The original object to which additional behavior will be added.
• Decorator: An abstract class that implements the Component interface and contains a
reference to a Component object. It forwards the requests to the component it wraps and
may add some behavior before or after forwarding.
• Concrete Decorator: A class that extends the Decorator class and overrides the
component’s methods to add additional behavior.
How it Works
1. Component Interface: Defines the interface that will be implemented by both the
ConcreteComponent and the decorators.
2. ConcreteComponent: The class that will have new behaviors added to it dynamically.
3. Decorator Class: Contains a reference to a Component object and implements the
Component interface. It forwards requests to the ConcreteComponent, adding additional
behavior.
4. Concrete Decorators: They extend the Decorator class and implement specific
behavior to be added to the ConcreteComponent.
Example
Imagine a coffee shop where you can order different types of coffee and add extras like milk,
sugar, or cream. In this scenario:
@Override
public String getDescription() {
return "Simple Coffee";
}
@Override
public double getCost() {
return 5.0;
}
}
@Override
public String getDescription() {
return "Simple Coffee";
}
@Override
public double getCost() {
return 5.0;
}
}
@Override
public String getDescription() {
return decoratedCoffee.getDescription();
}
@Override
public double getCost() {
return decoratedCoffee.getCost();
}
}
@Override
public String getDescription() {
return decoratedCoffee.getDescription() + ", Milk";
}
@Override
public double getCost() {
return decoratedCoffee.getCost() + 1.5;
}
}
@Override
public String getDescription() {
return decoratedCoffee.getDescription() + ", Sugar";
}
@Override
public double getCost() {
return decoratedCoffee.getCost() + 0.5;
}
}
The Facade Design Pattern is a structural design pattern that provides a simplified interface to a
complex subsystem. The main idea is to hide the complexities of the system and provide an easy-
to-use interface to the client. The Facade Pattern is particularly useful when working with a
complex set of classes or systems, as it reduces dependencies on the complex subsystem and
promotes loose coupling between the client and the subsystem.
Key Concepts
• Facade: A class that provides a simplified interface to a complex subsystem. The Facade
delegates the client requests to appropriate objects within the subsystem and may provide
additional functionality that simplifies the client's work.
• Subsystem: The collection of classes, libraries, or functions that the Facade is meant to
simplify. The subsystem itself is not aware of the Facade and does not communicate with
the client directly.
1. Simplifies Client Interaction: The client interacts with the Facade rather than dealing
with the complexities of the subsystem.
2. Reduces Coupling: The client is less dependent on the complex subsystem, making the
system easier to maintain and modify.
3. Promotes Reusability: By hiding the subsystem behind a Facade, the subsystem can be
reused in different contexts without exposing its complexities.
Example
Let's consider a home theater system, which involves various components like a DVD player,
sound system, and projector. Without the Facade Pattern, a client would have to interact with
each of these components separately to turn on the system and start a movie.
Step 1: Define the Subsystem Classes
// Subsystem class for DVD Player
public class DVDPlayer {
public void on() {
System.out.println("DVD Player is on.");
}
Intent: The Flyweight pattern reduces the cost of creating and manipulating a large number of
similar objects by sharing common parts.
The Flyweight Design Pattern is a structural design pattern that is used to minimize memory
usage or computational expenses by sharing as much data as possible with similar objects. It is
particularly useful in applications where a large number of objects need to be created, but many
of them can share common data.
Key Concepts
• Flyweight: The shared object that can be used in multiple contexts simultaneously. It is
immutable and should contain intrinsic state (data that can be shared).
• Intrinsic State: The state that is shared across different objects. This data is stored in the
Flyweight object and is independent of the context.
• Extrinsic State: The state that is unique to each object and cannot be shared. This data is
passed to the Flyweight object when it is used.
• Flyweight Factory: A factory that manages and creates Flyweight objects. It ensures that
Flyweights are shared rather than created anew each time.
Benefits of the Flyweight Pattern
1. Memory Efficiency: By sharing common data between objects, the pattern greatly
reduces the amount of memory needed.
2. Performance Improvement: Reducing the number of objects can improve performance,
particularly in resource-constrained environments.
3. Simplification: The pattern simplifies object creation by ensuring that common data is
centralized and managed effectively.
Example
Consider a scenario where we need to create a large number of tree objects in a forest
visualization application. Instead of creating a separate object for each tree, we can use the
Flyweight Pattern to share common properties like tree type, color, and texture.
Step 1: Define the Flyweight Interface
// Flyweight Interface
public interface Tree {
void display(int x, int y); // Extrinsic state: coordinates where the tree
will be displayed
}
@Override
public void display(int x, int y) {
System.out.println("Displaying " + name + " tree at (" + x + ", " + y +
") with color " + color + " and texture " + texture);
}
}
Intent: The Proxy pattern provides a surrogate or placeholder for another object to control
access to it.
The Proxy Design Pattern is one of the structural design patterns in software engineering. It
provides a surrogate or placeholder for another object to control access to it. The Proxy pattern is
useful when you need to add an additional level of control over the actual object, such as
controlling access, managing resource-intensive operations, or adding security.
Types of Proxy
1. Virtual Proxy: Manages access to a resource that is expensive to create. The proxy
creates the real object only when it's needed.
2. Remote Proxy: Acts as a local representative for an object that is in a different address
space, like a network or another server.
3. Protection Proxy: Controls access to the original object, based on access rights.
4. Smart Proxy: Provides additional functionality when accessing an object, such as
reference counting, lazy initialization, or logging.
• Controlled Access: Proxies can control access to the real object, ensuring that only
authorized or appropriate operations are performed.
• Lazy Initialization: The proxy can delay the creation of an expensive object until it's
needed, optimizing resource usage.
• Security: By providing a protection proxy, you can add an extra layer of security,
ensuring only certain clients can access the real object.
• Logging and Auditing: Proxies can keep track of the usage of the real object, providing
logs or audit trails.
• Remote Access: Allows access to objects located in different locations or systems
through a remote proxy.
• Subject: An interface or abstract class that defines the common operations for both the
RealSubject and the Proxy.
• RealSubject: The actual object that performs the real operations or actions.
• Proxy: The object that controls access to the RealSubject. It implements the same
interface as the RealSubject and maintains a reference to the RealSubject.
Example in Java
Let’s consider an image viewer application where images are stored on disk, and loading them
into memory is resource-intensive. The ProxyImage class will control access to the RealImage
class, loading the image only when necessary.
// Subject Interface
interface Image {
void display();
}
// RealSubject Class
class RealImage implements Image {
private String filename;
@Override
public void display() {
System.out.println("Displaying " + filename);
}
}
// Proxy Class
class ProxyImage implements Image {
private RealImage realImage;
private String filename;
public ProxyImage(String filename) {
this.filename = filename;
}
@Override
public void display() {
// Load the real image only when needed
if (realImage == null) {
realImage = new RealImage(filename);
}
realImage.display();
}
}
// Client Code
public class ProxyPatternDemo {
public static void main(String[] args) {
Image image = new ProxyImage("test_image.jpg");
Explanation
• Subject (Image): The Image interface defines the display method, which both
RealImage and ProxyImage implement.
• RealSubject (RealImage): This class represents the actual image. It loads the image
from the disk in its constructor and displays it when the display method is called.
• Proxy (ProxyImage): This class acts as a surrogate to RealImage. It defers the loading
of the image until the display method is called, thus implementing lazy loading.
Key Points
• Lazy Loading: The ProxyImage class delays the loading of the RealImage until it's
actually required (i.e., when display is called for the first time).
• Memory Efficiency: This pattern helps in reducing memory consumption and improves
performance by not loading the image into memory until needed.
• Controlled Access: The proxy controls when and how the RealImage is accessed.
Behavioral Design Patterns focus on how objects interact and communicate with each other.
Unlike structural patterns, which deal with object composition, and creational patterns, which
deal with object creation, behavioral patterns are concerned with the assignment of
responsibilities between objects, defining the ways in which they cooperate to achieve a goal.
These patterns help make the communication between objects more flexible, efficient, and
manageable.
1. Chain of Responsibility:
o Intent: Passes a request along a chain of handlers. Each handler decides either to
process the request or pass it to the next handler in the chain.
o Example: Logging frameworks where a log request might be passed from one
logger to another until it is processed by the appropriate logger.
2. Command:
o Intent: Encapsulates a request as an object, thereby allowing for parameterization
of clients with queues, requests, and operations.
o Example: Undo/redo functionality in applications like text editors.
3. Interpreter:
o Intent: Defines a grammatical representation for a language and provides an
interpreter to interpret sentences in the language.
o Example: SQL query processing or mathematical expression evaluation.
4. Iterator:
o Intent: Provides a way to access elements of a collection sequentially without
exposing the underlying representation.
o Example: Iterating over a collection of objects in Java using the Iterator
interface.
5. Mediator:
o Intent: Defines an object that encapsulates how a set of objects interact,
promoting loose coupling by preventing direct interactions between objects.
o Example: A chat room that handles communication between users, where the
users don’t communicate directly but through the mediator (chat room).
6. Memento:
o Intent: Captures and externalizes an object's internal state without violating
encapsulation, allowing the object to be restored to this state later.
o Example: The save and restore functionality in text editors or games.
7. Observer:
o Intent: Defines a one-to-many dependency between objects so that when one
object changes state, all its dependents are notified and updated automatically.
o Example: The observer pattern is commonly used in implementing distributed
event-handling systems, such as GUI frameworks (e.g., listeners for button
clicks).
8. State:
o Intent: Allows an object to alter its behavior when its internal state changes. The
object will appear to change its class.
o Example: A state machine where an object changes its behavior based on its
current state, such as a TCP connection which can be in states like Closed,
Listening, or Established.
9. Strategy:
o Intent: Defines a family of algorithms, encapsulates each one, and makes them
interchangeable. The strategy pattern lets the algorithm vary independently from
the clients that use it.
o Example: Different sorting algorithms (e.g., quicksort, mergesort) that can be
swapped out at runtime.
10. Template Method:
o Intent: Defines the skeleton of an algorithm in a method, deferring some steps to
subclasses. Template Method lets subclasses redefine certain steps of an
algorithm without changing the algorithm's structure.
o Example: A method in a base class that sets up a game loop, with certain steps
like initializing the game or ending the game left to be implemented by
subclasses.
11. Visitor:
o Intent: Represents an operation to be performed on the elements of an object
structure. The Visitor pattern lets you define a new operation without changing
the classes of the elements on which it operates.
o Example: A tax calculation system where different types of tax calculations
(visitor) can be applied to different types of products or income (elements).
Summary
Behavioral Design Patterns are essential in software design because they offer powerful ways to
manage and streamline the communication and interaction between objects. They help in
organizing complex control flows, ensuring that objects remain loosely coupled, and
responsibilities are well-distributed. These patterns are widely used in real-world applications to
achieve more modular, maintainable, and extensible codebases.
Intent: The Chain of Responsibility pattern allows an object to pass a request along a chain of
handlers. Each handler decides whether to process the request or pass it to the next handler in the
chain.
Benefits:
Properties:
• Handler: Defines an interface for handling requests and optionally implements default
behavior.
• ConcreteHandler: Handles requests and can forward them to the next handler in the
chain.
• Client: Initiates the request to the first handler in the chain.
// Handler Interface
abstract class Handler {
protected Handler nextHandler;
// Concrete Handler 1
class ConcreteHandlerA extends Handler {
@Override
public void handleRequest(String request) {
if (request.equals("A")) {
System.out.println("Handler A processing request A");
} else if (nextHandler != null) {
nextHandler.handleRequest(request);
}
}
}
// Concrete Handler 2
class ConcreteHandlerB extends Handler {
@Override
public void handleRequest(String request) {
if (request.equals("B")) {
System.out.println("Handler B processing request B");
} else if (nextHandler != null) {
nextHandler.handleRequest(request);
}
}
}
// Client Code
public class ChainOfResponsibilityDemo {
public static void main(String[] args) {
Handler handlerA = new ConcreteHandlerA();
Handler handlerB = new ConcreteHandlerB();
handlerA.setNextHandler(handlerB);
2. Command
Key Concepts:
Example Code:
// Command interface
interface Command {
void execute();
}
// Receiver
class Light {
public void turnOn() {
System.out.println("Light is on");
}
// ConcreteCommand
class LightOnCommand implements Command {
private Light light;
@Override
public void execute() {
light.turnOn();
}
}
// ConcreteCommand
class LightOffCommand implements Command {
private Light light;
@Override
public void execute() {
light.turnOff();
}
}
// Invoker
class RemoteControl {
private Command command;
// Client
public class CommandPatternDemo {
public static void main(String[] args) {
Light light = new Light();
Command lightOn = new LightOnCommand(light);
Command lightOff = new LightOffCommand(light);
remote.setCommand(lightOff);
remote.pressButton();
}
}
3. Interpreter
Purpose: The Interpreter pattern defines a grammatical representation for a language and
provides an interpreter to interpret sentences in that language.
Key Concepts:
// Expression interface
interface Expression {
boolean interpret(String context);
}
// TerminalExpression
class TerminalExpression implements Expression {
private String data;
@Override
public boolean interpret(String context) {
return context.contains(data);
}
}
// NonTerminalExpression
class OrExpression implements Expression {
private Expression expr1;
private Expression expr2;
@Override
public boolean interpret(String context) {
return expr1.interpret(context) || expr2.interpret(context);
}
}
// Client
public class InterpreterPatternDemo {
public static void main(String[] args) {
Expression isJava = new TerminalExpression("Java");
Expression isPython = new TerminalExpression("Python");
4. Iterator
Purpose: The Iterator pattern provides a way to access the elements of an aggregate object
sequentially without exposing its underlying representation.
Key Concepts:
import java.util.ArrayList;
import java.util.List;
// Iterator interface
interface Iterator<T> {
boolean hasNext();
T next();
}
// Aggregate interface
interface Aggregate<T> {
Iterator<T> createIterator();
}
// ConcreteIterator
class ConcreteIterator<T> implements Iterator<T> {
private List<T> items;
private int position = 0;
@Override
public boolean hasNext() {
return position < items.size();
}
@Override
public T next() {
return items.get(position++);
}
}
// ConcreteAggregate
class ConcreteAggregate<T> implements Aggregate<T> {
private List<T> items = new ArrayList<>();
@Override
public Iterator<T> createIterator() {
return new ConcreteIterator<>(items);
}
}
// Client
public class IteratorPatternDemo {
public static void main(String[] args) {
ConcreteAggregate<String> aggregate = new ConcreteAggregate<>();
aggregate.add("Item1");
aggregate.add("Item2");
aggregate.add("Item3");
5. Mediator Pattern
Intent: The Mediator pattern defines an object that encapsulates how a set of objects interact,
promoting loose coupling by preventing direct interactions between objects.
Benefits:
Properties:
// Mediator Interface
interface Mediator {
void send(String message, Colleague colleague);
}
// Concrete Mediator
class ConcreteMediator implements Mediator {
private ColleagueA colleagueA;
private ColleagueB colleagueB;
@Override
public void send(String message, Colleague colleague) {
if (colleague == colleagueA) {
colleagueB.receive(message);
} else if (colleague == colleagueB) {
colleagueA.receive(message);
}
}
}
// Colleague Interface
abstract class Colleague {
protected Mediator mediator;
// Concrete Colleague A
class ColleagueA extends Colleague {
public ColleagueA(Mediator mediator) {
super(mediator);
}
@Override
public void receive(String message) {
System.out.println("Colleague A received: " + message);
}
// Concrete Colleague B
class ColleagueB extends Colleague {
public ColleagueB(Mediator mediator) {
super(mediator);
}
@Override
public void receive(String message) {
System.out.println("Colleague B received: " + message);
}
// Client Code
public class MediatorPatternDemo {
public static void main(String[] args) {
ConcreteMediator mediator = new ConcreteMediator();
mediator.setColleagueA(colleagueA);
mediator.setColleagueB(colleagueB);
Intent: The Memento pattern captures and externalizes an object's internal state without
violating encapsulation, allowing the object to be restored to this state later.
Benefits:
• Encapsulation Preservation: Allows you to save and restore object states without
exposing its internal structure.
• Undo/Redo Operations: Useful for implementing undo/redo functionality.
Properties:
Example Code:
// Memento Class
class Memento {
private String state;
// Originator Class
class Originator {
private String state;
// Caretaker Class
class Caretaker {
private Memento memento;
// Client Code
public class MementoPatternDemo {
public static void main(String[] args) {
Originator originator = new Originator();
Caretaker caretaker = new Caretaker();
originator.setState("State1");
caretaker.saveState(originator);
originator.setState("State2");
System.out.println("Current State: " + originator.getState()); // Output:
State2
caretaker.restoreState(originator);
System.out.println("Restored State: " + originator.getState()); //
Output: State1
}
}
7. Observer Pattern
Intent: 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:
Properties:
Example Code:
import java.util.ArrayList;
import java.util.List;
// Observer Interface
interface Observer {
void update(String message);
}
// Subject Class
class Subject {
private List<Observer> observers = new ArrayList<>();
// Concrete Observer
class ConcreteObserver implements Observer {
private String name;
@Override
public void update(String message) {
System.out.println(name + " received message: " + message);
}
}
// Client Code
public class ObserverPatternDemo {
public static void main(String[] args) {
Subject subject = new Subject();
subject.addObserver(observer1);
subject.addObserver(observer2);
subject.notifyObservers("Hello Observers!");
}
}
8. State Pattern
Intent: The State pattern allows an object to alter its behavior when its internal state changes.
The object will appear to change its class.
Benefits:
Properties:
• Context: Maintains an instance of a ConcreteState subclass that defines the current state.
• State: Defines an interface for encapsulating the behavior associated with a particular
state.
• ConcreteState: Implements the state-specific behavior.
Example Code:
// State Interface
interface State {
void handle();
}
// Concrete State A
class ConcreteStateA implements State {
@Override
public void handle() {
System.out.println("Handling request in State A");
}
}
// Concrete State B
class ConcreteStateB implements State {
@Override
public void handle() {
System.out.println("Handling request in State B");
}
}
// Context Class
class Context {
private State state;
// Client Code
public class StatePatternDemo {
public static void main(String[] args) {
Context context = new Context();
context.setState(stateA);
context.request(); // Output: Handling request in State A
context.setState(stateB);
context.request(); // Output: Handling request in State B
}
}
10. Template Method Pattern
Intent: The Template Method pattern defines the skeleton of an algorithm in a method, deferring
some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm
without changing the algorithm's structure.
Benefits:
• Code Reuse: Promotes code reuse by defining common behavior in a base class.
• Extensibility: Allows subclasses to customize parts of the algorithm.
Properties:
Example Code:
// Abstract Class
abstract class AbstractClass {
// Template Method
public final void templateMethod() {
step1();
step2();
step3();
}
// Concrete Class A
class ConcreteClassA extends AbstractClass {
@Override
protected void step1() {
System.out.println("ConcreteClassA: Step 1");
}
@Override
protected void step2() {
System.out.println("ConcreteClassA: Step 2");
}
}
// Concrete Class B
class ConcreteClassB extends AbstractClass {
@Override
protected void step1() {
System.out.println("ConcreteClassB: Step 1");
}
@Override
protected void step2() {
System.out.println("ConcreteClassB: Step 2");
}
}
// Client Code
public class TemplateMethodPatternDemo {
public static void main(String[] args) {
AbstractClass classA = new ConcreteClassA();
classA.templateMethod();