Program 12 : Design Patterns
Implement a simple Java program to demonstrate common design patterns such as Singleton, Factory, Observer, and Strategy.
A) Singleton Design Pattern
The Singleton pattern ensures that a class has only one instance and provides a global point of access to it. The getInstance()
method in this example is made synchronized
to ensure thread safety.
Printer.java
// Singleton class
public class Printer {
// The single instance of the class
private static Printer instance;
// Private constructor to prevent instantiation from outside
private Printer() {
System.out.println("Printer is ready.");
}
// Public static method to get the single instance of the class
// Synchronized to ensure thread safety
public static synchronized Printer getInstance() {
if (instance == null) {
instance = new Printer();
}
return instance;
}
public void print(String message) {
System.out.println("Printing: " + message);
}
}
SingleDP.java
public class SingleDP {
public static void main(String[] args) {
// Get the single instance of Printer
Printer p1 = Printer.getInstance();
p1.print("Hello, World!");
// Get the same instance again
Printer p2 = Printer.getInstance();
p2.print("Java Design Patterns");
// Verify that both variables point to the same object
System.out.println("p1 & p2 refer the same object: "
+ (p1 == p2));
}
}
B) Factory Design Pattern
The Factory Method pattern defines an interface for creating an object but lets subclasses alter the type of objects that will be created.
FactoryMethodExample.java
// Abstract Product Class
abstract class Product {
public abstract void display();
}
// Concrete Products
class ConcreteProductA extends Product {
@Override
public void display() {
System.out.println("This is Concrete Product A.");
}
}
class ConcreteProductB extends Product {
@Override
public void display() {
System.out.println("This is Concrete Product B.");
}
}
// Creator Abstract Class
abstract class Creator {
public abstract Product factoryMethod();
}
// Concrete Creators
class ConcreteCreatorA extends Creator {
@Override
public Product factoryMethod() {
return new ConcreteProductA();
}
}
class ConcreteCreatorB extends Creator {
@Override
public Product factoryMethod() {
return new ConcreteProductB();
}
}
// Client Code
public class FactoryMethodExample {
public static void main(String[] args) {
Creator creatorA = new ConcreteCreatorA();
Product productA = creatorA.factoryMethod();
productA.display();
Creator creatorB = new ConcreteCreatorB();
Product productB = creatorB.factoryMethod();
productB.display();
}
}
C) Observer Design 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.
ObserverDemo.java
import java.util.ArrayList;
import java.util.List;
// Observer interface
interface Observer {
void update(String weatherData);
}
// Concrete Observers
class MobileApp implements Observer {
@Override
public void update(String weatherData) {
System.out.println("Mobile App: Weather updated - "
+ weatherData);
}
}
class Website implements Observer {
@Override
public void update(String weatherData) {
System.out.println("Website: Weather updated - "
+ weatherData);
}
}
// Subject (or Publisher)
class WeatherStation {
private List<Observer> observers = new ArrayList<>();
private String weather;
public void addObserver(Observer o) {
observers.add(o);
}
public void removeObserver(Observer o) {
observers.remove(o);
}
public void setWeather(String newWeather) {
this.weather = newWeather;
notifyObservers();
}
private void notifyObservers() {
for (Observer o : observers) {
o.update(weather);
}
}
}
// Client Code
public class ObserverDemo {
public static void main(String[] args) {
WeatherStation station = new WeatherStation();
Observer mobile = new MobileApp();
Observer site = new Website();
station.addObserver(mobile);
station.addObserver(site);
station.setWeather("Sunny 32°C");
station.setWeather("Rainy 24°C");
}
}
D) Strategy Design Pattern
The Strategy pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. Strategy lets the algorithm vary independently from clients that use it.
StrategyTravelDemo.java
@FunctionalInterface
interface TravelStrategy {
void travel(String destination);
}
// Client code that defines and uses strategies
public class StrategyTravelDemo {
public static void main(String[] args) {
// 1. Define strategies using lambda expressions instead of separate classes
TravelStrategy car = destination ->
System.out.println("Driving to " + destination + " by car.");
TravelStrategy train = destination ->
System.out.println("Going to " + destination + " by train.");
TravelStrategy plane = destination ->
System.out.println("Flying to " + destination + " by plane.");
// 2. Execute the strategies directly
System.out.println("Trip 1:");
car.travel("Goa");
System.out.println("\nTrip 2:");
train.travel("Delhi");
System.out.println("\nTrip 3:");
plane.travel("London");
}
}
Longer Version
// Strategy interface
interface TravelStrategy {
void travel(String destination);
}
// Concrete Strategies
class CarTravel implements TravelStrategy {
@Override
public void travel(String destination) {
System.out.println("Driving to " + destination
+ " by car. Takes more time but gives flexibility.");
}
}
class TrainTravel implements TravelStrategy {
@Override
public void travel(String destination) {
System.out.println("Going to " + destination
+ " by train. Comfortable and affordable.");
}
}
class PlaneTravel implements TravelStrategy {
@Override
public void travel(String destination) {
System.out.println("Flying to " + destination
+ " by plane. Fastest but most expensive.");
}
}
// Context class
class TravelPlanner {
private TravelStrategy strategy;
// Set the travel strategy (can be changed at runtime)
public void setStrategy(TravelStrategy strategy) {
this.strategy = strategy;
}
// Execute the selected travel strategy
public void goTo(String destination) {
if (strategy == null) {
System.out.println("Select a travel strategy first!");
} else {
strategy.travel(destination);
}
}
}
// Client code
public class StrategyTravelDemo {
public static void main(String[] args) {
TravelPlanner planner = new TravelPlanner();
System.out.println("Trip 1:");
planner.setStrategy(new CarTravel());
planner.goTo("Goa");
System.out.println("\nTrip 2:");
planner.setStrategy(new TrainTravel());
planner.goTo("Delhi");
System.out.println("\nTrip 3:");
planner.setStrategy(new PlaneTravel());
planner.goTo("London");
}
}