UNIT-3(OOPS)

Download as docx, pdf, or txt
Download as docx, pdf, or txt
You are on page 1of 43

Unit-3(Design patterns)

Design patterns
Reusable solutions for typical software design challenges are
known as design patterns. Expert object-oriented software
engineers use these best practices to write more structured,
manageable, and scalable code. Design patterns provide a
standard terminology and are specific to particular scenarios and
problems. Design patterns are not finished code but templates or
blueprints only.
Key Characteristics of Design Patterns
 Reusability: Patterns can be applied to different projects and
problems, saving time and effort in solving similar issues.
 Standardization: They provide a shared language and
understanding among developers, helping in communication
and collaboration.
 Efficiency: By using these popular patterns, developers can
avoid finding the solution to same recurring problems, which
leads to faster development.
 Flexibility: Patterns are abstract solutions/templates that can
be adapted to fit various scenarios and requirements.
Why Learn Design Patterns?
There are multiple reasons to learn design patterns:
 Code that is simpler to comprehend, update, and expand is
produced with the help of design patterns.
 They offer solutions that have been tried and tested as well as
best practices.
 Learning this enables them to quickly and effectively address
similar challenges in various projects.
 Developers can produce reusable components that can be
utilized in a variety of applications by implementing design
patterns.
 This reduces redundancy and saves development time.
Types of Software Design Patterns
There are three types of Design Patterns:
 Creational Design Pattern
 Structural Design Pattern
 Behavioral Design Pattern
1. Creational Design Patterns
Creational Design Patterns focus on the process of object
creation or problems related to object creation. They help in
making a system independent of how its objects are created,
composed and represented.

Types of Creational Design Patterns:


 Factory Method Design Pattern
o This pattern is typically helpful when it’s necessary to
separate the construction of an object from its
implementation.
o With the use of this design pattern, objects can be
produced without having to define the exact class of
object to be created.
 Abstract Factory Method Design Pattern
o Abstract Factory pattern is almost similar to Factory
Pattern and is considered as another layer of
abstraction over factory pattern.
o Abstract Factory patterns work around a super-factory
which creates other factories.
 Singleton Method Design Pattern
o Of all, the Singleton Design pattern is the most
straightforward to understand.
o It guarantees that a class has just one instance and
offers a way to access it globally.
 Prototype Method Design Pattern
o Prototype allows us to hide the complexity of making
new instances from the client.
o The concept is to copy an existing object rather than
creating a new instance from scratch, something that
may include costly operations.
 Builder Method Design Pattern
o To “Separate the construction of a complex object
from its representation so that the same construction
process can create different representations.” Builder
pattern is used
o It helps in constructing a complex object step by step
and the final step will return the object.
2. Structural Design Patterns
Structural Design Patterns solves problems related to how
classes and objects are composed/assembled to form larger
structures which are efficient and flexible in nature. Structural
class patterns use inheritance to compose interfaces or
implementations.

Types of Structural Design Patterns:


 Adapter Method Design Pattern
o The adapter pattern convert the interface of a class
into another interface clients expect.
o Adapter lets classes work together that couldn’t
otherwise because of incompatible interfaces.
 Bridge Method Design Pattern
o The bridge pattern allows the Abstraction and the
Implementation to be developed independently.
o The client code can access only the Abstraction part
without being concerned about the Implementation
part.
 Composite Method Design Pattern
o As a partitioning design pattern, the composite pattern
characterizes a collection of items that are handled
the same way as a single instance of the same type of
object.
o The intent of a composite is to “compose” objects into
tree structures to represent part-whole hierarchies.
 Decorator Method Design Pattern
o It allows us to dynamically add functionality and
behavior to an object without affecting the behavior of
other existing objects within the same class.
o We use inheritance to extend the behavior of the
class. This takes place at compile-time, and all the
instances of that class get the extended behavior.
 Facade Method Design Pattern
o Facade Method Design Pattern provides a unified
interface to a set of interfaces in a subsystem.
o Facade defines a high-level interface that makes the
subsystem easier to use.
 Flyweight Method Design Pattern
o This pattern provides ways to decrease object count
thus improving application required objects structure.
o Flyweight pattern is used when we need to create a
large number of similar objects.
 Proxy Method Design Pattern
o Proxy means ‘in place of’, representing’ or ‘in place of’
or ‘on behalf of’ are literal meanings of proxy and that
directly explains Proxy Design Pattern.
o Proxies are also called surrogates, handles, and
wrappers. They are closely related in structure, but not
purpose, to Adapters and Decorators.
3. Behavioral Design Patterns
Behavioral Patterns are concerned with algorithms and the
assignment of responsibilities between objects. Behavioral
patterns describe not just patterns of objects or classes but also
the patterns of communication between them. These patterns
characterize complex control flow that’s difficult to follow at run-
time.
Types of Behavioral Design Patterns:
 Chain Of Responsibility Method Design Pattern
