This project demonstrates the implementation of fundamental design patterns in Java, showcasing both the problems they solve and their practical applications. Each pattern includes comparison implementations to highlight the benefits of using design patterns.
src/
โโโ behavioural/ # Behavioral Design Patterns
โ โโโ command/ # Command Pattern Implementation
โ โ โโโ smartHome/ # Smart Home Automation Example
โ โ โโโ banking/ # Banking Transaction System Example
โ โโโ iterator/ # Iterator Pattern Implementation
โ โ โโโ book/ # Simple book collection example
โ โ โโโ notificationmanagement/ # Comprehensive notification system
โ โโโ mediator/ # Mediator Pattern Implementation
โ โโโ memento/ # Memento Pattern Implementation
โ โโโ observer/ # Observer Pattern Implementation
โ โโโ state/ # State Pattern Implementation
โ โโโ strategy/ # Strategy Pattern Implementation
โ โโโ template/ # Template Method Pattern Implementation
โโโ creational/ # Creational Design Patterns
โ โโโ abstractfactorypattern/ # Abstract Factory Pattern Implementation
โ โ โโโ problem/ # Implementation without the pattern
โ โ โโโ solution/ # Refactored with Abstract Factory
โ โโโ factorymethod/ # Factory Method Pattern Implementation
โ โ โโโ LogisticsApp.java # Logistics example for Factory Method
โ โโโ singleton/ # Singleton Pattern Implementation
โ โ โโโ logger/ # Thread-safe Logger System
โ โ โโโ AppSetting.java # Application Configuration Singleton
โ โ โโโ WithOutSingletonPattern.java # Demonstrates singleton usage
โ โโโ builder/ # Builder Pattern Implementation
โ โโโ prototype/ # Prototype Pattern Implementation
โโโ Main.java # Main entry point
Design patterns are categorized into three main types based on their purpose:
Focus: Communication between objects and the assignment of responsibilities
- Define how objects interact and communicate with each other
- Concerned with algorithms and assignment of responsibilities between objects
- Help in defining the communication patterns between objects
Focus: Object creation mechanisms
- Deal with object creation in a manner suitable to the situation
- Provide flexibility in deciding which objects need to be created for a given use case
- Make the system independent of how its objects are created, composed, and represented
Focus: Object composition and relationships
- Deal with object composition to form larger structures
- Describe ways to compose objects to realize new functionality
- Help ensure that when one part changes, the entire structure doesn't need to change
Creational patterns provide various object creation mechanisms, which increase flexibility and reuse of existing code. They help make a system independent of how its objects are created, composed, and represented.
- Hiding Creation Logic: They encapsulate the knowledge of which concrete classes the system uses.
- Flexibility: The system becomes more flexible in what gets created, who creates it, and how it gets created.
- Reusability: They promote reusing code by defining a way to create objects.
Location: src/creational/singleton/
Core Purpose: Ensures a class has only one instance and provides a global point of access to it.
- Use Cases: Logging, driver objects, caching, and thread pools.
- Implementation: Involves a private constructor, a static field containing its only instance, and a static factory method for obtaining the instance.
Location: src/creational/abstractfactorypattern/
Core Purpose: Provides an interface for creating families of related or dependent objects without specifying their concrete classes.
-
Problem Solved: Imagine creating UI elements for different operating systems (Windows, macOS). You need to ensure that a
WindowsButtonis always used with aWindowsScrollBar. An Abstract Factory (WindowsFactory) guarantees that all created components belong to the same family. -
Key Components:
- AbstractFactory: Declares an interface for operations that create abstract product objects.
- ConcreteFactory: Implements the operations to create concrete product objects.
- AbstractProduct: Declares an interface for a type of product object.
- ConcreteProduct: Defines a product object to be created by the corresponding concrete factory.
-
Code Example (
solution/Application.java):// Abstract Factory interface UiFactory { Button createButton(); Scrollbar createScrollBar(); } // Concrete Factory for Windows class WindowUiFactory implements UiFactory { public Button createButton() { return new WindowButton(); } public Scrollbar createScrollBar() { return new WindowScrollBar(); } } // Client Code public class Application { private Button button; private Scrollbar scrollBar; public Application(UiFactory factory) { button = factory.createButton(); scrollBar = factory.createScrollBar(); } // ... }
Location: src/creational/factorymethod/
Core Purpose: Defines an interface for creating a single object, but lets subclasses decide which class to instantiate. It lets a class defer instantiation to subclasses.
-
Problem Solved: Consider a logistics application. The main
Logisticsclass plans a delivery but doesn't know if it will be by truck or ship. Subclasses likeRoadLogisticsandSeaLogisticsdecide which specific transport object to create. -
Key Components:
- Product: Defines the interface of objects the factory method creates.
- ConcreteProduct: Implements the Product interface.
- Creator: Declares the factory method, which returns an object of type Product.
- ConcreteCreator: Overrides the factory method to return an instance of a ConcreteProduct.
-
Code Example (
LogisticsApp.java):// Creator abstract class Logistics { // The factory method public abstract Transport createTransport(); public void planDelivery() { Transport transport = createTransport(); transport.deliver(); } } // Concrete Creator class RoadLogistics extends Logistics { public Transport createTransport() { return new Truck(); } } // Client Code Logistics logistics = new RoadLogistics(); logistics.planDelivery(); // Uses a Truck
Location: src/creational/builder/
Core Purpose: Separate the construction of a complex object from its representation so the same construction process can create different representations.
- Problem Solved: When creating objects with many optional parameters or complex assembly steps, constructors become unwieldy and error-prone. Builder provides step-by-step construction and readable, flexible configuration.
- Key Components:
Product(e.g.,House),Builderinterface (HouseBuilder),ConcreteBuilders(EcoHouseBuilder,LuxuryHouseBuilder),Director(ConstructionEngineer). - Demo:
BuilderDemo.javabuilds multiple house variants and shows a fluent builder alternative viaHouse.Builder.
// Director orchestrates steps
ConstructionEngineer engineer = new ConstructionEngineer(new LuxuryHouseBuilder());
House house = engineer.constructHouse();
System.out.println(house);
// Fluent builder alternative
House custom = new House.Builder().rooms(4).garage(true).roof("Hip Roof").build();classDiagram
class House {
-foundation : String
-structure : String
-rooms : int
-hasGarage : boolean
-hasGarden : boolean
-hasSwimmingPool : boolean
-roofType : String
-interiorStyle : String
+getSummary() String
<<Product>>
}
class HouseBuilder {
<<interface>>
+buildFoundation()
+buildStructure()
+buildRooms()
+buildRoof()
+buildInterior()
+getHouse() House
}
class EcoHouseBuilder {
<<ConcreteBuilder>>
}
class LuxuryHouseBuilder {
<<ConcreteBuilder>>
}
class ConstructionEngineer {
-houseBuilder : HouseBuilder
+constructHouse() House
<<Director>>
}
HouseBuilder <|.. EcoHouseBuilder
HouseBuilder <|.. LuxuryHouseBuilder
ConstructionEngineer --> HouseBuilder : uses
EcoHouseBuilder --> House : builds
LuxuryHouseBuilder --> House : builds
Location: src/creational/prototype/
Core Purpose: Create new objects by cloning existing ones (prototypes), which is useful when object creation is costly or complex.
- Structure: Contains both
problem/(without pattern) andsolutions/(withPrototypeinterface,GamePiece,GameBoard, andGameClientWithPrototype). - Benefit: Avoids subclass explosion for similar objects; enables fast duplication of configured instances.
classDiagram
class Prototype {
<<interface>>
+clone() Prototype
}
class GamePiece {
-type : String
-color : String
-health : int
+clone() Prototype
<<ConcretePrototype>>
}
class GameBoard {
-pieces : List~GamePiece~
+addPiece(GamePiece)
+copyPiece(GamePiece) GamePiece
<<Client/Aggregate>>
}
Prototype <|.. GamePiece
GameBoard o-- GamePiece : contains
GameBoard ..> Prototype : clones
Behavioral patterns are design patterns that identify common communication patterns between objects and realize these patterns. These patterns increase flexibility in carrying out communication by characterizing the ways in which classes or objects interact and distribute responsibility.
- Inter-object Communication: Define how objects talk to each other
- Message Passing: Establish protocols for object interaction
- Event Handling: Manage how objects respond to events and state changes
- Notification Systems: Implement observer-subscriber relationships
- Role Assignment: Clearly define what each object is responsible for
- Separation of Concerns: Divide complex behaviors into manageable parts
- Delegation: Allow objects to delegate tasks to appropriate handlers
- Chain of Responsibility: Pass requests through a chain of potential handlers
- Strategy Encapsulation: Wrap algorithms in interchangeable objects
- Template Definition: Define algorithm skeletons with customizable steps
- Command Encapsulation: Package requests as objects for flexibility
- State Management: Encapsulate state-specific behavior in separate objects
- Runtime Behavior Changes: Modify object behavior during execution
- Loose Coupling: Reduce dependencies between communicating objects
- Open/Closed Principle: Open for extension, closed for modification
- Dynamic Composition: Compose behaviors at runtime
Use object composition to distribute behavior among objects:
- ๐ฎ Command Pattern: Encapsulate requests as objects
- ๐๏ธ Observer Pattern: Define one-to-many dependencies between objects
- ๐ฏ Strategy Pattern: Define family of algorithms and make them interchangeable
- ๐ Iterator Pattern: Provide sequential access to elements of an aggregate
- ๐พ Memento Pattern: Capture and restore object state without violating encapsulation
Use inheritance to distribute behavior between classes:
- ๐ Template Method Pattern: Define algorithm skeleton, let subclasses override specific steps
// Before: Tight coupling
class WeatherStation {
private Display display;
private MobileApp app;
public void updateTemperature(float temp) {
display.update(temp); // Direct coupling
app.notify(temp); // Hard to extend
}
}
// After: Observer Pattern - Loose coupling
class WeatherStation implements Subject {
private List<Observer> observers = new ArrayList<>();
public void updateTemperature(float temp) {
notifyObservers(temp); // Flexible, extensible
}
}// Before: Rigid algorithm selection
class PaymentProcessor {
public void processPayment(String type, double amount) {
if (type.equals("credit")) {
// Credit card logic
} else if (type.equals("debit")) {
// Debit card logic
}
// Hard to add new payment methods
}
}
// After: Strategy Pattern - Dynamic algorithm selection
class PaymentProcessor {
private PaymentStrategy strategy;
public void setStrategy(PaymentStrategy strategy) {
this.strategy = strategy;
}
public void processPayment(double amount) {
strategy.processPayment(amount); // Flexible, extensible
}
}// Command Pattern enables powerful undo/redo systems
class TextEditor {
private Stack<Command> history = new Stack<>();
public void executeCommand(Command command) {
command.execute();
history.push(command);
}
public void undo() {
if (!history.isEmpty()) {
Command command = history.pop();
command.undo(); // Powerful undo capability
}
}
}- Multiple objects need to communicate efficiently
- You need to change object behavior at runtime
- Algorithms need to be interchangeable
- You want to implement undo/redo functionality
- Complex workflows need to be managed
- Event-driven architectures are required
- You need to iterate over collections uniformly
- Simple direct method calls are sufficient
- Object interactions are minimal and unlikely to change
- Performance overhead is critical
- The system is small and unlikely to grow
This project implements multiple Creational Design Patterns that focus on object creation mechanisms:
- โ
Factory Method (
src/creational/factorymethod/) - โ
Abstract Factory (
src/creational/abstractfactorypattern/) - โ
Singleton (
src/creational/singleton/) - โ
Builder (
src/creational/builder/) - โ
Prototype (
src/creational/prototype/)
The project maintains parallel organization between Behavioral and Creational patterns:
Behavioral Patterns (Current) โ Creational Patterns (Current)
โโโ Command (Actions) โ โโโ Factory Method (Object Creation)
โโโ Observer (Notifications) โ โโโ Builder (Complex Construction)
โโโ Strategy (Algorithms) โ โโโ Singleton (Instance Control)
โโโ Iterator (Collection Access) โ โโโ Prototype (Object Cloning)
โโโ Memento (State Management) โ โโโ Abstract Factory (Family Creation)
โโโ Template Method (Workflows) โ โโโ [Additional patterns as needed]
These patterns can be combined for powerful designs:
- Factory + Strategy: Create strategy objects dynamically
- Builder + Command: Build complex command objects
- Singleton + Observer: Global event management systems
- Prototype + Memento: Efficient state cloning and restoration
Location: src/behavioural/observer/
Core Purpose: Defines a one-to-many dependency between objects so that when one object changes state, all dependents are notified automatically.
- Subject Interface: Attach, detach, notify observers
- Observer Interface: Update method for notifications
- Concrete Subject: Maintains state and observer list
- Concrete Observers: React to state changes
- GUI Applications: Model-View architectures (MVC, MVP, MVVM)
- Event Systems: DOM events, custom event handlers
- Stock Market: Price change notifications
- Social Media: Notification systems for followers
// Observer Interface
interface StockObserver {
void update(String stockSymbol, double price, double change);
String getObserverName();
}
// Subject Interface
interface StockSubject {
void addObserver(StockObserver observer);
void removeObserver(StockObserver observer);
void notifyObservers();
}
// Concrete Subject - Stock
class Stock implements StockSubject {
private String symbol;
private double price;
private double previousPrice;
private List<StockObserver> observers = new ArrayList<>();
public Stock(String symbol, double initialPrice) {
this.symbol = symbol;
this.price = initialPrice;
this.previousPrice = initialPrice;
}
public void setPrice(double newPrice) {
this.previousPrice = this.price;
this.price = newPrice;
System.out.println("๐ " + symbol + " price updated: $" + price);
notifyObservers();
}
public void addObserver(StockObserver observer) {
observers.add(observer);
System.out.println("โ " + observer.getObserverName() + " subscribed to " + symbol);
}
public void removeObserver(StockObserver observer) {
observers.remove(observer);
System.out.println("โ " + observer.getObserverName() + " unsubscribed from " + symbol);
}
public void notifyObservers() {
double change = price - previousPrice;
for (StockObserver observer : observers) {
observer.update(symbol, price, change);
}
}
public String getSymbol() { return symbol; }
public double getPrice() { return price; }
}
// Concrete Observers
class TradingBot implements StockObserver {
private String botName;
private double buyThreshold;
private double sellThreshold;
public TradingBot(String name, double buyThreshold, double sellThreshold) {
this.botName = name;
this.buyThreshold = buyThreshold;
this.sellThreshold = sellThreshold;
}
public void update(String stockSymbol, double price, double change) {
System.out.println("๐ค " + botName + " received update: " + stockSymbol +
" = $" + price + " (change: " + String.format("%.2f", change) + ")");
if (change <= -buyThreshold) {
System.out.println("๐ค " + botName + " DECISION: BUY " + stockSymbol +
" (price dropped by $" + Math.abs(change) + ")");
} else if (change >= sellThreshold) {
System.out.println("๐ค " + botName + " DECISION: SELL " + stockSymbol +
" (price increased by $" + change + ")");
}
}
public String getObserverName() { return botName; }
}
class PortfolioTracker implements StockObserver {
private String portfolioName;
private Map<String, Integer> holdings = new HashMap<>();
public PortfolioTracker(String name) {
this.portfolioName = name;
}
public void addHolding(String symbol, int shares) {
holdings.put(symbol, shares);
}
public void update(String stockSymbol, double price, double change) {
if (holdings.containsKey(stockSymbol)) {
int shares = holdings.get(stockSymbol);
double totalValue = shares * price;
double totalChange = shares * change;
System.out.println("๐ " + portfolioName + " Portfolio Update:");
System.out.println(" " + stockSymbol + ": " + shares + " shares @ $" + price +
" = $" + String.format("%.2f", totalValue));
System.out.println(" Change: " + (change >= 0 ? "+" : "") +
String.format("%.2f", totalChange));
}
}
public String getObserverName() { return portfolioName + " Portfolio"; }
}
class PriceAlertSystem implements StockObserver {
private String alertName;
private Map<String, Double> priceAlerts = new HashMap<>();
public PriceAlertSystem(String name) {
this.alertName = name;
}
public void setPriceAlert(String symbol, double targetPrice) {
priceAlerts.put(symbol, targetPrice);
System.out.println("๐ Alert set: " + symbol + " @ $" + targetPrice);
}
public void update(String stockSymbol, double price, double change) {
if (priceAlerts.containsKey(stockSymbol)) {
double targetPrice = priceAlerts.get(stockSymbol);
if ((change > 0 && price >= targetPrice) || (change < 0 && price <= targetPrice)) {
System.out.println("๐จ PRICE ALERT: " + stockSymbol +
" reached target price $" + targetPrice +
" (current: $" + price + ")");
priceAlerts.remove(stockSymbol); // Remove triggered alert
}
}
}
public String getObserverName() { return alertName; }
}
// Usage Example
public class StockMarketExample {
public static void main(String[] args) {
// Create stocks
Stock appleStock = new Stock("AAPL", 150.00);
Stock googleStock = new Stock("GOOGL", 2800.00);
// Create observers
TradingBot dayTrader = new TradingBot("DayTrader Bot", 2.0, 3.0);
TradingBot swingTrader = new TradingBot("Swing Trader", 5.0, 8.0);
PortfolioTracker myPortfolio = new PortfolioTracker("John's");
PriceAlertSystem alertSystem = new PriceAlertSystem("Mobile App");
// Setup portfolio
myPortfolio.addHolding("AAPL", 100);
myPortfolio.addHolding("GOOGL", 10);
// Setup alerts
alertSystem.setPriceAlert("AAPL", 155.0);
alertSystem.setPriceAlert("GOOGL", 2750.0);
// Subscribe observers to stocks
appleStock.addObserver(dayTrader);
appleStock.addObserver(myPortfolio);
appleStock.addObserver(alertSystem);
googleStock.addObserver(swingTrader);
googleStock.addObserver(myPortfolio);
googleStock.addObserver(alertSystem);
System.out.println("\n--- Market Updates ---");
// Simulate price changes
appleStock.setPrice(152.50); // +$2.50
googleStock.setPrice(2790.00); // -$10.00
appleStock.setPrice(156.00); // +$3.50 (triggers alert)
googleStock.setPrice(2745.00); // -$45.00 (triggers alert)
// Unsubscribe an observer
System.out.println("\n--- Unsubscribing DayTrader from AAPL ---");
appleStock.removeObserver(dayTrader);
appleStock.setPrice(160.00); // Only portfolio and alerts notified
}
}// Observer Interface for Social Media
interface SocialMediaObserver {
void onNewPost(String username, String content, String timestamp);
void onNewFollower(String followerName);
void onLike(String username, String postId);
String getNotificationPreference();
}
// Subject Interface
interface SocialMediaSubject {
void addFollower(SocialMediaObserver follower);
void removeFollower(SocialMediaObserver follower);
void notifyNewPost(String content);
void notifyNewFollower(String followerName);
void notifyLike(String postId);
}
// Concrete Subject - User Profile
class UserProfile implements SocialMediaSubject {
private String username;
private List<SocialMediaObserver> followers = new ArrayList<>();
private List<String> posts = new ArrayList<>();
private int followerCount = 0;
public UserProfile(String username) {
this.username = username;
}
public void addFollower(SocialMediaObserver follower) {
followers.add(follower);
followerCount++;
System.out.println("๐ค " + follower.getNotificationPreference() +
" started following " + username);
notifyNewFollower(follower.getNotificationPreference());
}
public void removeFollower(SocialMediaObserver follower) {
followers.remove(follower);
followerCount--;
System.out.println("๐ค " + follower.getNotificationPreference() +
" unfollowed " + username);
}
public void createPost(String content) {
String postId = "POST_" + System.currentTimeMillis();
posts.add(content);
System.out.println("๐ " + username + " created new post: \"" + content + "\"");
notifyNewPost(content);
}
public void notifyNewPost(String content) {
String timestamp = java.time.LocalDateTime.now().toString();
for (SocialMediaObserver follower : followers) {
follower.onNewPost(username, content, timestamp);
}
}
public void notifyNewFollower(String followerName) {
// Notify the user about new follower (not all followers)
System.out.println("๐ " + username + " gained a new follower: " + followerName);
}
public void notifyLike(String postId) {
for (SocialMediaObserver follower : followers) {
follower.onLike(username, postId);
}
}
public String getUsername() { return username; }
public int getFollowerCount() { return followerCount; }
}
// Concrete Observers
class MobileAppNotification implements SocialMediaObserver {
private String deviceId;
private boolean pushEnabled = true;
public MobileAppNotification(String deviceId) {
this.deviceId = deviceId;
}
public void onNewPost(String username, String content, String timestamp) {
if (pushEnabled) {
System.out.println("๐ฑ [Mobile Push] " + username + " posted: \"" +
content.substring(0, Math.min(content.length(), 50)) +
(content.length() > 50 ? "..." : "") + "\"");
}
}
public void onNewFollower(String followerName) {
// Mobile app doesn't notify about new followers to other users
}
public void onLike(String username, String postId) {
if (pushEnabled) {
System.out.println("๐ฑ [Mobile Push] " + username + " liked your post");
}
}
public void setPushEnabled(boolean enabled) {
this.pushEnabled = enabled;
System.out.println("๐ฑ Push notifications " + (enabled ? "enabled" : "disabled") +
" for " + deviceId);
}
public String getNotificationPreference() { return "Mobile User (" + deviceId + ")"; }
}
class EmailNotification implements SocialMediaObserver {
private String emailAddress;
private boolean dailyDigest = true;
private List<String> pendingNotifications = new ArrayList<>();
public EmailNotification(String email) {
this.emailAddress = email;
}
public void onNewPost(String username, String content, String timestamp) {
if (dailyDigest) {
pendingNotifications.add("New post from " + username + ": " + content);
} else {
sendImmediateEmail("New post from " + username, content);
}
}
public void onNewFollower(String followerName) {
// Email notifications for new followers are usually disabled
}
public void onLike(String username, String postId) {
pendingNotifications.add(username + " liked your post");
}
private void sendImmediateEmail(String subject, String content) {
System.out.println("๐ง [Email] To: " + emailAddress +
" | Subject: " + subject + " | Content: " + content);
}
public void sendDailyDigest() {
if (!pendingNotifications.isEmpty()) {
System.out.println("๐ง [Daily Digest] To: " + emailAddress);
System.out.println(" You have " + pendingNotifications.size() + " notifications:");
for (String notification : pendingNotifications) {
System.out.println(" - " + notification);
}
pendingNotifications.clear();
}
}
public String getNotificationPreference() { return "Email User (" + emailAddress + ")"; }
}
class AnalyticsTracker implements SocialMediaObserver {
private String analyticsId;
private Map<String, Integer> postEngagement = new HashMap<>();
private Map<String, Integer> userActivity = new HashMap<>();
public AnalyticsTracker(String id) {
this.analyticsId = id;
}
public void onNewPost(String username, String content, String timestamp) {
userActivity.put(username, userActivity.getOrDefault(username, 0) + 1);
System.out.println("๐ [Analytics] Post created by " + username +
" (total posts: " + userActivity.get(username) + ")");
}
public void onNewFollower(String followerName) {
System.out.println("๐ [Analytics] New follower acquired: " + followerName);
}
public void onLike(String username, String postId) {
postEngagement.put(postId, postEngagement.getOrDefault(postId, 0) + 1);
System.out.println("๐ [Analytics] Like recorded for " + postId +
" (total likes: " + postEngagement.get(postId) + ")");
}
public void generateReport() {
System.out.println("๐ [Analytics Report]");
System.out.println(" Active users: " + userActivity.size());
System.out.println(" Total posts: " + userActivity.values().stream().mapToInt(Integer::intValue).sum());
System.out.println(" Total likes: " + postEngagement.values().stream().mapToInt(Integer::intValue).sum());
}
public String getNotificationPreference() { return "Analytics System"; }
}
// Usage Example
public class SocialMediaExample {
public static void main(String[] args) {
// Create user profiles
UserProfile techInfluencer = new UserProfile("@TechGuru");
UserProfile photographer = new UserProfile("@PhotoPro");
// Create notification observers
MobileAppNotification johnMobile = new MobileAppNotification("iPhone_John");
MobileAppNotification sarahMobile = new MobileAppNotification("Android_Sarah");
EmailNotification johnEmail = new EmailNotification("john@example.com");
AnalyticsTracker analytics = new AnalyticsTracker("SocialMedia_Analytics");
// Users follow the influencer
techInfluencer.addFollower(johnMobile);
techInfluencer.addFollower(sarahMobile);
techInfluencer.addFollower(johnEmail);
techInfluencer.addFollower(analytics);
photographer.addFollower(johnMobile);
photographer.addFollower(analytics);
System.out.println("\n--- Social Media Activity ---");
// Simulate social media activity
techInfluencer.createPost("Just released a new tutorial on design patterns! Check it out ๐");
photographer.createPost("Golden hour shot from today's photoshoot โจ๐ธ");
// Simulate likes (would normally come from other users)
techInfluencer.notifyLike("POST_123");
photographer.notifyLike("POST_456");
// User changes notification preferences
System.out.println("\n--- Changing Notification Preferences ---");
johnMobile.setPushEnabled(false);
// More activity
techInfluencer.createPost("Working on a new project with microservices architecture!");
// Generate analytics report
System.out.println("\n--- Analytics Report ---");
analytics.generateReport();
// Send daily digest
System.out.println("\n--- Daily Email Digest ---");
johnEmail.sendDailyDigest();
}
}- ๐ก Loose Coupling: Subjects don't know about specific observer implementations
- ๐ Dynamic Relationships: Observers can subscribe/unsubscribe at runtime
- ๐ข Broadcast Communication: One event notifies multiple observers simultaneously
- โก Event-Driven Architecture: Automatic reactions to state changes
- ๐๏ธ Flexible Notifications: Different observers can handle events differently
- ๐ Real-Time Updates: Immediate propagation of changes to interested parties
Location: src/behavioural/strategy/
Core Purpose: Defines a family of algorithms, encapsulates each one, and makes them interchangeable at runtime.
- Strategy Interface: Common interface for all algorithms
- Concrete Strategies: Different algorithm implementations
- Context: Uses strategy to perform operations
- Payment Processing: Credit card, PayPal, cryptocurrency
- Sorting Algorithms: QuickSort, MergeSort, BubbleSort
- Navigation Apps: Fastest route, shortest route, scenic route
- Authentication: OAuth, LDAP, database authentication
Location: src/behavioural/memento/
Core Purpose: Captures and externalizes an object's internal state without violating encapsulation, allowing the object to be restored to this state later.
- Text Editor with Undo: Basic undo/redo functionality
- Graphic Editor: Shape manipulation with history
- Transaction Rollback: Database-like operations with rollback
- Text Editors: Undo/Redo functionality (MS Word, VS Code)
- Database Systems: Transaction rollback, savepoints
- Game Development: Save/load game states, checkpoints
- Version Control: Git commits, branching
Location: src/behavioural/template/
Core Purpose: Defines the skeleton of an algorithm in a base class, letting subclasses override specific steps without changing the algorithm's structure.
- Abstract Class: Defines template method and common steps
- Template Method: Defines algorithm skeleton
- Concrete Classes: Implement specific steps
- Data Processing: File parsing (CSV, JSON, XML, Excel)
- Web Frameworks: Request processing pipeline
- Game Development: Game loop (input, update, render)
- Testing Frameworks: Test execution (setup, execute, teardown)
Location: src/behavioural/iterator/
Core Purpose: Provides a way to access the elements of an aggregate object sequentially without exposing its underlying representation.
This implementation demonstrates the Iterator pattern through a real-world notification management system that handles three different types of notifications, each using optimal data structures:
A. Core Components:
- Notification.java - Basic notification entity with message content
- NotificationCollection.java - Interface defining
createIterator()method - NotificationManager.java - Central coordinator managing all notification types
- NotificationApp.java - Interactive application with user input and demonstration
B. Three Notification Types with Different Data Structures:
-
EmailNotification.java
- Uses
ArrayList<Notification>for indexed access - Implements
EmailNotificationIteratorfor sequential traversal - Ideal for: Ordered email queues, batch processing
- Uses
-
SMSNotification.java
- Uses
ArrayDeque<Notification>(Queue) for FIFO processing - Implements
SMSNotificationIteratorfor queue-based iteration - Ideal for: Real-time SMS delivery, priority messaging
- Uses
-
PushNotification.java
- Uses
LinkedHashSet<Notification>for unique, ordered notifications - Implements
PushNotificationIteratorfor set-based iteration - Ideal for: Preventing duplicate push notifications, maintaining order
- Uses
- Uniform Interface: Same iterator pattern across different data structures (List, Queue, Set)
- Encapsulation: Internal data structure completely hidden from NotificationManager
- Flexibility: Each notification type uses the most appropriate data structure
- Polymorphism: All notification types implement the same NotificationCollection interface
- Scalability: Easy to add new notification types without changing existing code
- Data Structure Independence: Client code works regardless of underlying collection type
- Notification Systems: Mobile apps, web applications, desktop software
- Message Queuing: Email servers, SMS gateways, push notification services
- Event Processing: Real-time analytics, logging systems, audit trails
- Social Media Platforms: Feed updates, friend requests, activity notifications
- E-commerce: Order updates, shipping notifications, promotional messages
- Enterprise Applications: System alerts, user notifications, workflow updates
- IoT Systems: Device status updates, sensor alerts, system monitoring
- Gaming Platforms: Achievement notifications, friend activities, game invites
| Pattern Combination | Use Case | Example |
|---|---|---|
| Command + Memento | Undo/Redo with state restoration | Advanced text editors |
| Observer + Strategy | Dynamic algorithm notification | Real-time trading systems |
| Template Method + Strategy | Pluggable algorithm frameworks | Data processing pipelines |
| Command + Observer | Event-driven command execution | GUI event handling |
| Iterator + Composite | Traversing hierarchical structures | File system navigation |
| Iterator + Visitor | Processing collections with different operations | Data analysis pipelines |
Ask These Questions:
- Need to undo operations? โ Command Pattern
- Need to save/restore state? โ Memento Pattern
- Multiple objects need notifications? โ Observer Pattern
- Multiple algorithms for same task? โ Strategy Pattern
- Common algorithm with variations? โ Template Method Pattern
- Need to access aggregate elements sequentially? โ Iterator Pattern
- Banking Systems: Command (transactions), Memento (rollback), Observer (account notifications), Iterator (transaction history)
- E-commerce: Strategy (payment methods), Observer (inventory updates), Template Method (order processing), Iterator (product catalogs)
- Content Management: Command (content operations), Memento (version control), Template Method (publishing workflow), Iterator (content browsing)
- Game Engines: Command (input handling), Memento (save states), Observer (event systems), Iterator (game object collections)
- Mobile Games: Strategy (difficulty levels), Template Method (game loops), Iterator (inventory systems)
- Frameworks: Template Method (request lifecycle), Observer (event listeners), Strategy (routing), Iterator (data pagination)
- Frontend: Observer (reactive programming), Command (user actions), Iterator (component rendering)
- Java 8 or higher
- IDE (IntelliJ IDEA, Eclipse, VS Code)
Experience design patterns through a beautiful, interactive GUI application!
For macOS/Linux:
# Make the script executable (first time only)
chmod +x run-gui.sh
# Run the GUI application
./run-gui.shFor Windows:
# Double-click the file or run from command prompt
run-gui.batManual Compilation (All Platforms):
# Compile all Java files
mkdir -p bin
find src -name "*.java" -print0 | xargs -0 javac -d bin -cp src
# Run the GUI application
java -cp bin gui.DesignPatternShowcaseThe interactive GUI application provides:
- Overview of all implemented patterns
- Pattern category explanations
- Navigation guide
- Configuration Singleton: Interactive AppSetting management
- Update database URL and API key
- Verify single instance behavior with hash codes
- Demonstrate shared state across references
- Logger Singleton: Thread-safe logging system
- Test different log levels (INFO, WARN, ERROR)
- Real-time log output with timestamps
- Instance verification with visual feedback
- Smart Home Automation System: Full interactive control panel
- ๐ก Light Controls: Turn lights on/off, dim functionality
- ๐ก๏ธ Thermostat: Slider and custom temperature controls
- ๐ Security System: Arm/disarm with visual status
- โถ Undo Functionality: Reverse any command
- ๐ Macro Commands: Good Night routine (multiple commands)
- ๐ Command History: Track all executed commands with timestamps
- ๐ Real-time Status: Live device status updates
- Stock market simulation with real-time updates
- Multiple observer types (trading bots, portfolio trackers, alerts)
- Visual notification system
- Notification management system
- Different collection types (ArrayList, ArrayDeque, LinkedHashSet)
- Uniform iteration interface demonstration
- Visual Learning: See patterns in action with immediate feedback
- Interactive Exploration: Click, modify, and experiment with real examples
- Real-time Updates: Watch how pattern components interact
- Educational Explanations: Built-in pattern explanations and benefits
- Professional UI: Modern, clean interface with intuitive navigation
Each pattern has its own main class for demonstration:
# Command Pattern
java behavioural.command.WithCommondPattern
# Iterator Pattern - Notification Management System
java behavioural.iterator.notificationmanagement.NotificationApp
# Memento Pattern
java behavioural.memento.texteditor.TextEditorMain
java behavioural.memento.graphiceditor.GraphicEditorMain
java behavioural.memento.tnxrollback.Solution
# Observer Pattern
java behavioural.observer.weather.ObserverPatternExample
# Strategy Pattern
java behavioural.strategy.payment.StrategyPattern
# Template Method Pattern
java behavioural.template.WithTemplatePattern๐ฏ GUI Application (Recommended):
./run-gui.sh # macOS/Linux
run-gui.bat # Windows
# Features:
# - Interactive Singleton configuration and logging
# - Smart home automation with Command pattern
# - Visual device controls and command history
# - Real-time status updates and undo functionality
# - Pattern explanations and educational contentSingleton Pattern - Logger Exercise:
java creational.singleton.logger.Exercise
# Interactive demo that allows you to:
# 1. Test different logging levels (INFO, WARN, ERROR)
# 2. See timestamp formatting in action
# 3. Verify singleton behavior with instance comparison
# 4. Experience thread-safe singleton implementation
#
# Example interaction:
# Enter an info message: Application started successfully
# Enter a warning message: Low memory detected
# Enter an error message: Database connection failed
#
# Output shows:
# 2024-01-15 14:30:25 [INFO]: Application started successfully
# 2024-01-15 14:30:30 [WARN]: Low memory detected
# 2024-01-15 14:30:35 [ERROR]: Database connection failed
#
# Singleton verification shows same instance hash codesSingleton Pattern - Configuration Demo:
java creational.singleton.WithOutSingletonPattern
# Demonstrates:
# - Single instance creation and reuse
# - Shared state across multiple references
# - Object identity comparison (== returns true)
# - Global configuration access pattern{{ ... }}
| Pattern | Use When | Avoid When |
|---|---|---|
| Singleton | Need single instance, global access | Multiple instances needed, testing difficulties |
| Command | Need undo/redo, queuing, logging | Simple direct method calls suffice |
| {{ ... }} |
Singleton Pattern Enhancements:
- Add Double-Checked Locking for better performance
- Implement Enum Singleton for serialization safety
- Create Registry Singleton for managing multiple named instances
- Add Lazy Holder Pattern for thread-safe lazy initialization
- Implement Singleton with Parameters for configurable instances
General Pattern Extensions:
- Add Undo functionality to Command Pattern {{ ... }}