Java Design Patterns: Observer, Factory, and Singleton
Understanding Java Design Patterns
Design patterns are proven solutions to common design problems in software development. They provide a template for writing code that is modular, reusable, and easy to maintain. In this article, we’ll dive into three essential Java design patterns: Observer, Factory, and Singleton. We’ll explore their purposes, implementation, and practical examples to illustrate their use in real-world scenarios.
1. Observer Pattern
The Observer pattern is used to define a one-to-many dependency between objects, so that when one object changes state, all its dependents are notified and updated automatically. This pattern is ideal for implementing distributed event-handling systems.
Example: Implementing the Observer Pattern
Here’s a simple example demonstrating the Observer pattern with a `Subject` and `Observer` interface.
```java
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<>();
private String state;
public void addObserver(Observer observer) {
observers.add(observer);
}
public void removeObserver(Observer observer) {
observers.remove(observer);
}
public void setState(String state) {
this.state = state;
notifyObservers();
}
private void notifyObservers() {
for (Observer observer : observers) {
observer.update(state);
}
}
}
// Concrete Observer
class ConcreteObserver implements Observer {
private String name;
public ConcreteObserver(String name) {
this.name = name;
}
@Override
public void update(String message) {
System.out.println(name + " received update: " + message);
}
}
// Main class
public class ObserverPatternExample {
public static void main(String[] args) {
Subject subject = new Subject();
Observer observer1 = new ConcreteObserver("Observer1");
Observer observer2 = new ConcreteObserver("Observer2");
subject.addObserver(observer1);
subject.addObserver(observer2);
subject.setState("State1");
subject.setState("State2");
}
}
```
2. Factory Pattern
The Factory pattern provides a way to delegate the instantiation of objects to subclasses, allowing for the creation of objects without specifying the exact class of the object that will be created. This pattern is useful for managing and maintaining a large number of related classes.
Example: Implementing the Factory Pattern
Here’s an example of a simple factory method for creating different types of `Shape` objects.
```java
// Product interface
interface Shape {
void draw();
}
// Concrete Products
class Circle implements Shape {
@Override
public void draw() {
System.out.println("Drawing a Circle");
}
}
class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Drawing a Rectangle");
}
}
// Factory class
class ShapeFactory {
public static 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;
}
}
// Main class
public class FactoryPatternExample {
public static void main(String[] args) {
Shape circle = ShapeFactory.getShape("CIRCLE");
circle.draw();
Shape rectangle = ShapeFactory.getShape("RECTANGLE");
rectangle.draw();
}
}
```
3. Singleton Pattern
The Singleton pattern ensures that a class has only one instance and provides a global point of access to that instance. This pattern is useful for managing resources that should have a single, centralized point of access.
Example: Implementing the Singleton Pattern
Here’s how you can implement a thread-safe Singleton class in Java.
```java
// Singleton class
class Singleton {
private static Singleton instance;
private Singleton() {
// private constructor to prevent instantiation
}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
public void showMessage() {
System.out.println("Hello from Singleton!");
}
}
// Main class
public class SingletonPatternExample {
public static void main(String[] args) {
Singleton singleton = Singleton.getInstance();
singleton.showMessage();
}
}
```
Conclusion
Understanding and applying design patterns like Observer, Factory, and Singleton can significantly improve the design and flexibility of your Java applications. These patterns provide robust solutions to common design problems, making your code more modular, reusable, and easier to maintain. By leveraging these patterns, you can enhance the scalability and manageability of your software projects.
Further Reading:
Table of Contents