o Chain of responsibility pattern is used to achieve loose
coupling in software design where a request from the
client is passed to a chain of objects to process them.
o Later, the object in the chain will decide themselves
who will be processing the request and whether the
request is required to be sent to the next object in the
chain or not.
 Command Method Design Pattern
o A behavioral design pattern called the Command
Pattern transforms a request into an independent
object with all of the information’s request
o This object can be passed around, stored, and
executed at a later time.
 Interpreter Method Design Pattern
o Interpreter pattern is used to defines a grammatical
representation for a language and provides an
interpreter to deal with this grammar.
 Mediator Method Design Pattern
o It enables decoupling of objects by introducing a layer
in between so that the interaction between objects
happen via the layer.
 Memento Method Design Patterns
o It is used to return an object’s state to its initial state.
o You might wish to create checkpoints in your
application and return to them at a later time when it
develops.
 Observer Method Design Pattern
o It establishes a one-to-many dependency between
objects, meaning that all of the dependents
(observers) of the subject are immediately updated
and notified when the subject changes.
 State Method Design Pattern
o When an object modifies its behavior according to its
internal state, the state design pattern is applied.
o If we have to change the behavior of an object based
on its state, we can have a state variable in the Object
and use the if-else condition block to perform different
actions based on the state.
 Strategy Method Design Pattern
o It is possible to select an object’s behavior at runtime
by utilizing the Strategy Design Pattern.
o Encapsulating a family of algorithms into distinct
classes that each implement a common interface is
the foundation of the Strategy pattern.
 Template Method Design Pattern
o The template method design pattern defines an
algorithm as a collection of skeleton operations, with
the child classes handling the implementation of the
specifics.
o The parent class maintains the overall structure and
flow of the algorithm.
 Visitor Method Design Pattern
o It is used when we have to perform an operation on a
group of similar kind of Objects. With the help of
visitor pattern, we can move the operational logic from
the objects to another class.
 Singleton Pattern
o The Singleton method or Singleton Design pattern is
one of the simplest design patterns. It ensures a class
only has one instance, and provides a global point of
access to it.
The Singleton method or Singleton Design pattern is one of the
simplest design patterns. It ensures a class only has one
instance, and provides a global point of access to it.
Singleton Design Pattern Principles
Below are the principles of the Singleton Pattern:
 Single Instance: Singleton ensures that only one instance of
the class exists throughout the application.
 Global Access: Provide a global point of access to that
instance.
 Lazy or Eager Initialization: Support creating the instance
either when needed (lazy) or when the class is loaded (eager).
 Thread Safety: Implement mechanisms to prevent multiple
threads from creating separate instances simultaneously.
 Private Constructor: Restrict direct instantiation by making
the constructor private, forcing the use of the access point
When to use Singleton Method Design Pattern?
Use the Singleton method Design Pattern when:
 Consider using the Singleton pattern when you need to ensure
that only one instance of a class exists in your application.
 Use it when you want to provide a straightforward way for
clients to access that instance from a specific location in your
code.
 If you think you might want to extend the class later, the
Singleton pattern is a good choice. It allows for subclassing, so
clients can work with the extended version without changing
the original Singleton.
 This pattern is often used in situations like logging, managing
connections to hardware or databases, caching data, or
handling thread pools, where having just one instance makes
sense.
Initialization of Types of Singleton
Singleton class can be instantiated by two methods:
 Early initialization : In this method, class is initialized
whether it is to be used or not. The main advantage of this
method is its simplicity. You initiate the class at the time of
class loading. Its drawback is that class is always initialized
whether it is being used or not.
 Lazy initialization : In this method, class in initialized only
when it is required. It can save you from instantiating the class
when you don’t need it. Generally, lazy initialization is used
when we create a singleton class.

1. Static Member:
The Singleton pattern or pattern Singleton employs a static
member within the class. This static member ensures that
memory is allocated only once, preserving the single instance of
the Singleton class.
Java

// Static member to hold the single instance


private static Singleton instance;
2. Private Constructor:
The Singleton pattern or pattern singleton incorporates a private
constructor, which serves as a barricade against external
attempts to create instances of the Singleton class. This ensures
that the class has control over its instantiation process.
Java

// Private constructor to
// prevent external instantiation
class Singleton {
// Making the constructor as Private
private Singleton()
{
// Initialization code here
}
}
3. Static Factory Method:
A crucial aspect of the Singleton pattern is the presence of a
static factory method. This method acts as a gateway, providing
a global point of access to the Singleton object. When someone
requests an instance, this method either creates a new instance
(if none exists) or returns the existing instance to the caller.
Java

// Static factory method for global access


public static Singleton getInstance()
{
// Check if an instance exists
if (instance == null) {
// If no instance exists, create one
instance = new Singleton();
}
// Return the existing instance
return instance;
}
Implementation of Singleton Method Design Pattern
The implementation of a Singleton Design Pattern or Pattern
Singleton is described in the following class diagram:

Implementation of Singleton Method Design Pattern

The implementation of the singleton Design pattern is very


simple and consists of a single class. To ensure that the singleton
instance is unique, all the singleton constructors should be made
private. Global access is done through a static method that can
be globally accesed to a single instance
 Factory Method Pattern
