Welcome to this 3-part guide. In this post, I'll provide a basic introduction to design patterns, along with examples.
This specific post will cover what design patterns are, why we should know about them, the types of design patterns, and creational patterns.
Quick links:
What are Design Patterns?
Design patterns are proven solutions to common problems in software development. They help developers solve complex tasks in a structured way and allow them to reuse successful solutions, making their code easier to understand and maintain. Think of design patterns as blueprints for solving typical problems that occur when building software.
Why Should We Know About Them?
Knowing about design patterns helps developers solve common problems in a structured and efficient way, making code more reusable, maintainable, and scalable.
Think of a restaurant kitchen. The chef follows a recipe (design pattern) to prepare a dish. Instead of reinventing the process each time, the chef uses proven steps to consistently deliver the same quality dish. Similarly, design patterns help developers efficiently solve problems without reinventing solutions each time.
Types of Design Patterns:
In Java, design patterns are categorized into three main groups:
1. Creational Patterns: These patterns help with creating objects in a flexible and reusable way.
2. Structural Patterns: These patterns focus on how objects are arranged and connected to form larger systems.
3. Behavioral Patterns: These patterns deal with how objects interact and communicate with each other.
Creational Patterns
Creational patterns focus on object creation and make it easier to create objects in a flexible, reusable way. These patterns help ensure that the object creation process is independent of the system using it.
Abstract Factory:
Imagine a game where you can choose between different "themes" (e.g., medieval, sci-fi). The abstract factory can be used to create related sets of objects (like characters, weapons, and settings) specific to the selected theme without changing the game’s core logic.
interface GameFactory {
Character createCharacter();
Weapon createWeapon();
}
class MedievalFactory implements GameFactory {
public Character createCharacter() {
return new Knight();
}
public Weapon createWeapon() {
return new Sword();
}
}
class SciFiFactory implements GameFactory {
public Character createCharacter() {
return new Robot();
}
public Weapon createWeapon() {
return new LaserGun();
}
}
Builder:
Think of building a complex sandwich where you can customize the ingredients (e.g., type of bread, meat, veggies). The builder pattern helps in assembling these ingredients step-by-step.
class Sandwich {
private String bread;
private String meat;
private String cheese;
public void setBread(String bread) { this.bread = bread; }
public void setMeat(String meat) { this.meat = meat; }
public void setCheese(String cheese) { this.cheese = cheese; }
}
class SandwichBuilder {
private Sandwich sandwich = new Sandwich();
public SandwichBuilder addBread(String bread) {
sandwich.setBread(bread);
return this;
}
public SandwichBuilder addMeat(String meat) {
sandwich.setMeat(meat);
return this;
}
public SandwichBuilder addCheese(String cheese) {
sandwich.setCheese(cheese);
return this;
}
public Sandwich build() {
return sandwich;
}
}
Singleton:
You only need one instance of a connection to a database throughout the application. The singleton pattern ensures that only a single instance of the database connection is created.
class DatabaseConnection {
private static DatabaseConnection instance;
private DatabaseConnection() {} // Private constructor
public static DatabaseConnection getInstance() {
if (instance == null) {
instance = new DatabaseConnection();
}
return instance;
}
}
Prototype:
Imagine duplicating complex objects like a “high-level” document template that contains various elements. Instead of creating it from scratch, you can copy a prototype of the document and modify it.
abstract class DocumentPrototype {
abstract DocumentPrototype clone();
}
class Document extends DocumentPrototype {
private String content;
public Document(String content) {
this.content = content;
}
public DocumentPrototype clone() {
return new Document(this.content);
}
}
Object Pool:
Suppose you have a limited number of printer objects in a printing system. Instead of creating new printer objects each time, an object pool helps manage and reuse printers efficiently.
class Printer {
public void print() {
System.out.println("Printing document...");
}
}
class ObjectPool {
private List<Printer> availablePrinters = new ArrayList<>();
public Printer getPrinter() {
if (availablePrinters.isEmpty()) {
return new Printer();
}
return availablePrinters.remove(0);
}
public void releasePrinter(Printer printer) {
availablePrinters.add(printer);
}
}
Factory Method:
You are building a system that needs different types of vehicles. The factory method pattern allows you to decide which vehicle to create based on certain conditions.
interface Vehicle {
void drive();
}
class Car implements Vehicle {
public void drive() {
System.out.println("Driving a car");
}
}
class Bike implements Vehicle {
public void drive() {
System.out.println("Riding a bike");
}
}
abstract class VehicleFactory {
abstract Vehicle createVehicle();
}
class CarFactory extends VehicleFactory {
public Vehicle createVehicle() {
return new Car();
}
}
class BikeFactory extends VehicleFactory {
public Vehicle createVehicle() {
return new Bike();
}
}
Top comments (0)