Behavioral pattern
Behavioral design pattern focuses on how objects interact and communicate with each other. It defines patterns of responsibility and delegation among objects, targeting to make interactions more flexible and easier to manage.
Goals
- Improve communication between objects.
- Assign responsibilities cleanly and clearly.
- Increase flexibility in how behavior is distributed.
Strategy pattern
It lets you define multiple behaviors (strategies) and switch between them at runtime without altering the code that uses them.
Key Features
- Open/Closed Principle: Add new strategies without modifying existing code.
- Cleaner Code: Removes cluttered if/else logic.
- Reusability: Reuse algorithms across different contexts.
Use Cases
- When you have multiple related classes that only differ in their behavior.
- When you want to avoid using large conditional statements (e.g., if-else or switch-case) to select behavior.
- When you want to isolate behavior so it can be reused or tested independently.
Cons
- Can increase the number of classes.
- Clients must be aware of different strategies.
java
// Interface
public interface PaymentStrategy {
void pay(int amount);
}
// Concrete implementation
public class CreditCardPayment implements PaymentStrategy {
private String cardNumber;
public CreditCardPayment(String cardNumber) {
this.cardNumber = cardNumber;
}
@Override
public void pay(int amount) {
System.out.println("Credit Card: Paid $" + amount + " Card: " + cardNumber);
}
}
public class PayPalPayment implements PaymentStrategy {
private String email;
public PayPalPayment(String email) {
this.email = email;
}
@Override
public void pay(int amount) {
System.out.println("PayPal: Paid $" + amount + " account: " + email);
}
}
// Context
public class ShoppingCart {
private PaymentStrategy paymentStrategy;
public void setPaymentStrategy(PaymentStrategy strategy) {
this.paymentStrategy = strategy;
}
public void checkout(int amount) {
if (paymentStrategy == null) {
System.out.println("No payment strategy set.");
} else {
paymentStrategy.pay(amount);
}
}
}
// Usage
public class Main {
public static void main(String[] args) {
ShoppingCart cart = new ShoppingCart();
// Using PayPal
cart.setPaymentStrategy(new PayPalPayment("test@test.com"));
cart.checkout(35);
// Switch to Credit Card
cart.setPaymentStrategy(new CreditCardPayment("1234"));
cart.checkout(50);
}
}
Command pattern
It encapsulates a request as an object, thereby letting you parameterize clients with different requests, delay execution, or support undo.
Components
Role | Description |
---|---|
Command | An interface that declares a method for executing the command. |
ConcreteCommand | Implements the Command interface and defines the binding between a Receiver and an action. |
Receiver | The object that knows how to perform the operation. |
Invoker | Asks the command to carry out the request. |
Client | Creates the command and sets its receiver. |
Use Cases
- To queue and execute commands at different times.
- To implement undo/redo functionality.
- To decouple objects that produce commands from those that execute them.
- For macro recording (e.g., keyboard macros).
Observer pattern
It allows an object (called the subject) to notify other objects (called observers) about changes to its state, without knowing who or what those observers are.
Pros
- Promotes loose coupling between subject and observers.
- Observers can be added/removed dynamically.
- Good for implementing event-driven systems.
Cons
- Can lead to memory leaks if observers aren't removed properly.
- Difficult to debug due to many dynamic relationships.
Use Cases
- When changes to one object should trigger updates in others.
- In GUI frameworks (e.g., buttons and event listeners).
- In real-time systems (e.g., stock tickers, news feeds, chat apps).