Factory Method Design Pattern is a creational design pattern
used in software development. It provides an interface for
creating objects in a superclass while allowing subclasses to
specify the types of objects they create.
 This pattern simplifies the object creation process by placing it
in a dedicated method, promoting loose coupling between the
object creator and the objects themselves.
 This approach enhances flexibility, extensibility, and
maintainability, enabling subclasses to implement their own
factory methods for creating specific object types.
When to Use the Factory Method Design Pattern
Below is when to use factory method design pattern:
 If your object creation process is complex or varies under
different conditions, using a factory method can make your
client code simpler and promote reusability.
 The Factory Method Pattern allows you to create objects
through an interface or abstract class, hiding the details of
concrete implementations. This reduces dependencies and
makes it easier to modify or expand the system without
affecting existing code.
 If your application needs to create different versions of a
product or may introduce new types in the future, the Factory
Method Pattern provides a flexible way to handle these
variations by defining specific factory methods for each
product type.
 Factories can also encapsulate configuration logic, allowing
clients to customize the object creation process by providing
parameters or options to the factory method.
Components of Factory Method Design Pattern
Below are the main components of Factory Design Pattern:
 Creator: This is an abstract class or an interface that declares
the factory method. The creator typically contains a method
that serves as a factory for creating objects. It may also
contain other methods that work with the created objects.
 Concrete Creator: Concrete Creator classes are subclasses
of the Creator that implement the factory method to create
specific types of objects. Each Concrete Creator is responsible
for creating a particular product.
 Product: This is the interface or abstract class for the objects
that the factory method creates. The Product defines the
common interface for all objects that the factory method can
create.
 Concrete Product: Concrete Product classes are the actual
objects that the factory method creates. Each Concrete
Product class implements the Product interface or extends the
Product abstract class.
Factory Method Design Pattern Example
Consider a software application that needs to handle the
creation of various types of vehicles, such as Two Wheelers,
Three Wheelers, and Four Wheelers. Each type of vehicle has
its own specific properties and behaviors.

// Library classes
abstract class Vehicle {
public abstract void printVehicle();
}

class TwoWheeler extends Vehicle {


public void printVehicle() {
System.out.println("I am two wheeler");
}
}

class FourWheeler extends Vehicle {


public void printVehicle() {
System.out.println("I am four wheeler");
}
}

// Factory Interface
interface VehicleFactory {
Vehicle createVehicle();
}

// Concrete Factory for TwoWheeler


class TwoWheelerFactory implements VehicleFactory {
public Vehicle createVehicle() {
return new TwoWheeler();
}
}

// Concrete Factory for FourWheeler


class FourWheelerFactory implements VehicleFactory {
public Vehicle createVehicle() {
return new FourWheeler();
}
}

// Client class
class Client {
private Vehicle pVehicle;

public Client(VehicleFactory factory) {


pVehicle = factory.createVehicle();
}

public Vehicle getVehicle() {


return pVehicle;
}
}

// Driver program
public class GFG {
public static void main(String[] args) {
VehicleFactory twoWheelerFactory = new
TwoWheelerFactory();
Client twoWheelerClient = new
Client(twoWheelerFactory);
Vehicle twoWheeler = twoWheelerClient.getVehicle();
twoWheeler.printVehicle();

VehicleFactory fourWheelerFactory = new


FourWheelerFactory();
Client fourWheelerClient = new
Client(fourWheelerFactory);
Vehicle fourWheeler = fourWheelerClient.getVehicle();
fourWheeler.printVehicle();
}
}
Output:
I am two wheeler
I am four wheeler

 Abstract Factory Pattern


The Abstract Factory Pattern is a way of organizing how you
create groups of things that are related to each other. It provides
a set of rules or instructions that let you create different types of
things without knowing exactly what those things are. This helps
you keep everything organized and lets you switch between
different types easily.
 Abstract Factory pattern is almost same as Factory
Pattern and is considered as another layer of abstraction over
factory pattern.
 Abstract Factory patterns work around a super-factory which
creates other factories.
 At runtime, the abstract factory is coupled with any desired
concrete factory which can create objects of the desired type.
Components of Abstract Factory Pattern
To understand abstract factory pattern, we have to understand
the components of it and relationships between them.
 Abstract Factory:
o Abstract Factory provides a high-level blueprint that
defines rules for creating families of related object
without specifying their concrete classes.
o It provides a way such that concrete factories follow a
common interface, providing consistent way to
produce related set of objects.
 Concrete Factories:
o Concrete Factories implement the rules specified by
the abstract factory. It contain the logic for creating
specific instances of objects within a family.
o Also multiple concrete factories can exist, each
produce a distinct family of related objects.
 Abstract Products:
o Abstract Products represents a family of related
objects by defining a set of common methods or
properties.
o It acts as an abstract or interface type that all
concrete products within a family must follow to and
provides a unified way for concrete products to be
used interchangeably.
 Concrete Products:
