Design Patterns in Software Development
Design patterns are reusable solutions to common problems in software design.
They provide best practices and templates for structuring code efficiently.
Why should I learn patterns?
The truth is that you might manage to work as a programmer for many years
without knowing about a single pattern. A lot of people do just that. Even in that
case, though, you might be implementing some patterns without even knowing it.
So why would you spend time learning them?
Design patterns are a toolkit of tried and tested solutions to common
problems in software design. Even if you never encounter these problems,
knowing patterns is still useful because it teaches you how to solve all sorts
of problems using principles of object-oriented design.
Design patterns define a common language that you and your teammates
can use to communicate more efficiently. You can say, “Oh, just use a
Singleton for that,” and everyone will understand the idea behind your
suggestion. No need to explain what a singleton is if you know the pattern
and its name.
What does the pattern consist of?
Most patterns are described very formally so people can reproduce them in many
contexts. Here are the sections that are usually present in a pattern description:
Intent of the pattern briefly describes both the problem and the solution.
Motivation further explains the problem and the solution the pattern
makes possible.
Structure of classes shows each part of the pattern and how they are
related.
Code example in one of the popular programming languages makes it
easier to grasp the idea behind the pattern.
Classification of patterns
Design patterns differ by their complexity, level of detail and scale of applicability to the
entire system being designed. I like the analogy to road construction: you can make an
intersection safer by either installing some traffic lights or building an entire multi-level
interchange with underground passages for pedestrians.
The most basic and low-level patterns are often called idioms. They usually apply only to
a single programming language.
The most universal and high-level patterns are architectural patterns. Developers can
implement these patterns in virtually any language. Unlike other patterns, they can be
used to design the architecture of an entire application.
In addition, all patterns can be categorized by their intent, or purpose. This book covers
three main groups of patterns:
Creational patterns provide object creation mechanisms that increase
flexibility and reuse of existing code.
Structural patterns explain how to assemble objects and classes into larger
structures, while keeping these structures flexible and efficient.
Behavioral patterns take care of effective communication and the assignment
of responsibilities between objects.
Types of Design Patterns
1. Creational Patterns (Deal with object creation)
o Singleton – Ensures a class has only one instance.
o Factory Method – Creates objects without specifying the exact
class.
o Abstract Factory – Creates families of related objects.
o Builder – Separates object construction from representation.
o Prototype – Creates objects by copying an existing object.
2. Structural Patterns (Deal with object composition)
o Adapter – Bridges two incompatible interfaces.
o Decorator – Adds new functionality to objects dynamically.
o Facade – Simplifies complex subsystems with a single interface.
o Composite – Treats a group of objects as a single object.
o Proxy – Controls access to an object.
o Bridge – Separates abstraction from implementation.
o Flyweight – Minimizes memory usage by sharing objects.
3. Behavioral Patterns (Deal with object interaction)
o Observer – Defines a dependency between objects (e.g., event
listeners).
o Strategy – Selects algorithms at runtime.
o Command – Encapsulates requests as objects.
o State – Changes behavior based on state.
o Chain of Responsibility – Passes requests along a chain of
handlers.
o Mediator – Reduces dependencies between communicating objects.
o Memento – Captures and restores an object's state.
o Interpreter – Defines a grammar and interpreter for a language.
o Template Method – Defines a skeleton for an algorithm with steps
left for subclasses.
o Visitor – Adds operations to object structures without modifying
them.
Simple Explanation of the Factory Pattern – Vehicle Example 🚗🏍️
Imagine you go to a vehicle showroom. You tell the salesperson, "I need a Car" or "I need a
Bike."
You don’t go inside the factory and build the vehicle yourself!
Here, the salesperson acts as the Factory – they listen to your request and give you the right
vehicle.
You just ask for what you need, and you get it without worrying about how it was made.
How This Works in Code
1. Vehicle (Interface) – A common type for all vehicles.
2. Car & Bike (Concrete Classes) – Different vehicle types.
3. Factory (VehicleFactory) – The "salesperson" that gives you the right vehicle.
4. Client (Main Method) – The customer who asks for a vehicle.
Code Example
java
CopyEdit
// 1. Common Interface for Vehicles
interface Vehicle {
void drive();
}
// 2. Specific Vehicle Types
class Car implements Vehicle {
public void drive() {
[Link]("Driving a Car...");
}
}
class Bike implements Vehicle {
public void drive() {
[Link]("Riding a Bike...");
}
}
// 3. Factory Class (Handles Object Creation)
class VehicleFactory {
public static Vehicle createVehicle(String type) {
if ([Link]("Car")) {
return new Car(); // Returns a Car object
} else if ([Link]("Bike")) {
return new Bike(); // Returns a Bike object
}
return null; // If invalid type, return null
}
}
// 4. Client Code (Requests Vehicles)
public class FactoryPatternExample {
public static void main(String[] args) {
// Customer asks for a Car
Vehicle vehicle1 = [Link]("Car");
[Link](); // Output: Driving a Car...
// Customer asks for a Bike
Vehicle vehicle2 = [Link]("Bike");
[Link](); // Output: Riding a Bike...
}
}
Easy Summary
You (Client) → Want a Car or Bike.
Salesperson (Factory) → Gives you the right vehicle.
Factory Pattern → Helps you get the right object without knowing how it was made.
Now, if you want to add a new vehicle type (like a Truck), just modify the Factory – no
changes needed in the client code! 🚀
Singleton:
class PrinterSpooler { This declares the PrinterSpooler class,
which will implement the Singleton Pattern.
Singleton Pattern ensures that only
one instance of this class exists at any time.
static variable that will hold the
single instance of PrinterSpooler.
private static PrinterSpooler instance;
, it is null, meaning no instance
exists yet.
// Private constructor to prevent The constructor is private to prevent
instantiation creating objects using new
PrinterSpooler().
private PrinterSpooler() { This ensures that the only way to get
an instance is through the
[Link]("Printer Spooler getInstance() method.
Initialized"); Prints a message when the spooler is
first created.
}
global access to the
single instance of PrinterSpooler.
Lazy Initialization: The instance is only
// Public method to get the single instance created when needed (i.e., the first time
getInstance() is called).
public static PrinterSpooler getInstance() { How it works:
if (instance == null) { 1. First Call: instance is null, so a new
instance is created.
instance = new PrinterSpooler(); 2. Subsequent Calls: The existing
instance is returned, preventing
} multiple object creation.
return instance;
} simulates printing a
document.
// Method to add a print job
Accepts a String (document) as input and
public void print(String document) { prints the document name
[Link]("Printing: " +
document);
}
Main Method (Usage of Singleton)
}
This is the main class where the
PrinterSpooler singleton will be tested.
// Usage:
public class PrinterSpoolerExample {
references: spooler1 and spooler2.
public static void main(String[] args) { Both references point to the same object
because PrinterSpooler is a Singleton
PrinterSpooler spooler1 =
[Link]();
PrinterSpooler spooler2 = use the same instance to print documents.
[Link](); all print requests are
handled by the same printer spooler.
[Link]("Annual [Link]");
[Link]("[Link]");
Compares spooler1 and spooler2
using ==.
[Link](spooler1 == Since both variables point to the
spooler2); // Output: true (same instance) same instance, this prints true,
confirming the Singleton Pattern is
} working correctly.
} Full Output of the Program
Printer Spooler Initialized
Printing: Annual [Link]
Printing: [Link]
true
Step-by-Step Execution:
1. First call to getInstance():
o No instance exists, so it creates a PrinterSpooler.
o Output: "Printer Spooler Initialized"
2. Second call to getInstance():
o The existing same instance is returned.
3. Printing jobs using both spooler1 and spooler2:
o Both use the same printer spooler, so the jobs are managed correctly.
4. Comparison (spooler1 == spooler2):
o Output is true, proving that only one Printer Spooler exists.
FACTORY PATTERN:
The image represents the Factory Method design pattern, which is used to
create objects in a structured and flexible way. Let me explain it with a
simple real-world example.
Example: Pizza Factory
Imagine you have a pizza restaurant that makes different types of pizzas. Instead of
creating each pizza manually, you can define a Pizza Factory that produces pizzas
based on the type requested.
// Product interface
interface Pizza {
void prepare();
}
// Concrete Products
class MargheritaPizza implements Pizza {
public void prepare() {
[Link]("Preparing Margherita Pizza");
class PepperoniPizza implements Pizza {
public void prepare() {
[Link]("Preparing Pepperoni Pizza");
// Creator
abstract class PizzaFactory {
abstract Pizza createPizza(); // Factory Method
public void orderPizza() {
Pizza pizza = createPizza();
[Link]();
}
// Concrete Creators
class MargheritaPizzaFactory extends PizzaFactory {
Pizza createPizza() {
return new MargheritaPizza();
class PepperoniPizzaFactory extends PizzaFactory {
Pizza createPizza() {
return new PepperoniPizza();
// Client
public class FactoryMethodExample {
public static void main(String[] args) {
PizzaFactory factory1 = new MargheritaPizzaFactory();
[Link](); // Output: Preparing Margherita Pizza
PizzaFactory factory2 = new PepperoniPizzaFactory();
[Link](); // Output: Preparing Pepperoni Pizza
}
Step 1: Define an Interface (Product) :
In simple terms, an interface in Java is like a contract that defines a set of methods that a class
must implement. It does not contain any code for the methods, just their names and parameters.
// Product interface
interface Pizza {
void prepare();
Step 2: Create Concrete Products:
// Concrete Products
class MargheritaPizza implements Pizza {
public void prepare() {
[Link]("Preparing Margherita Pizza");
class PepperoniPizza implements Pizza {
public void prepare() {
[Link]("Preparing Pepperoni Pizza");
In the Factory Method Pattern, Concrete Products are the actual classes that implement the
Product interface. They provide the specific implementation of the methods defined in the
interface.
Step 3: Create an Abstract Creator:
An Abstract Creator is a class that declares the Factory Method but does not implement it.
Instead, it defines the structure that its subclasses must follow.
abstract class PizzaFactory {
abstract Pizza createPizza(); // Factory Method
public void orderPizza() {
Pizza pizza = createPizza(); // Calls the Factory Method
[Link]();
It declares the method createPizza(), but does not implement it.
It defines the method orderPizza(), which uses createPizza() to get a Pizza and call
prepare().
Step 4: Implement Concrete Factories:
In the Factory Method Pattern, a Concrete Factory is a class that extends the Abstract
Creator and implements the Factory Method to create specific objects.
class MargheritaPizzaFactory extends PizzaFactory {
Pizza createPizza() {
return new MargheritaPizza();
class PepperoniPizzaFactory extends PizzaFactory {
Pizza createPizza() {
return new PepperoniPizza();
}
This is a Concrete Factory that extends PizzaFactory.
It implements createPizza() to return a MargheritaPizza.
Another Concrete Factory that creates PepperoniPizza.
Step 5: Use the Factory
"Use the Factory" means calling the Concrete Factory to create objects without directly using
the new keyword. Instead of creating objects manually, we let the Factory Method handle the
creation.
public class FactoryMethodExample {
public static void main(String[] args) {
// Step 1: Create a Margherita Pizza Factory
PizzaFactory factory1 = new MargheritaPizzaFactory();
[Link](); // Uses factory to create and prepare pizza
// Step 2: Create a Pepperoni Pizza Factory
PizzaFactory factory2 = new PepperoniPizzaFactory();
[Link](); // Uses factory to create and prepare pizza
1. We create a factory (MargheritaPizzaFactory)
2. We call orderPizza(): Inside orderPizza(), the factory calls createPizza(),
which returns a MargheritaPizza object.
Then prepare() is called, printing: "Preparing Margherita Pizza".
3. We repeat the process for PepperoniPizzaFactory
FACTORY PATTERN:
Implementation Steps
1. Abstract Product: Defines the structure for Beverage.
2. Concrete Products: Coffee and Tea implement the Beverage interface.
3. Abstract Factory: BeverageFactory declares methods to create beverages.
4. Concrete Factories: CoffeeFactory and TeaFactory implement
BeverageFactory.
5. Client: Uses the factory without knowing the concrete class.
Below is an example of implementing a simple Factory Pattern for a vending
machine that supplies coffee and tea. In this example, we have a common interface
for beverages, concrete classes for coffee and tea, and a factory that creates the
appropriate beverage based on user input.
Step 1: Abstract Product (Beverage)
// Abstract Product
public interface Beverage {
String prepare();
}
Step 2: Concrete Products (Coffee & Tea)
// Concrete Product - Coffee
public class Coffee implements Beverage {
@Override
public String prepare() {
return "Preparing Coffee ☕";
}
}
// Concrete Product - Tea
public class Tea implements Beverage {
@Override
public String prepare() {
return "Preparing Tea 🍵";
}
}
Step 3: Abstract Factory (BeverageFactory)
// Abstract Factory
public interface BeverageFactory {
Beverage createBeverage();
}
Step 4: Concrete Factories (CoffeeFactory & TeaFactory)
// Concrete Factory - CoffeeFactory
public class CoffeeFactory implements BeverageFactory {
@Override
public Beverage createBeverage() {
return new Coffee();
}
}
// Concrete Factory - TeaFactory
public class TeaFactory implements BeverageFactory {
@Override
public Beverage createBeverage() {
return new Tea();
}
}
Step 5: Client Code (Main Class)
// Client Code
public class VendingMachine {
public static void main(String[] args) {
// Creating coffee using CoffeeFactory
BeverageFactory coffeeFactory = new CoffeeFactory();
Beverage coffee = [Link]();
[Link]([Link]());
// Creating tea using TeaFactory
BeverageFactory teaFactory = new TeaFactory();
Beverage tea = [Link]();
[Link]([Link]());
}
}
Explanation
1. Beverage (Abstract Product) - Defines a common method prepare().
2. Coffee & Tea (Concrete Products) - Implement the Beverage interface.
3. BeverageFactory (Abstract Factory) - Defines a method
createBeverage().
4. CoffeeFactory & TeaFactory (Concrete Factories) - Implement
BeverageFactory to create Coffee or Tea.
5. VendingMachine (Client) - Uses the factory pattern without knowing
the exact beverage being created.
Summary of Program Flow
1. The VendingMachine program starts executing.
2. A CoffeeFactory is created, which returns a Coffee object.
3. The Coffee object calls prepare(), printing "Preparing Coffee ☕".
4. A TeaFactory is created, which returns a Tea object.
5. The Tea object calls prepare(), printing "Preparing Tea 🍵".
6. The program ends.