Design patterns are reusable solutions to common problems in software design. They provide a template for writing clean, maintainable, and scalable code. In this tutorial, we'll explore some of the most commonly used design patterns in Java, including Singleton, Factory, Observer, and more.
Java Design Patterns: Singleton, Factory, Observer, and More
By the end of this tutorial, you'll understand how to implement and use these design patterns effectively in your Java applications.
Singleton Pattern
The Singleton pattern ensures that a class has only one instance and provides a global point of access to it.
- Key Features:
- Private constructor to prevent instantiation.
- Static method to provide access to the single instance.
- Example:
public class Singleton { private static Singleton instance; private Singleton() { // Private constructor } public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } } public class Main { public static void main(String[] args) { Singleton singleton = Singleton.getInstance(); System.out.println("Singleton instance created."); } }
Factory Pattern
The Factory pattern provides an interface for creating objects in a superclass but allows subclasses to alter the type of objects that will be created.
- Key Features:
- Encapsulates object creation logic.
- Promotes loose coupling by separating object creation from usage.
- Example:
interface Shape { void draw(); } class Circle implements Shape { public void draw() { System.out.println("Drawing a circle"); } } class Rectangle implements Shape { public void draw() { System.out.println("Drawing a rectangle"); } } class ShapeFactory { public Shape getShape(String shapeType) { if (shapeType == null) { return null; } if (shapeType.equalsIgnoreCase("CIRCLE")) { return new Circle(); } else if (shapeType.equalsIgnoreCase("RECTANGLE")) { return new Rectangle(); } return null; } } public class Main { public static void main(String[] args) { ShapeFactory factory = new ShapeFactory(); Shape circle = factory.getShape("CIRCLE"); circle.draw(); // Output: Drawing a circle } }
Observer Pattern
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.
- Key Features:
- Promotes loose coupling between objects.
- Used in event handling systems.
- Example:
import java.util.ArrayList; import java.util.List; interface Observer { void update(String message); } class ConcreteObserver implements Observer { private String name; public ConcreteObserver(String name) { this.name = name; } public void update(String message) { System.out.println(name + " received message: " + message); } } class Subject { private List<Observer> observers = new ArrayList<>(); public void addObserver(Observer observer) { observers.add(observer); } public void notifyObservers(String message) { for (Observer observer : observers) { observer.update(message); } } } public class Main { public static void main(String[] args) { Subject subject = new Subject(); Observer observer1 = new ConcreteObserver("Observer 1"); Observer observer2 = new ConcreteObserver("Observer 2"); subject.addObserver(observer1); subject.addObserver(observer2); subject.notifyObservers("Hello, Observers!"); // Output: // Observer 1 received message: Hello, Observers! // Observer 2 received message: Hello, Observers! } }
Other Common Design Patterns
Here are a few more design patterns commonly used in Java:
- Builder Pattern:
- Used to construct complex objects step by step.
- Example: Building a
Pizza
object with optional toppings.
- Strategy Pattern:
- Defines a family of algorithms, encapsulates each one, and makes them interchangeable.
- Example: Sorting algorithms like
BubbleSort
andQuickSort
.
- Decorator Pattern:
- Adds behavior to objects dynamically without affecting other objects.
- Example: Adding toppings to a
Pizza
object.
This tutorial covered some of the most commonly used design patterns in Java. Practice implementing these patterns to write clean, maintainable, and scalable code.