o They are the actual instances of objects created by
concrete factories.
o They implement the methods declared in the abstract
products, ensuring consistency within a family and
belong to a specific category or family of related
objects.
 Client:
o Client utilizes the abstract factory to create families of
objects without specifying their concrete types and
interacts with objects through abstract interfaces
provided by abstract products.
Example of Abstract Factory Design Pattern
Imagine you’re managing a global car manufacturing company
You want to design a system to create cars with specific
configurations for different regions, such as North America and
Europe.
Each region may have unique requirements and regulations, and
you want to ensure that cars produced for each region meet
those standards.
// Abstract Factory Interface
interface CarFactory {
Car createCar();
CarSpecification createSpecification();
}

// Concrete Factory for North America Cars


class NorthAmericaCarFactory implements CarFactory {
public Car createCar() {
return new Sedan();
}

public CarSpecification createSpecification() {


return new NorthAmericaSpecification();
}
}

// Concrete Factory for Europe Cars


class EuropeCarFactory implements CarFactory {
public Car createCar() {
return new Hatchback();
}

public CarSpecification createSpecification() {


return new EuropeSpecification();
}
}

// Abstract Product Interface for Cars


interface Car {
void assemble();
}

// Abstract Product Interface for Car Specifications


interface CarSpecification {
void display();
}

// Concrete Product for Sedan Car


class Sedan implements Car {
public void assemble() {
System.out.println("Assembling Sedan car.");
}
}

// Concrete Product for Hatchback Car


class Hatchback implements Car {
public void assemble() {
System.out.println("Assembling Hatchback car.");
}
}

// Concrete Product for North America Car Specification


class NorthAmericaSpecification implements CarSpecification {
public void display() {
System.out.println("North America Car Specification: Safety features
compliant with local regulations.");
}
}

// Concrete Product for Europe Car Specification


class EuropeSpecification implements CarSpecification {
public void display() {
System.out.println("Europe Car Specification: Fuel efficiency and emissions
compliant with EU standards.");
}
}

// Client Code
public class CarFactoryClient {
public static void main(String[] args) {
// Creating cars for North America
CarFactory northAmericaFactory = new NorthAmericaCarFactory();
Car northAmericaCar = northAmericaFactory.createCar();
CarSpecification northAmericaSpec =
northAmericaFactory.createSpecification();

northAmericaCar.assemble();
northAmericaSpec.display();

// Creating cars for Europe


CarFactory europeFactory = new EuropeCarFactory();
Car europeCar = europeFactory.createCar();
CarSpecification europeSpec = europeFactory.createSpecification();
europeCar.assemble();
europeSpec.display();
}
}
Output:

Assembling Sedan car.


North America Car Specification: Safety features compliant with local
regulations.
Assembling Hatchback car.
Europe Car Specification: Fuel efficiency and emissions compliant wit...

 Bridge design Pattern


The Bridge design pattern allows you to separate the abstraction
from the implementation. It is a structural design pattern.
There are 2 parts in Bridge design pattern :
1. Abstraction
2. Implementation
This is a design mechanism that encapsulates an implementation
class inside of an interface class.
 The bridge pattern allows the Abstraction and the
Implementation to be developed independently and the client
code can access only the Abstraction part without being
concerned about the Implementation part.
 The abstraction is an interface or abstract class and the
implementer is also an interface or abstract class.
 The abstraction contains a reference to the implementer.
Children of the abstraction are referred to as refined
abstractions, and children of the implementer are concrete
implementers. Since we can change the reference to the
implementer in the abstraction, we are able to change the
abstraction’s implementer at run-time. Changes to the
implementer do not affect client code.
 It increases the loose coupling between class abstraction and
it’s implementation.
For a thorough understanding of the Bridge Design Pattern, the
System Design Course offers detailed examples and practical
applications to help you implement it in your projects.
UML Diagram of Bridge Design Pattern
Elements of Bridge Design Pattern
 Abstraction – core of the bridge design pattern and defines
the crux. Contains a reference to the implementer.
 Refined Abstraction – Extends the abstraction takes the
finer detail one level below. Hides the finer elements from
implementers.
 Implementer – It defines the interface for implementation
classes. This interface does not need to correspond directly to
the abstraction interface and can be very different.
Abstraction imp provides an implementation in terms of
operations provided by the Implementer interface.
 Concrete Implementation – Implements the above
implementer by providing the concrete implementation.
Lets see an Example of Bridge Design Pattern :
Java

// Java code to demonstrate


// bridge design pattern

// abstraction in bridge pattern


abstract class Vehicle {
protected Workshop workShop1;
protected Workshop workShop2;

protected Vehicle(Workshop workShop1, Workshop workShop2)


{
this.workShop1 = workShop1;
this.workShop2 = workShop2;
}

abstract public void manufacture();


}

// Refine abstraction 1 in bridge pattern


class Car extends Vehicle {
public Car(Workshop workShop1, Workshop workShop2)
{
super(workShop1, workShop2);
}

@Override
public void manufacture()
{
System.out.print("Car ");
workShop1.work();
workShop2.work();
}
}

// Refine abstraction 2 in bridge pattern


class Bike extends Vehicle {
public Bike(Workshop workShop1, Workshop workShop2)
{
super(workShop1, workShop2);
}

@Override
public void manufacture()
{
System.out.print("Bike ");
workShop1.work();
workShop2.work();
}
}

// Implementer for bridge pattern


interface Workshop
{
abstract public void work();
}

// Concrete implementation 1 for bridge pattern


class Produce implements Workshop {
@Override
public void work()
{
System.out.print("Produced");
}
}

// Concrete implementation 2 for bridge pattern


class Assemble implements Workshop {
@Override
public void work()
{
System.out.print(" And");
System.out.println(" Assembled.");
}
}

// Demonstration of bridge design pattern


class BridgePattern {
public static void main(String[] args)
{
Vehicle vehicle1 = new Car(new Produce(), new Assemble());
vehicle1.manufacture();
Vehicle vehicle2 = new Bike(new Produce(), new Assemble());
vehicle2.manufacture();
}
}
Output :
Car Produced And Assembled.
Bike Produced And Assembled.

