Behavioral patterns are concerned with algorithms and the assignment of responsibilities between objects. They describe not just patterns of objects or classes but also the patterns of communication between them.
Intent: Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.
Think of a YouTube channel. You subscribe to a channel. When the creator uploads a new video, YouTube automatically notifies all subscribers. You don't have to keep refreshing the channel page to check for new content.
Imagine a weather station that measures temperature. Multiple display elements (a phone app, a web dashboard, a smart watch) all need to update whenever the temperature changes. Without the Observer pattern, the weather station would need hardcoded references to every display, and adding a new display would require modifying the station's code.
import java.util.ArrayList;
import java.util.List;
// The Subject (Publisher)
interface Subject {
void subscribe(Observer o);
void unsubscribe(Observer o);
void notifyObservers();
}
// The Observer (Subscriber)
interface Observer {
void update(float temperature);
}
class WeatherStation implements Subject {
private List<Observer> observers = new ArrayList<>();
private float temperature;
public void subscribe(Observer o) { observers.add(o); }
public void unsubscribe(Observer o) { observers.remove(o); }
public void notifyObservers() {
for (Observer o : observers) {
o.update(temperature);
}
}
public void setTemperature(float temp) {
this.temperature = temp;
notifyObservers(); // Automatically notify all subscribers!
}
}
class PhoneApp implements Observer {
public void update(float temperature) {
System.out.println("Phone App: Temperature is " + temperature + " C");
}
}
class WebDashboard implements Observer {
public void update(float temperature) {
System.out.println("Dashboard: Temperature is " + temperature + " C");
}
}
// Usage
WeatherStation station = new WeatherStation();
station.subscribe(new PhoneApp());
station.subscribe(new WebDashboard());
station.setTemperature(25.5f);
// Output:
// Phone App: Temperature is 25.5 C
// Dashboard: Temperature is 25.5 C
Adding a SmartWatch display requires zero changes to WeatherStation.
Intent: Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.
When booking a ride, the transportation app lets you choose between different pricing strategies: Economy, Premium, or Pool. Each strategy calculates the fare differently, but the booking workflow remains the same.
A navigation app needs to support different routing algorithms: driving, walking, and cycling. Without the Strategy pattern, the route calculation method would be a massive if-else block that grows every time a new mode is added.
// The Strategy Interface
interface RouteStrategy {
String calculateRoute(String origin, String destination);
}
// Concrete Strategies
class DrivingStrategy implements RouteStrategy {
public String calculateRoute(String o, String d) {
return "Driving route from " + o + " to " + d + " via highways";
}
}
class WalkingStrategy implements RouteStrategy {
public String calculateRoute(String o, String d) {
return "Walking route from " + o + " to " + d + " via parks";
}
}
class CyclingStrategy implements RouteStrategy {
public String calculateRoute(String o, String d) {
return "Cycling route from " + o + " to " + d + " via bike lanes";
}
}
// The Context
class Navigator {
private RouteStrategy strategy;
void setStrategy(RouteStrategy strategy) {
this.strategy = strategy;
}
void navigate(String origin, String destination) {
String route = strategy.calculateRoute(origin, destination);
System.out.println(route);
}
}
// Usage: The algorithm can be changed at RUNTIME
Navigator nav = new Navigator();
nav.setStrategy(new DrivingStrategy());
nav.navigate("Home", "Office");
// Output: Driving route from Home to Office via highways
nav.setStrategy(new CyclingStrategy());
nav.navigate("Home", "Office");
// Output: Cycling route from Home to Office via bike lanes
The Navigator class doesn't know or care how the route is calculated. It delegates the algorithm to the injected strategy object. Swapping algorithms at runtime is as simple as calling setStrategy().