 Flyweight design Pattern


The Flyweight Design Pattern is a way to save memory in
applications that create a large number of similar objects.
Instead of creating a new object for each instance, the Flyweight
pattern reuses existing ones wherever possible, sharing common
parts between objects.
Here’s how it works:
 Shared vs. Unique Data: Objects are split into shared
(intrinsic) data and unique (extrinsic) data. The shared data is
stored in a central place and reused, while the unique data is
kept separately.
 Example: Imagine a text editor displaying thousands of
characters. Using Flyweight, the program can store each
unique character style (like font or color) only once and reuse
it, rather than duplicating it for every character.
Components of Flyweight Design Pattern
The Flyweight design pattern typically consists of the following
components:
Flyweight Design Pattern

1. Flyweight Interface/Class:
 Defines the interface through which flyweight objects can
receive and act on extrinsic state.
2. Concrete Flyweight Classes:
 Implements the Flyweight interface and represents objects
that can be shared.
 Stores intrinsic state (state that can be shared) and
provides methods to manipulate intrinsic state if needed.
3. Flyweight Factory:
 Manages a pool of flyweight objects.
 Provides methods for clients to retrieve or create flyweight
objects.
 Ensures flyweight objects are shared appropriately to
maximize reusability.
4. Client:
 Uses flyweight objects to perform operations.
 Maintains or passes extrinsic state to flyweight objects
when needed.
 Does not manage the lifecycle of flyweight objects directly
but interacts with them via the factory.
How to implement Flyweight Design Pattern?
To implement the Flyweight Design Pattern, follow these simple
steps:
 Step 1: Identify Shared and Unique Data: Arrange the
data in your objects first. Determine which information is
specific to each object (known as extrinsic data) and which
can be shared across objects (known as intrinsic data).
 Step 2: Create a Flyweight Class: This class will hold the
intrinsic (shared) data. All instances of this class represent
objects with similar data.
 Step 3: Build a Flyweight Factory: This factory class
manages instances of the Flyweight objects. When a new
object is needed, the factory checks if an object with the same
shared data already exists. If it does, it reuses that object; if
not, it creates a new one.
 Step 4: Pass Unique Data as Needed: The extrinsic data,
or data specific to that instance(extrinsic data), should be
passed as a parameter when using an object. In this manner,
the object can act in a unique way without storing all of the
data.
 Step 5: Use Flyweights Instead of Creating New
Objects: Now, instead of creating new objects directly, always
request them through the factory. The factory will manage all
shared instances and reuse them where possible.
Example Implementation of Flyweight Design Pattern
Below is the problem statement to understand flyweight design
pattern:
Imagine a graphical user interface (GUI) application where
multiple icons of different types (e.g., file icons, folder icons)
need to be displayed on a screen. Each icon type has a specific
appearance and behavior, such as different images and positions
on the screen. However, displaying numerous icons of the same
type can consume significant memory if each icon object stores
its unique properties independently.
How Flyweight Design Pattern will help to solve this problem?
 The Flyweight design pattern can optimize memory usage by
sharing common parts of the icons (intrinsic state), such as
the image and basic properties (like size and color), among
multiple icon instances.
 Each icon instance then stores only its unique properties
(extrinsic state), such as its position on the screen.
 This approach reduces the memory footprint and enhances
performance, especially when dealing with a large number of
similar objects.
Complete code of the flyweight design pattern:

import java.util.HashMap;
import java.util.Map;

// Flyweight interface
interface Icon {
void draw(int x, int y); // Method to draw the icon at given
coordinates
}

// Concrete Flyweight class representing a File Icon


class FileIcon implements Icon {
private String type; // Intrinsic state: type of file icon
private String imageName; // Intrinsic state: image name
specific to the file icon

public FileIcon(String type, String imageName) {


this.type = type;
this.imageName = imageName;
}

@Override
public void draw(int x, int y) {
// Simulated logic to load and draw image
System.out.println("Drawing " + type + " icon with image
" + imageName + " at position (" + x + ", " + y + ")");
}
}

// Concrete Flyweight class representing a Folder Icon


class FolderIcon implements Icon {
private String color; // Intrinsic state: color of the folder
icon
private String imageName; // Intrinsic state: image name
specific to the folder icon

public FolderIcon(String color, String imageName) {


this.color = color;
this.imageName = imageName;
}
@Override
public void draw(int x, int y) {
// Simulated logic to load and draw image
System.out.println("Drawing folder icon with color " +
color + " and image " + imageName + " at position (" + x + ",
" + y + ")");
}
}

// Flyweight factory to manage creation and retrieval of


flyweight objects
class IconFactory {
private Map<String, Icon> iconCache = new HashMap<>();

public Icon getIcon(String key) {


// Check if the icon already exists in the cache
if (iconCache.containsKey(key)) {
return iconCache.get(key);
} else {
// Create a new icon based on the key (type of icon)
Icon icon;
if (key.equals("file")) {
icon = new FileIcon("document", "document.png");
} else if (key.equals("folder")) {
icon = new FolderIcon("blue", "folder.png");
} else {
throw new IllegalArgumentException("Unsupported
icon type: " + key);
}
// Store the created icon in the cache
iconCache.put(key, icon);
return icon;
}
}
}

// Client code to use the flyweight objects (icons)


public class Client {
public static void main(String[] args) {
IconFactory iconFactory = new IconFactory();

// Draw file icons at different positions


Icon fileIcon1 = iconFactory.getIcon("file");
fileIcon1.draw(100, 100);

Icon fileIcon2 = iconFactory.getIcon("file");


fileIcon2.draw(150, 150);

// Draw folder icons at different positions


Icon folderIcon1 = iconFactory.getIcon("folder");
folderIcon1.draw(200, 200);

Icon folderIcon2 = iconFactory.getIcon("folder");


folderIcon2.draw(250, 250);
}
}
Output
Drawing document icon with image document.png at position (100, 100)
Drawing document icon with image document.png at position (150, 150)
Drawing folder icon with color blue and image folder.png at po...

 Iterator design Pattern

The Iterator design pattern is a behavioral design pattern that


provides a way to access the elements of an aggregate object
(like a list) sequentially without exposing its underlying
representation. It defines a separate object, called an iterator,
which encapsulates the details of traversing the elements of the
aggregate, allowing the aggregate to change its internal
structure without affecting the way its elements are accessed.
Implementing the Iterator pattern can greatly simplify data
access patterns. To further explore design patterns in system
design, consider the System Design Course that offers insights
into applying design patterns effectively in real-world
applications.
 Iterator Pattern is a relatively simple and frequently used
design pattern. There are a lot of data structures/collections
available in every language.
 Each collection must provide an iterator that lets it iterate
through its objects. However, while doing so it should make
sure that it does not expose its implementation.
Suppose we are building an application that requires us to
maintain a list of notifications. Eventually, some parts of your
code will require you to iterate over all notifications. If we
implemented your collection of notifications as an array you
would iterate over them as:
Java

// If a simple array is used to store notifications


for (int i = 0; i < notificationList.length; i++)
Notification notification = notificationList[i]);

And if it were some other collection like set, tree, etc. way of
iterating would change slightly. Now, what if we build an iterator
that provides a generic way of iterating over a collection
independent of its type.
Java

// Create an iterator
Iterator iterator = notificationList.createIterator();
// It wouldn’t matter if list is Array or ArrayList or
// anything else.
while (iterator.hasNext())
{
Notification notification = iterator.next());
}
Components of Iterator Design Pattern
The Iterator design pattern consists of several components:
1. Iterator Interface/Abstract Class
Defines the interface for accessing and traversing elements in
the collection. It typically includes methods like hasNext(), next(),
and optionally remove().
2. Concrete Iterator
Implements the Iterator interface and maintains the current
position in the traversal of the aggregate. It provides the actual
implementation for the traversal operations defined in the
Iterator interface.
3. Aggregate Interface/Abstract Class
Defines the interface for creating an Iterator object. It typically
includes a method like createIterator() that returns an Iterator
object for the collection.
4. Concrete Aggregate
Implements the Aggregate interface and represents the
collection of objects. It provides the implementation for creating
an Iterator object that can traverse its elements.
Iterator Design Pattern example
Problem Statement:
Let’s say we have a collection of employees in a company, and
we want to iterate over the employees to calculate their total
salary. However, the employees are stored in different types of
collections (arrays, lists, etc.), and we want to iterate over them
without exposing the underlying collection types.
import java.util.*;

// Employee class
class Employee {
private String name;
private double salary;

public Employee(String name, double salary) {


this.name = name;
this.salary = salary;
}

public double getSalary() {


return salary;
}
}

// Iterator interface
interface Iterator<T> {
boolean hasNext();
T next();
}

// Aggregate interface
interface Aggregate<T> {
Iterator<T> createIterator();
}

// Concrete Iterator
class EmployeeIterator implements Iterator<Employee> {
private int currentIndex = 0;
private List<Employee> employees;

public EmployeeIterator(List<Employee> employees) {


this.employees = employees;
}

@Override
public boolean hasNext() {
return currentIndex < employees.size();
}

@Override
public Employee next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
return employees.get(currentIndex++);
}
}

// Concrete Aggregate
class Company implements Aggregate<Employee> {
private List<Employee> employees;

public Company(List<Employee> employees) {


this.employees = employees;
}

@Override
public Iterator<Employee> createIterator() {
return new EmployeeIterator(employees);
}
}

// Main class
public class Main {
public static void main(String[] args) {
List<Employee> employees = new ArrayList<>();
employees.add(new Employee("Alice", 50000));
employees.add(new Employee("Bob", 60000));
employees.add(new Employee("Charlie", 70000));

Company company = new Company(employees);


Iterator<Employee> iterator = company.createIterator();

double totalSalary = 0;
while (iterator.hasNext()) {
totalSalary += iterator.next().getSalary();
}

System.out.println("Total salary: " + totalSalary);


}
}
Output

Total salary: 180000.0

 Observer design Pattern

The Observer Design Pattern is a behavioral design pattern that


defines a one-to-many dependency between objects. When one
object (the subject) changes state, all its dependents (observers)
are notified and updated automatically. It primarily deals with the
interaction and communication between objects, specifically
focusing on how objects behave in response to changes in the
state of other objects.
Note: Subjects are the objects that maintain and notify
observers about changes in their state, while Observers are the
entities that react to those changes.
Below are some key points about observer design pattern:
 Defines how a group of objects (observers) interact based on
changes in the state of a subject.
 Observers react to changes in the subject’s state.
 The subject doesn’t need to know the specific classes of its
observers, allowing for flexibility.
 Observers can be easily added or removed without affecting
the subject.
To master the Observer Design Pattern, the System Design
Course offers comprehensive lessons on this and other
behavioral patterns to help you implement them effectively.
Real-world analogy of the Observer Design Pattern
Let’s understand the observer design pattern through a real-
world example:
magine a scenario where a weather station is observed by
various smart devices. The weather station maintains a list of
registered devices. Weather Station will update all the devices
whenver there is change in the weather.
 Each of the devices are concrete observers and each have
their ways to interpret and display the information.
 The Observer Design Pattern provides a flexible and scalable
system where adding new devices or weather stations doesn’t
disrupt the overall communication, providing real-time and
location-specific weather updates to users.
Components of Observer Design Pattern
Below are the main components of Observer Design Pattern:

 Subject:
o The subject maintains a list of observers (subscribers
or listeners).
o It Provides methods to register and unregister
observers dynamically and defines a method to notify
observers of changes in its state.
 Observer:
o Observer defines an interface with an update method
that concrete observers must implement and ensures
a common or consistent way for concrete observers to
receive updates from the subject.
 ConcreteSubject:
o ConcreteSubjects are specific implementations of the
subject. They hold the actual state or data that
observers want to track. When this state changes,
concrete subjects notify their observers.
o For instance, if a weather station is the subject,
specific weather stations in different locations would
be concrete subjects.
 ConcreteObserver:
o Concrete Observer implements the observer interface.
They register with a concrete subject and react when
notified of a state change.
o When the subject’s state changes, the concrete
observer’s update() method is invoked, allowing it to
take appropriate actions.
o For example, a weather app on your smartphone is a
concrete observer that reacts to changes from a
weather station.
Observer Design Pattern Example
To understand observer design pattern, lets take an example:
Consider a scenario where you have a weather monitoring
system. Different parts of your application need to be updated
when the weather conditions change.
Challenges or difficulties while implementing this system
without Observer Design Pattern
 Components interested in weather updates would need direct
references to the weather monitoring system, leading to tight
coupling.
 Adding or removing components that react to weather
changes requires modifying the core weather monitoring
system code, making it hard to maintain.
How Observer Pattern helps to solve above challenges?
The Observer Pattern facilitates the decoupling of the weather
monitoring system from the components that are interested in
weather updates (via interfaces). Every element can sign up as
an observer, and observers are informed when the weather
conditions change. The weather monitoring system is thus
unaffected by the addition or removal of components.
import java.util.ArrayList;
import java.util.List;

// Observer Interface
interface Observer {
void update(String weather);
}

// Subject Interface
interface Subject {
void addObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers();
}

// ConcreteSubject Class
class WeatherStation implements Subject {
private List<Observer> observers = new ArrayList<>();
private String weather;

@Override
public void addObserver(Observer observer) {
observers.add(observer);
}

@Override
public void removeObserver(Observer observer) {
observers.remove(observer);
}

@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(weather);
}
}

public void setWeather(String newWeather) {


this.weather = newWeather;
notifyObservers();
}
}

// ConcreteObserver Class
class PhoneDisplay implements Observer {
private String weather;
@Override
public void update(String weather) {
this.weather = weather;
display();
}

private void display() {


System.out.println("Phone Display: Weather updated - " +
weather);
}
}

// ConcreteObserver Class
class TVDisplay implements Observer {
private String weather;

@Override
public void update(String weather) {
this.weather = weather;
display();
}

private void display() {


System.out.println("TV Display: Weather updated - " +
weather);
}
}
// Usage Class
public class WeatherApp {
public static void main(String[] args) {
WeatherStation weatherStation = new WeatherStation();

Observer phoneDisplay = new PhoneDisplay();


Observer tvDisplay = new TVDisplay();

weatherStation.addObserver(phoneDisplay);
weatherStation.addObserver(tvDisplay);

// Simulating weather change


weatherStation.setWeather("Sunny");

// Output:
// Phone Display: Weather updated - Sunny
// TV Display: Weather updated - Sunny
}
}
Output
Phone Display: Weather updated - Sunny
TV Display: Weather updated - Sunny

 MVC(Model-view-controller) design Pattern

The Model View Controller (MVC) design pattern specifies that an


application consists of a data model, presentation information,
and control information. The pattern requires that each of these
be separated into different objects.
 The MVC pattern separates the concerns of an application into
three distinct components, each responsible for a specific
aspect of the application’s functionality.
 This separation of concerns makes the application easier to
maintain and extend, as changes to one component do not
require changes to the other components.

1. Model
The Model component in the MVC (Model-View-Controller)
design pattern demonstrates the data and business logic of
an application. It is responsible for managing the
application’s data, processing business rules, and
responding to requests for information from other
components, such as the View and the Controller.
2. View
Displays the data from the Model to the user and sends
user inputs to the Controller. It is passive and does not
directly interact with the Model. Instead, it receives data
from the Model and sends user inputs to the Controller for
processing.
3. Controller
Controller acts as an intermediary between the Model and
the View. It handles user input and updates the Model
accordingly and updates the View to reflect changes in the
Model. It contains application logic, such as input validation
and data transformation.
Communication between the Components
This below communication flow ensures that each component is
responsible for a specific aspect of the application’s functionality,
leading to a more maintainable and scalable architecture
 User Interaction with View: The user interacts with the
View, such as clicking a button or entering text into a form.
 View Receives User Input: The View receives the user input
and forwards it to the Controller.
 Controller Processes User Input: The Controller receives
the user input from the View. It interprets the input, performs
any necessary operations (such as updating the Model), and
decides how to respond.
 Controller Updates Model: The Controller updates the
Model based on the user input or application logic.
 Model Notifies View of Changes: If the Model changes, it
notifies the View.
 View Requests Data from Model: The View requests data
from the Model to update its display.
 Controller Updates View: The Controller updates the View
based on the changes in the Model or in response to user
input.
 View Renders Updated UI: The View renders the updated UI
based on the changes made by the Controller.
Example of the MVC Design Pattern
Below is the code of above problem statement using MVC
Design Pattern:
Let’s break down into the component wise code:

Below is the complete code for the above example:


class Student {
private String rollNo;
private String name;
public String getRollNo() {
return rollNo;
}

public void setRollNo(String rollNo) {


this.rollNo = rollNo;
}

public String getName() {


return name;
}

public void setName(String name) {


this.name = name;
}
}

class StudentView {
public void printStudentDetails(String studentName, String studentRollNo) {
System.out.println("Student:");
System.out.println("Name: " + studentName);
System.out.println("Roll No: " + studentRollNo);
}
}

class StudentController {
private Student model;
private StudentView view;

public StudentController(Student model, StudentView view) {


this.model = model;
this.view = view;
}

public void setStudentName(String name) {


model.setName(name);
}

public String getStudentName() {


return model.getName();
}

public void setStudentRollNo(String rollNo) {


model.setRollNo(rollNo);
}

public String getStudentRollNo() {


return model.getRollNo();
}

public void updateView() {


view.printStudentDetails(model.getName(), model.getRollNo());
}
}

public class MVCPattern {


public static void main(String[] args) {
Student model = retriveStudentFromDatabase();

StudentView view = new StudentView();

StudentController controller = new StudentController(model, view);

controller.updateView();
controller.setStudentName("Vikram Sharma");

controller.updateView();
}

private static Student retriveStudentFromDatabase() {


Student student = new Student();
student.setName("Lokesh Sharma");
student.setRollNo("15UCS157");
return student;
}
}
Output

Student:
Name: Lokesh Sharma
Roll No: 15UCS157
Student:
Name: Vikram Sharma
Roll No: 15UCS157

When to Use the MVC Design Pattern


Below is when to use MVC Design Pattern:
 Complex Applications: Use MVC for apps with many
features and user interactions, like e-commerce sites. It helps
organize code and manage complexity.
 Frequent UI Changes: If the UI needs regular updates, MVC
allows changes to the View without affecting the underlying
logic.
 Reusability of Components: If you want to reuse parts of
your app in other projects, MVC’s modular structure makes
this easier.
 Testing Requirements: MVC supports thorough testing,
allowing you to test each component separately for better
quality control.

You might also like