Spring Boot Design Patterns Overview
Spring Boot Design Patterns Overview
Creational
1. Singleton
2. Builder
3. Factory
4. Abstract Factory
5. Object Pool
6. Prototype
Behavioural
1. Adapter
c. We can use a factory to pick the best version among the adapters
e. Autowired annotation and Map can be used in spring boot to get the
corresponding instance with the Component qualifier name
2. Bridge
In some complex architectures, you may build adapters that use other
adapters internally (e.g., in a plug-in system), and the result starts
resembling a Bridge structurally.
From a bird’s eye view, both patterns are solving integration problems —
Bridge for decoupling, Adapter for compatibility.
Autowired annotation and Map can be used in spring boot to get the
corresponding instance with the Component qualifier name and type
3. Decorator ( Wrapper )
b. We want to notify users via email, but also allow optional SMS and Slack
notifications without altering the original notifier.
🧩
Design Patterns Use Cases (Java And Spring) 3
🧩 Pattern Intent
Pattern Purpose
4. Composite
5. Filter (Criteria)
6. Proxy
b. Used for:
7. Flyweight
🔧
Design Patterns Use Cases (Java And Spring) 4
🔧 When to Use
You have many objects that consume a lot of memory.
Most object data is intrinsic (shared) and only a small part is extrinsic
(context-specific).
You want to reuse objects instead of creating new ones each time.
8. Facade
Structural
1. Chain Of Responsibility
Task Queue
@PostConstruct
public void startExecutionLoop() {
// Start a background thread to execute commands
new Thread(this::processCommands).start();
}
Benefit Description
3. Interator
a. The Iterator pattern allows you to traverse a collection (like a list, set, or
custom data structure) one element at a time, without knowing its internal
structure.
e. Traversal abstraction – when you want to hide the internal structure (tree,
graph, etc.).
4. Intepreter
d. SQL parsing
e. Mini-language processing
f. Regex parsing
g. Search filters
5. Mediator
d. [Link]
a. Without exposing the object’s internal structure, you can save and restore
its state.
b. AL/ML rollbacks
✅ Summary Table
Domain Use Case
7. Null State
a. The Null Object Design Pattern provides an object as a surrogate for the absence of a real object.
Instead of returning null, return a special object that implements the expected interface but does nothing (a
“do-nothing” implementation). This helps eliminate null checks and NullPointerExceptions.
✅
Design Patterns Use Cases (Java And Spring) 8
✅ Real-world Use Cases
Context Use Case
8. Observer
Role Description
9. State
10. Strategy
Authentication
mechanisms
11. Template
Algorithms with invariant Sorting algorithms that share common parts but
structure but variable steps vary the pivot choice.
Code reuse with common Base classes define the skeleton, subclasses
algorithm parts override details.
12. Visitor
Invoice
Report
d. We want to:
Creational
1. Singleton
Java
private Singleton() {}
Spring Boot
package [Link];
import [Link];
✅ [Link]
package [Link];
import [Link];
import [Link];
import [Link].*;
@RestController
@RequestMapping("/log")
public class LogController {
@Autowired
public LogController(LoggerService loggerService) {
[Link] = loggerService;
}
@PostMapping
public String logMessage(@RequestParam String message) {
[Link](message);
return "Logged: " + message;
Even if you hit the /log?message=Hello endpoint multiple times, the same instance will
be reused.
2. Builder
Java
// Optional fields
private final int age;
private final String phone;
private final String address;
@Override
public String toString() {
return "User: " + firstName + " " + lastName + ", Age: " + age + ", Phone: " +
}
}
[Link](user);
}
}
Spring Boot
package [Link];
import [Link];
import [Link];
import [Link];
@Getter
@ToString
@Builder
public class User {
private String firstName;
private String lastName;
package [Link];
import [Link];
import [Link];
import [Link].*;
@RestController
@RequestMapping("/api/users")
public class UserController {
@PostMapping("/build")
public User buildUser(@RequestBody UserRequest req) {
return [Link]()
.firstName([Link]())
.lastName([Link]())
.age([Link]())
.phone([Link]())
.address([Link]())
.build();
}
}
3. Factory
Java
✅ 5. Client Usage
public class Main {
public static void main(String[] args) {
Notification notification = [Link](Notificati
[Link]);
[Link](); // Output: Sending an SMS notification
}
}
Spring Boot
[Link]
package [Link];
[Link]
package [Link];
[Link]
package [Link];
🧾 2. Enum
[Link]
package [Link];
🏭 3. Factory Class
[Link]
package [Link];
import [Link].*;
import [Link];
import [Link];
@Component
public class NotificationFactory {
🌐 4. Controller
[Link]
package [Link];
import [Link];
import [Link];
import [Link];
import [Link].*;
@RestController
@RequestMapping("/api")
public class NotificationController {
@PostMapping("/notify")
public String notifyUser(@RequestParam NotificationType type) {
Notification notification = [Link](type);
return [Link]();
}
}
🚀
Design Patterns Use Cases (Java And Spring) 23
🚀 5. Main Application
[Link]
package [Link];
import [Link];
import [Link];
@SpringBootApplication
public class NotificationFactoryApplication {
public static void main(String[] args) {
[Link]([Link], args);
}
}
package [Link];
import [Link];
import [Link].slf4j.Slf4j;
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
@Service
@Slf4j
public class VehicleProvider {
@Autowired
public VehicleProvider(List<Vehicle> vehicleList){
vehicleMap = new HashMap<>();
for(Vehicle vehicle : vehicleList){
[Link]([Link](), vehicle);
}
}
package [Link];
switch (type) {
case "bike":
return new Bike(wheel);
case "car":
return new Car(wheel);
default:
[Link]("invalid type");
}
return null;
package [Link];
import [Link];
import [Link];
import [Link];
import [Link];
@Service
@RequiredArgsConstructor
public class VehicleService {
@Service
@Slf4j
@RequiredArgsConstructor
@Override
public int getWheel() {
return wheel;
}
@Override
public VehicleType getType() {
return type;
}
@Override
public void process(CarContext context) {
[Link](type + " :process");
}
}
@Service
@Slf4j
@RequiredArgsConstructor
public class Bike implements Vehicle<BikeContext> {
@Override
public int getWheel() {
return wheel;
}
@Override
public VehicleType getType() {
return type;
}
@Override
public void process(BikeContext bikeContext) {
[Link](type + " : process");
}
@Component
public class VehicleBuilder {
@Data
public class BikeContext extends VehicleContext {
}
@Data
public class CarContext extends VehicleContext {
}
@Data
public class VehicleContext {
private String id;
}
// Abstract Product B
public interface Checkbox {
void render();
}
✅ 2. Concrete Products
// Windows
public class WindowsButton implements Button {
public void render() {
[Link]("Rendering Windows Button");
}
}
// Mac
public class MacButton implements Button {
public void render() {
[Link]("Rendering Mac Button");
}
}
✅ 3. Abstract Factory
public interface GUIFactory {
Button createButton();
Checkbox createCheckbox();
}
✅ 4. Concrete Factories
public class WindowsFactory implements GUIFactory {
public Button createButton() {
return new WindowsButton();
}
🧠 Key Concepts
Expensive objects: These are objects that are costly to instantiate (e.g.,
database connections, thread pools, socket connections).
Pooling: Keeping a fixed number of objects and reusing them rather than
creating/destroying repeatedly.
Borrow & Return: Clients borrow objects from the pool and return them when
done.
You want to control the number of instances due to system resource limits
(e.g., threads, DB connections).
🧱 Structure
Client --> ObjectPool --> ReusableObject
🧪 Example in Java
class Reusable {
public void doWork() {
[Link]("Using reusable object: " + this);
}
}
class ObjectPool {
private List<Reusable> available = new ArrayList<>();
private List<Reusable> inUse = new ArrayList<>();
Usage:
Reusable r1 = [Link]();
[Link]();
[Link](r1);
📦 Real-World Examples
Database Connection Pooling (e.g., HikariCP, Apache DBCP)
Cons:
Not suitable for objects that are cheap to create or heavily stateful.
✅
Design Patterns Use Cases (Java And Spring) 52
✅ Example: Custom Object Pool in Spring Boot
🔧 Step 1: Create a Reusable Object
package [Link];
import [Link];
import [Link].*;
import [Link];
@Component
public class ReusableObjectPool {
private final List<Reusable> available = new LinkedList<>();
private final Set<Reusable> inUse = new HashSet<>();
import [Link].*;
@RestController
@RequestMapping("/pool")
public class PoolController {
@GetMapping("/acquire")
public String acquire() {
try {
Reusable reusable = [Link]();
[Link]();
return "Acquired object with ID: " + [Link]();
} catch (RuntimeException e) {
return "Pool is full. Try again later.";
}
}
@PostMapping("/release/{id}")
public String release(@PathVariable long id) {
Reusable objToRelease = new Reusable(id);
[Link](objToRelease);
return "Released object with ID: " + id;
}
@GetMapping("/status")
public String status() {
return "Available: " + [Link]()
package [Link];
package [Link];
import [Link];
import [Link];
import [Link];
import [Link];
@Component
public class ConnectionPoolManager {
private static final int MAX_POOL_SIZE = 5;
public ConnectionPoolManager() {
pool = new ConcurrentLinkedQueue<>();
package [Link];
import [Link];
import [Link];
@Service
@RequiredArgsConstructor
public class DatabaseService {
package [Link];
package [Link];
import [Link];
import [Link].*;
import [Link];
@Component
public class WorkerPool {
[Link](worker -> {
[Link](worker);
[Link](worker);
});
}
package [Link];
import [Link];
import [Link];
import [Link].*;
import [Link];
import [Link];
@RestController
@RequestMapping("/api/pool")
public class PoolController {
@GetMapping("/acquire")
public Map<String, Object> acquire() {
Map<String, Object> response = new HashMap<>();
try {
ExpensiveWorker worker = [Link]();
[Link]();
[Link]("message", "Worker acquired successfully");
@PostMapping("/release/{id}")
public Map<String, Object> release(@PathVariable long id) {
[Link](id);
return [Link]("message", "Worker " + id + " released.");
}
@GetMapping("/status")
public Map<String, Object> status() {
return [Link](
"availableCount", [Link](),
"inUseCount", [Link](),
"availableIds", [Link](),
"inUseIds", [Link]()
);
}
}
import [Link];
import [Link];
@SpringBootApplication
public class ObjectPoolApplication {
GET [Link]
Release a worker:
POST [Link]
GET [Link]
6. Prototype Pattern
✅ Real-World Analogy
Imagine you’re designing a document editor where users can duplicate shapes
(like a circle or rectangle) — instead of re-creating the shape every time, you can
clone an existing shape.
@Override
public Shape clone() {
return new Shape(type, x, y); // deep copy
}
3. Client Usage
🧪 Output
Drawing Circle at (10, 20)
Drawing Circle at (30, 40)
@Override
public Shape clone() {
try {
return (Shape) [Link]();
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
⚠️ Note: The Cloneable interface and [Link]() have limitations (e.g., shallow
copy), so many developers prefer implementing custom clone() logic.
package [Link];
import [Link];
import [Link];
public Vehicle() {
[Link] = new ArrayList<>();
}
@Override
public Object clone() throws CloneNotSupportedException {
List<String> temp = new ArrayList<>();
for (String s : [Link]) {
[Link](s);
}
return new Vehicle(temp);
}
package [Link];
import [Link];
package [Link];
import [Link];
import [Link];
@Data
@AllArgsConstructor
public class Car extends AbstractVehicle {
@Override
public AbstractVehicle clone() {
return new Car(vehicleList);
}
}
package [Link];
import [Link];
import [Link];
@Data
public abstract class AbstractVehicle {
private List<String> vehicleList;
protected abstract AbstractVehicle clone();
}
🔧 Java Example
Let's say we have:
class Address {
String city;
Address(String city) {
[Link] = city;
}
}
class Person {
String name;
Address address;
🧪 1. Shallow Copy
Person original = new Person("Sai", new Address("Bangalore"));
Person shallowCopy = new Person([Link], [Link]);
🧪 2. Deep Copy
Person original = new Person("Sai", new Address("Bangalore"));
[Link] = "Hyderabad";
🚀 Summary
Scenario Use Deep Copy?
Behavioural
✅ Use Case
Imagine you are integrating a third-party Media Player into your application, but
your app expects a different interface. Instead of rewriting existing code, you write
an adapter to connect both.
🧩 Structure
Target: The interface your client expects.
Adapter: The class that implements Target and wraps an instance of Adaptee .
2. Adaptee Class
@Override
public void play(String audioType, String fileName) {
if ("vlc".equalsIgnoreCase(audioType)) {
[Link](fileName);
} else if ("mp4".equalsIgnoreCase(audioType)) {
advancedMediaPlayer.playMp4(fileName);
} else {
[Link]("Invalid format: " + audioType);
}
}
}
4. Client Class
@Override
public void play(String audioType, String fileName) {
if ("mp3".equalsIgnoreCase(audioType)) {
[Link]("Playing MP3 file: " + fileName);
} else if ("vlc".equalsIgnoreCase(audioType) || "mp4".equalsIgnoreCase
(audioType)) {
mediaAdapter = new MediaAdapter(audioType);
[Link](audioType, fileName);
} else {
5. Main Method
📦 Output
Playing MP3 file: track.mp3
Playing MP4 file: movie.mp4
Playing VLC file: [Link]
Unsupported format: avi
🎯 Use Cases
Scenario Description
✅ Legacy When working with legacy or third-party code that has a different
integration interface.
✅ Interface When two classes have incompatible interfaces, and you can't
mismatch change their source code.
The Adapter Design Pattern is commonly used in Spring Boot projects when
integrating external systems, legacy APIs, or adapting interfaces to fit domain
models.
Stripe Adapter
@Component("stripeAdapter")
public class StripeAdapter implements PaymentGateway {
private final StripePaymentSDK stripe = new StripePaymentSDK();
@Override
public void pay(double amount) {
[Link](amount);
}
}
Razorpay Adapter
@Component("razorpayAdapter")
public class RazorpayAdapter implements PaymentGateway {
private final RazorpaySDK razorpay = new RazorpaySDK();
@Override
public void pay(double amount) {
[Link](amount * 100); // Convert to paise
}
}
@Autowired
public PaymentService(Map<String, PaymentGateway> gateways) {
[Link] = gateways;
}
@Component("stripeAdapter") and
@Component("razorpayAdapter") will inject the adapters using
a bean map.
5. Controller
@RestController
@RequestMapping("/api/payments")
public class PaymentController {
@Autowired
private PaymentService paymentService;
🧠 Summary
Component Purpose
PaymentGateway Target interface used by Spring app
2. Bridge
🔧 Real-World Motivation
Imagine you're developing a drawing app where you have:
Instead of creating a class for every combination ( VectorCircle , RasterCircle , etc.), you
separate shape logic and rendering logic using the Bridge pattern.
🧱 Structure
Abstraction: High-level interface (e.g., Shape )
1. Implementor Interface
2. Concrete Implementors
3. Abstraction
4. Refined Abstractions
@Override
public void draw() {
[Link]("Circle");
}
}
@Override
public void draw() {
[Link]("Square");
}
}
5. Demo
✅ File format A document can be exported as PDF, Word, HTML using different
exporters export strategies.
✅ Messaging apps Send messages (text, image, video) via various channels (SMS,
Email, Push Notification).
✅ Game Game objects (e.g., units) can be rendered using different graphics
development engines (OpenGL, DirectX).
🔑 When to Use
You want to avoid a combinatorial explosion of classes.
Adapter pattern: You already have a StripeSDK and want to adapt it to your
internal interface PaymentProcessor .
Bridge pattern: You’re designing the system from scratch to support multiple
payment types (Stripe, Razorpay, PayPal) and multiple payment methods
(card, wallet, UPI) with separate hierarchies.
Implementing the Bridge Design Pattern in Spring Boot is useful when you want
to decouple abstraction from implementation, enabling them to vary
independently. This is especially helpful in systems that require flexible
architecture, such as notification systems, file exporters, or multi-database
systems.
🧱 Structure
Abstraction: Notification
2. Concrete Implementors
@Component("emailSender")
public class EmailSender implements MessageSender {
@Override
public void sendMessage(String message) {
[Link]("Sending Email: " + message);
}
}
@Component("smsSender")
public class SMSSender implements MessageSender {
@Override
3. Abstraction
4. Refined Abstractions
@Override
public void notifyUser(String message) {
[Link]("[ALERT] " + message);
}
}
@Override
public void notifyUser(String message) {
[Link]("[MARKETING] " + message);
}
}
@Service
public class NotificationService {
@Autowired
public NotificationService(Map<String, MessageSender> messageSenders)
{
[Link] = messageSenders;
}
Notification notification;
switch (type) {
case "alert":
notification = new AlertNotification(sender);
break;
case "marketing":
notification = new MarketingNotification(sender);
[Link](message);
}
}
6. REST Controller
@RestController
@RequestMapping("/notifications")
public class NotificationController {
@Autowired
public NotificationController(NotificationService notificationService) {
[Link] = notificationService;
}
@PostMapping
public ResponseEntity<String> send(
@RequestParam String type,
@RequestParam String channel,
@RequestParam String message
){
[Link](type, channel, message);
return [Link]("Notification sent.");
}
}
📦
Design Patterns Use Cases (Java And Spring) 85
📦 Sample Request
POST /notifications?type=alert&channel=emailSender&message=Disk+Usage
+High
📄 Report generation Report format (PDF, CSV) vs data source (SQL, NoSQL)
💬 Messaging systems Message type (text, image) vs delivery channel (email, push,
SMS)
🗃️ File storage Storage type (public, private) vs provider (S3, local, Azure)
🔌 Integration layers Business logic vs external APIs (bridge logic & adapter style)
3. Decorator (Wrapper)
The Decorator Design Pattern (also known as the Wrapper Pattern) is a structural
pattern used to dynamically add behavior or responsibilities to an object without
modifying its code. This is especially useful when you want to extend functionality
in a flexible and reusable way, without subclassing.
🧠 Core Concepts
Role Description
Component The original interface or abstract class (e.g., Notifier )
ConcreteComponent The original implementation (e.g., EmailNotifier )
Decorator The base class for decorators that implements Component
ConcreteDecorator Adds additional behavior (e.g., SMSDecorator , SlackDecorator )
✅
Design Patterns Use Cases (Java And Spring) 86
✅ Java Example: Notification System with Decorators
We want to notify users via email, but also allow optional SMS and Slack
notifications without altering the original notifier.
2. EmailNotifier (ConcreteComponent)
@Override
public void send(String message) {
[Link](message);
@Override
public void send(String message) {
[Link](message);
[Link]("Sending SMS: " + message);
}
}
@Override
public void send(String message) {
[Link](message);
[Link]("Sending Slack Message: " + message);
}
}
5. Demo Usage
[Link]("Server is down!");
}
}
🔁 Output
Sending Email: Server is down!
Sending SMS: Server is down!
Sending Slack Message: Server is down!
Spring Boot Filters Servlet filters and interceptors act like decorators
@Component
@Primary
@Override
public void createUser(String name) {
[Link]("Logging: Creating user " + name);
[Link](name);
}
}
✅ Summary
Decorator = Wrapper + Behavior
In Spring Boot, the Decorator Design Pattern is often used to enhance or modify
the behavior of services without changing the original implementation. This is
typically done using delegation, bean overriding, and proxies.
2. Core Implementation
@Component("baseNotificationService")
public class EmailNotificationService implements NotificationService {
@Override
public void send(String message) {
[Link](" 📧 Sending Email: " + message);
}
}
3. Logging Decorator
@Component
@Primary // This will override the base implementation
public class LoggingNotificationDecorator implements NotificationService {
public LoggingNotificationDecorator(@Qualifier("baseNotificationService")
NotificationService delegate) {
[Link] = delegate;
}
@Override
public void send(String message) {
[Link](" 📝Logging: About to send message");
[Link](message);
[Link]("📝 Logging: Message sent");
@Component("retryingNotificationService")
public class RetryNotificationDecorator implements NotificationService {
public RetryNotificationDecorator(@Qualifier("loggingNotificationDecorato
r") NotificationService delegate) {
[Link] = delegate;
}
@Override
public void send(String message) {
int attempts = 0;
boolean success = false;
if (!success) {
[Link](" ❌ Failed to send message after 3 attempts");
}
You can wire this retry decorator manually into a config or use it directly.
@RestController
@RequiredArgsConstructor
public class NotificationController {
@PostMapping("/send")
public ResponseEntity<String> sendNotification(@RequestParam String me
ssage) {
[Link](message);
return [Link]("Notification processed");
}
}
🧠 Notes
Concept Usage
✅ Benefits
Follows Open/Closed Principle (no changes to the core class).
@Configuration
public class NotificationConfig {
@Bean
public NotificationService notificationService() {
return new LoggingNotificationDecorator(
new RetryNotificationDecorator(
new EmailNotificationService()
)
);
}
}
4. Composite
🧠 Key Intent
"Compose objects into tree structures to represent part-whole
hierarchies. Composite lets clients treat individual objects and
compositions uniformly."
🧩 Real-World Analogy
Think of a file system:
A Directory can contain both files and subdirectories (which can contain more).
2. Leaf: File
@Override
public void showDetails(String indent) {
[Link](indent + "- File: " + name);
}
}
3. Composite: Directory
import [Link];
import [Link];
@Override
public void showDetails(String indent) {
[Link](indent + "+ Directory: " + name);
for (FileSystemComponent component : children) {
[Link](indent + " ");
}
package [Link];
import [Link];
import [Link];
4. Usage
🔄 Output
+ Directory: Work
- File: [Link]
+ Directory: Documents
- File: [Link]
- File: [Link]
🤖 Benefits
Treat individual and composite objects uniformly.
⚠️ Drawbacks
Can make the design overly general.
🔷Departments)
Use Case Example: Organization Structure (Employees and
Department is a composite.
✅ Final Structure
composite-pattern-springboot/
│
├── controller/
│ └── [Link]
├── model/
│ ├── [Link]
Step-by-Step Guide
📁 [Link] (Component)
package [Link];
📁 [Link] (Leaf)
package [Link];
@Override
public void showDetails() {
[Link]("Employee: " + name + ", Role: " + role);
}
}
📁 [Link] (Composite)
package [Link];
import [Link];
import [Link];
@Override
public void showDetails() {
[Link]("Department: " + name);
for (OrgComponent component : components) {
📁 [Link]
package [Link];
import [Link];
import [Link];
import [Link];
import [Link];
@Service
public class OrgService {
return headOffice;
📁 [Link]
package [Link];
import [Link];
import [Link];
import [Link];
import [Link];
@RestController
public class OrgController {
@GetMapping("/org")
public String getOrgStructure() {
OrgComponent root = [Link]();
[Link](); // You could alternatively return a custom DTO
return "Organization structure printed in logs.";
}
}
📁 [Link]
package [Link];
import [Link];
@SpringBootApplication
public class CompositePatternApplication {
public static void main(String[] args) {
[Link]([Link], args);
}
}
🧠 Summary
Pattern Used: Composite Pattern
Leaf: Employee
Composite: Department
5. Filter (Criteria)
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
[Link]("Males:");
printPersons([Link](persons));
[Link]("\nSingle Males:");
printPersons([Link](persons));
[Link]("\nSingle Or Females:");
printPersons([Link](persons));
}
Output
Males:
Person: [ Name: Robert, Gender: Male, Marital Status: Single ]
Person: [ Name: John, Gender: Male, Marital Status: Married ]
Person: [ Name: Mike, Gender: Male, Marital Status: Single ]
Person: [ Name: Bobby, Gender: Male, Marital Status: Single ]
Single Males:
Person: [ Name: Robert, Gender: Male, Marital Status: Single ]
Person: [ Name: Mike, Gender: Male, Marital Status: Single ]
Person: [ Name: Bobby, Gender: Male, Marital Status: Single ]
6. Proxy
The Proxy Design Pattern provides a surrogate or placeholder for another object
to control access to it. In Java, this is often used for:
✅ Real-world Analogy
Think of a bank ATM as a proxy to your bank account — the ATM controls access
and enforces authentication before you interact with your actual account.
🔧 Structure
Subject (interface)
│
├── RealSubject (RealService)
└── Proxy (controls access to RealSubject)
import [Link];
import [Link];
@Override
public void connectTo(String serverHost) throws Exception {
if ([Link]([Link]())) {
throw new Exception("Access Denied to " + serverHost);
}
[Link](serverHost);
try {
[Link]("[Link]");
[Link]("[Link]");
} catch (Exception e) {
[Link]([Link]());
}
}
}
🧾 Output
Connecting to [Link]
Access Denied to [Link]
🔍 When to Use
Scenario Proxy Type
🧠
Design Patterns Use Cases (Java And Spring) 112
🧠 Tip
Java's built-in [Link] and Spring AOP (like @Transactional ) also use
dynamic proxies.
🔧 Project Structure
proxy-pattern-springboot/
├── [Link]
├── service/
│ ├── [Link] (interface)
│ ├── [Link] (actual service)
│ └── [Link] (proxy)
└── controller/
└── [Link]
1. 🎬 [Link] (Interface)
package [Link];
📺
Design Patterns Use Cases (Java And Spring) 113
2. 📺 [Link] (Actual Service)
package [Link];
import [Link];
@Service("realVideoService")
public class RealVideoService implements VideoService {
@Override
public String streamVideo(String title) {
return "Streaming video: " + title;
}
}
3. 🔍 [Link] (Proxy)
package [Link];
import [Link];
import [Link];
@Service
public class LoggingProxyVideoService implements VideoService {
@Override
public String streamVideo(String title) {
4. 🎮 [Link]
package [Link];
import [Link];
import [Link].*;
@RestController
@RequestMapping("/video")
public class VideoController {
@GetMapping("/watch")
public String watchVideo(@RequestParam String title) {
return [Link](title);
}
}
5. 🚀 [Link]
import [Link];
import [Link];
@SpringBootApplication
public class ProxyPatternApplication {
public static void main(String[] args) {
[Link]([Link], args);
}
}
Terminal Log:
API Response:
🧠 Notes
We're injecting the proxy as the main bean ( LoggingProxyVideoService ) using
Spring's @Qualifier and constructor injection.
You can enhance the proxy to add auth, rate-limiting, caching, or circuit
breaking.
💡
Design Patterns Use Cases (Java And Spring) 116
💡 Alternatives in Spring
Feature Proxy Tool
🔧 Project Structure
proxy-pattern-springboot/
├── [Link]
├── service/
│ ├── [Link] (interface)
│ ├── [Link] (actual service)
│ └── [Link] (proxy)
└── controller/
└── [Link]
1. 🎬 [Link] (Interface)
package [Link];
java
CopyEdit
package [Link];
import [Link];
@Service("realVideoService")
public class RealVideoService implements VideoService {
@Override
public String streamVideo(String title) {
return "Streaming video: " + title;
}
}
3. 🔍 [Link] (Proxy)
java
CopyEdit
package [Link];
import [Link];
import [Link];
@Service
public class LoggingProxyVideoService implements VideoService {
@Override
public String streamVideo(String title) {
[Link]("[LOG] Requesting video: " + title);
String result = [Link](title);
[Link]("[LOG] Finished streaming: " + title);
return result;
}
}
4. 🎮 [Link]
java
CopyEdit
package [Link];
import [Link];
import [Link].*;
@RestController
@RequestMapping("/video")
public class VideoController {
5. 🚀 [Link]
java
CopyEdit
package [Link];
import [Link];
import [Link];
@SpringBootApplication
public class ProxyPatternApplication {
public static void main(String[] args) {
[Link]([Link], args);
}
}
Terminal Log:
API Response:
css
CopyEdit
Streaming video: Inception
🧠 Notes
We're injecting the proxy as the main bean ( LoggingProxyVideoService ) using
Spring's @Qualifier and constructor injection.
You can enhance the proxy to add auth, rate-limiting, caching, or circuit
breaking.
💡 Alternatives in Spring
Feature Proxy Tool
Would you like to see an AOP-based proxy as well (even more idiomatic in
Spring)?
You said:
ChatGPT said:
Here's how to implement the Proxy Design Pattern using Spring Framework
(without Spring Boot if preferred, or within Boot if needed), to intercept and
control access to a real service.
📁 Structure
php
CopyEdit
[Link]
├── [Link] (interface)
├── [Link] (real object)
├── [Link] (proxy object)
└── [Link] (Spring config)
java
CopyEdit
public interface Internet {
void connectTo(String url) throws Exception;
java
CopyEdit
import [Link];
@Component("realInternet")
public class RealInternet implements Internet {
@Override
public void connectTo(String url) {
[Link]("Connected to " + url);
}
}
java
CopyEdit
import [Link];
import [Link];
import [Link];
@Component
public class ProxyInternet implements Internet {
@Override
public void connectTo(String url) throws Exception {
if ([Link]([Link]())) {
throw new Exception("Access Denied to " + url);
}
[Link](url);
}
}
java
CopyEdit
import [Link]
ontext;
import [Link];
import [Link];
@Configuration
@ComponentScan("[Link]")
public class AppConfig {
public static void main(String[] args) {
var context = new AnnotationConfigApplicationContext([Link]
s);
Internet internet = [Link]([Link]);
try {
[Link]("[Link]");
[Link]("[Link]");
} catch (Exception e) {
[Link]();
}
}
✅ Output
bash
CopyEdit
Connected to [Link]
Access Denied to [Link]
✅largeApproach
apps)
2: Spring AOP-Based Proxy (Preferred in
xml
CopyEdit
<dependency>
<groupId>[Link]</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
java
CopyEdit
@Service
public class VideoService {
public void stream(String title) {
[Link]("Streaming video: " + title);
}
}
java
CopyEdit
@Aspect
@Component
public class LoggingAspect {
@Before("execution(* [Link](..))")
public void logBefore(JoinPoint joinPoint) {
[Link]("[LOG] Starting: " + [Link]().getName
());
}
@After("execution(* [Link](..))")
public void logAfter(JoinPoint joinPoint) {
[Link]("[LOG] Finished: " + [Link]().getName
());
}
}
java
CopyEdit
@RestController
public class VideoController {
private final VideoService videoService;
@GetMapping("/watch")
public String watch(@RequestParam String title) {
[Link](title);
return "OK";
}
}
✅ Output
less
CopyEdit
[LOG] Starting: stream
Streaming video: Avatar
[LOG] Finished: stream
🔚 Summary
Approach Use Case
✅ Intent
Use sharing to support a large number of fine-grained objects
efficiently.
🔧 When to Use
You have many objects that consume a lot of memory.
Most object data is intrinsic (shared) and only a small part is extrinsic
(context-specific).
You want to reuse objects instead of creating new ones each time.
🧠 Key Concepts
Flyweight: The shared object.
Game Trees (chess, go) Reuse similar game pieces instead of creating new ones.
🛠️ Java Implementation
Scenario: Drawing multiple circles with only a few shared colors.
@Override
public void draw(int x, int y) {
import [Link];
import [Link];
if (circle == null) {
circle = new Circle(color);
[Link](color, circle);
[Link]("Created circle of color: " + color);
}
return circle;
}
}
✅ Output Example
Created circle of color: Red
Drawing Red circle at (22, 11)
Created circle of color: Green
Drawing Green circle at (55, 91)
Created circle of color: Blue
Drawing Blue circle at (72, 34)
Drawing Red circle at (99, 67)
...
💡 Summary
Element Role in Flyweight
Shape Flyweight interface
Circle Concrete Flyweight
ShapeFactory Creates/reuses Flyweights
x, y Extrinsic data
color Intrinsic (shared) data
🏁 Benefits
✅ Reduces memory usage
✅ Improves performance for large datasets
✅ Encourages sharing and reuse
📁 Project Structure
[Link]
├── [Link] // Flyweight interface
├── [Link] // Concrete Flyweight
├── [Link] // Flyweight factory using Spring
├── [Link] // Client
├── [Link] // Main class (SpringBootApplication)
import [Link];
import [Link];
@Component
@Scope("prototype") // Ensure different beans for different icons, if needed
public class SharedIcon implements Icon {
@Override
public void draw(int x, int y) {
[Link]("Drawing '%s' icon at (%d, %d)%n", type, x, y);
}
}
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
@Component
public class IconFactory {
@Autowired
private ApplicationContext context;
java
CopyEdit
import [Link];
import [Link];
@Service
public class IconUsageService {
@Autowired
private IconFactory iconFactory;
import [Link];
import [Link];
@SpringBootApplication
public class FlyweightApp implements CommandLineRunner {
@Override
public void run(String... args) {
[Link]();
}
}
✅ Output
Created new icon of type: folder
Drawing 'folder' icon at (0, 0)
Created new icon of type: file
Drawing 'file' icon at (10, 5)
Created new icon of type: trash
Drawing 'trash' icon at (20, 10)
Drawing 'folder' icon at (30, 15)
Drawing 'file' icon at (40, 20)
...
📚
Design Patterns Use Cases (Java And Spring) 135
📚 Summary
Component Role
Icon Flyweight interface
SharedIcon Intrinsic state (shared icon)
IconFactory Flyweight manager
IconUsageService Client using icons
✅ Goal
We'll simulate a UI icon system where the icon’s shape and type are intrinsic
(shared) and position is extrinsic (context-specific). We'll use Spring to manage
and share the flyweight beans.
📁 Project Structure
[Link]
├── [Link] // Flyweight interface
├── [Link] // Concrete Flyweight
├── [Link] // Flyweight factory using Spring
├── [Link] // Client
├── [Link] // Main class (SpringBootApplication)
import [Link];
import [Link];
@Component
@Scope("prototype") // Ensure different beans for different icons, if needed
public class SharedIcon implements Icon {
@Override
public void draw(int x, int y) {
[Link]("Drawing '%s' icon at (%d, %d)%n", type, x, y);
}
}
import [Link];
import [Link];
@Component
public class IconFactory {
@Autowired
private ApplicationContext context;
import [Link];
import [Link];
@Service
public class IconUsageService {
import [Link];
import [Link];
import [Link];
@SpringBootApplication
public class FlyweightApp implements CommandLineRunner {
@Override
public void run(String... args) {
✅ Output
Created new icon of type: folder
Drawing 'folder' icon at (0, 0)
Created new icon of type: file
Drawing 'file' icon at (10, 5)
Created new icon of type: trash
Drawing 'trash' icon at (20, 10)
Drawing 'folder' icon at (30, 15)
Drawing 'file' icon at (40, 20)
...
📚 Summary
Component Role
Icon Flyweight interface
SharedIcon Intrinsic state (shared icon)
IconFactory Flyweight manager
IconUsageService Client using icons
✅ Updated [Link]
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
@Component
public class IconFactory {
@Autowired
private ApplicationContext context;
✅ Updated [Link]
We'll add a call to printStats() at the end of drawUI() .
import [Link];
import [Link];
@Service
public class IconUsageService {
@Autowired
private IconFactory iconFactory;
✅ Sample Output
Created new icon of type: folder
Drawing 'folder' icon at (0, 0)
Created new icon of type: file
Drawing 'file' icon at (10, 5)
Created new icon of type: trash
Drawing 'trash' icon at (20, 10)
Reused icon of type: folder
Drawing 'folder' icon at (30, 15)
...
✅ Goal
Create a REST controller with:
🔧 Step-by-Step Implementation
✅ 1. Add [Link] DTO
✅ 3. Create [Link]
import [Link];
import [Link].*;
@RestController
@RequestMapping("/icons")
public class IconController {
@Autowired
private IconFactory iconFactory;
@Autowired
@GetMapping("/stats")
public IconStatsResponse getStats() {
return new IconStatsResponse(
[Link](),
[Link](),
[Link]()
);
}
@PostMapping("/draw-ui")
public String drawUI() {
[Link]();
return "UI drawn with icons. Check /icons/stats for metrics.";
}
}
🧪 Example Requests
➤ Simulate Drawing
POST /icons/draw-ui
Response:
Response:
✅ Optional Enhancements
Feature Add-on
10. Facade
package [Link];
import [Link];
@Service
public class InventoryService {
public boolean checkStock(String productId) {
[Link]("Checking stock for: " + productId);
return true; // assume in stock
}
}
[Link]
package [Link];
import [Link];
[Link]
package [Link];
import [Link];
@Service
public class ShippingService {
public void shipProduct(String productId, String userId) {
[Link]("Shipping " + productId + " to user: " + userId);
}
}
package [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
@Autowired
private InventoryService inventoryService;
@Autowired
private PaymentService paymentService;
@Autowired
private ShippingService shippingService;
if () {
return "Payment failed!";
}
[Link](productId, userId);
return "Order placed successfully!";
}
}
package [Link];
import [Link];
import [Link];
import [Link].*;
@Autowired
private OrderFacade orderFacade;
@PostMapping("/place")
public String placeOrder(@RequestParam String productId,
@RequestParam String userId,
@RequestParam double amount) {
return [Link](productId, userId, amount);
}
}
Output:
✅ Summary
Layer Role
OrderController Accepts request from user
OrderFacade Simplifies interactions with subsystems
[Link]
[Link]
public OrderFacade() {
[Link] = new InventoryService();
[Link] = new PaymentService();
[Link] = new ShippingService();
}
if () {
[Link](productId, userId);
return "Order placed successfully!";
}
}
✅ Output
Cecking stock for: P123
Processing payment of $99.99 for user: U456
Shipping product P123 to user: U456
Order placed successfully!
✅ Summary
Class Responsibility
InventoryService Checks product availability
Structural
1. Chain Of Responsibility
The Chain of Responsibility (CoR) design pattern allows you to pass a request
along a chain of handlers until one of them handles it. It’s useful when multiple
objects can handle a request and you want to decouple the sender and receiver.
Authentication filters
Each logger decides if it can handle the log level; if not, it passes it forward.
📁
Design Patterns Use Cases (Java And Spring) 155
📁 File Structure
src/
├── [Link] (abstract handler)
├── [Link] (concrete handler)
├── [Link]
├── [Link]
└── [Link] (to run the chain)
✅ 2. Concrete Loggers
[Link]
@Override
protected void write(String message) {
[Link]("[DEBUG]: " + message);
}
}
[Link]
@Override
protected void write(String message) {
[Link]("[INFO]: " + message);
}
}
[Link]
@Override
protected void write(String message) {
[Link]("[ERROR]: " + message);
}
}
[Link](infoLogger);
[Link](errorLogger);
return debugLogger;
}
✅ Output
[DEBUG]: Debugging application.
[INFO]: Information message.
[ERROR]: An error occurred!
Each message is passed through the chain; each handler decides whether to act.
🧠 Summary
Concept Description
The Chain of Responsibility (CoR) design pattern allows you to pass a request
along a chain of handlers until one of them handles it. It’s useful when multiple
objects can handle a request and you want to decouple the sender and receiver.
Authentication filters
Each logger decides if it can handle the log level; if not, it passes it forward.
📁 File Structure
src/
├── [Link] (abstract handler)
├── [Link] (concrete handler)
├── [Link]
├── [Link]
└── [Link] (to run the chain)
✅ 2. Concrete Loggers
[Link]
@Override
protected void write(String message) {
[Link]("[DEBUG]: " + message);
}
}
[Link]
java
CopyEdit
public class InfoLogger extends Logger {
public InfoLogger() {
[Link] = INFO;
@Override
protected void write(String message) {
[Link]("[INFO]: " + message);
}
}
[Link]
java
CopyEdit
public class ErrorLogger extends Logger {
public ErrorLogger() {
[Link] = ERROR;
}
@Override
protected void write(String message) {
[Link]("[ERROR]: " + message);
}
}
java
CopyEdit
public class Main {
private static Logger getLoggerChain() {
Logger errorLogger = new ErrorLogger();
[Link](infoLogger);
[Link](errorLogger);
return debugLogger;
}
✅ Output
markdown
CopyEdit
[DEBUG]: Debugging application.
[INFO]: Information message.
[ERROR]: An error occurred!
Each message is passed through the chain; each handler decides whether to act.
🧠 Summary
Concept Description
Would you like to see this adapted for a Spring Boot-based middleware or
validator chain example?
You said:
use spring boot
ChatGPT said:
Let’s implement the Chain of Responsibility pattern in Spring Boot with a real-
world scenario.
📁 Project Structure
src/
├── chain/
│ ├── [Link]
│ ├── [Link]
│ ├── [Link]
│ ├── [Link]
├── controller/
│ └── [Link]
package [Link];
import [Link];
✅ 2. Create Validators
[Link]
[Link]
@Component
public class PasswordValidator extends RequestValidator {
@Override
protected void handle(RegisterRequest request) {
if ([Link]() == null || [Link]().length() < 6) {
throw new RuntimeException("Password too short");
}
}
}
[Link]
@Component
public class UserExistValidator extends RequestValidator {
@Override
protected void handle(RegisterRequest request) {
if ("existing@[Link]".equals([Link]())) {
throw new RuntimeException("User already exists");
}
}
}
✅
Design Patterns Use Cases (Java And Spring) 166
✅ 3. Create RegisterRequest DTO
package [Link];
@PostMapping("/register")
public ResponseEntity<String> register(@RequestBody RegisterRequest re
quest) {
[Link](request);
return [Link]("Registration valid");
}
}
✅ 5. Main Application
@SpringBootApplication
public class SpringBootApp {
public static void main(String[] args) {
[Link]([Link], args);
}
}
🔁 Chain Flow
If email is invalid → throws
Else → goes to password check
🧠 Summary
Concept Applied With
Start/stop a fan
Schedule commands
Support undo
Invoker RemoteControl
✅ Java Implementation
1. Command Interface
[Link]
[Link]
[Link]
[Link]
4. Invoker: RemoteControl
5. Client: [Link]
[Link](lightOn);
[Link](); // Light is ON
[Link](); // Light is OFF
[Link](fanStart);
[Link](); // Fan started
[Link](); // Fan stopped
}
}
✅ Output
Light is ON
Light is OFF
Fan started
Fan stopped
Decouples sender from receiver Remote doesn’t need to know device internals
✅ Summary
Use Command pattern when you need to encapsulate operations or actions
as objects.
Start/stop fan
📦 Structure Overview
src/
├── command/
│ ├── [Link]
│ ├── [Link]
│ ├── [Link]
│ ├── [Link]
│ ├── [Link]
│ └── [Link]
├── devices/
✅ 1. Command Interface
package [Link];
package [Link];
import [Link];
@Component
public class Light {
public String turnOn() {
return "Light turned ON";
}
[Link]
package [Link];
import [Link];
@Component
public class Fan {
public String start() {
return "Fan started";
}
✅ 3. Concrete Commands
[Link]
package [Link];
import [Link];
package [Link];
import [Link];
@Override
public String execute() {
return [Link]();
}
}
✅ 4. [Link] – Invoker
package [Link];
import [Link];
@Component
✅ 5. [Link] – DTO
package [Link];
✅ 6. [Link]
package [Link];
import [Link].*;
import [Link];
import [Link];
import [Link];
import [Link].*;
@RestController
@PostMapping("/command")
public String executeCommand(@RequestBody CommandRequest request)
{
Command command;
switch ([Link]().toLowerCase()) {
case "light":
command = [Link]().equalsIgnoreCase("on")
? new LightOnCommand(light)
: new LightOffCommand(light);
break;
case "fan":
command = [Link]().equalsIgnoreCase("start")
? new FanStartCommand(fan)
: new FanStopCommand(fan);
break;
default:
return "Invalid device";
}
return [Link](command);
✅ 7. [Link]
package [Link];
import [Link];
import [Link];
@SpringBootApplication
public class SpringBootApp {
public static void main(String[] args) {
[Link]([Link], args);
}
}
💡 Output:
Light turned ON
Fan STOP
💨 Output:
Fan stopped
🧠 Summary
Component Role
Command Encapsulates a request
To integrate a Queue with the Command Design Pattern in Spring Boot, you can
enhance the CommandExecutor to enqueue and process commands asynchronously
or in batch — like a task scheduler, job queue, or command dispatcher.
1. Queue commands.
✅ Step-by-Step Enhancements
Design Patterns Use Cases (Java And Spring) 181
🔁 1. Modify CommandExecutor to Queue Commands
package [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
@Component
public class CommandExecutor {
@PostConstruct
public void startExecutionLoop() {
// Start a background thread to execute commands
new Thread(this::processCommands).start();
}
if (command != null) {
String result = [Link]();
[Link]("[Executed] " + result);
}
}
}
}
⚙️Immediately
2. Modify Controller to Enqueue Instead of Execute
@PostMapping("/command")
public String enqueueCommand(@RequestBody CommandRequest request) {
Command command;
switch ([Link]().toLowerCase()) {
case "light":
command = [Link]().equalsIgnoreCase("on")
? new LightOnCommand(light)
: new LightOffCommand(light);
break;
case "fan":
command = [Link]().equalsIgnoreCase("start")
? new FanStartCommand(fan)
: new FanStopCommand(fan);
break;
default:
[Link](command);
return "Command enqueued: " + [Link]() + " " + [Link]
ion();
}
🧪 Sample Output
When you call the endpoint:
Console logs:
[Link]
@SpringBootApplication
@EnableAsync
public class SpringBootApp { ... }
Then modify processCommands() to use @Async methods if you want parallel execution
(not FIFO).
🧠
Design Patterns Use Cases (Java And Spring) 184
🧠 Summary: Why Use Queue in Command Pattern?
Benefit Description
3. Interator
The Iterator pattern allows you to traverse a collection (like a list, set, or custom
data structure) one element at a time, without knowing its internal structure.
📦 Key Components
1. Iterator Interface – defines the methods for accessing elements ( hasNext() ,
next() ).
✅ Java Example
Step 1: Iterator Interface
@Override
public Iterator<String> createIterator() {
return new NameIterator();
}
@Override
public boolean hasNext() {
return index < [Link];
}
@Override
public String next() {
return hasNext() ? names[index++] : null;
}
}
}
while ([Link]()) {
[Link]([Link]());
}
}
}
🧠 Use Cases
Custom collection frameworks – when creating your own data structures.
Traversal abstraction – when you want to hide the internal structure (tree,
graph, etc.).
✅ Advantages
Promotes encapsulation by hiding collection internals.
Let’s walk through a realistic Spring Boot example where we simulate iterating
over a list of users from a database using a custom iterator.
@Entity
public class User {
@Id
@GeneratedValue(strategy = [Link])
private Long id;
@Override
public boolean hasNext() {
return index < [Link]();
}
@Override
public User next() {
return hasNext() ? [Link](index++) : null;
}
}
}
@Autowired
private UserRepository userRepository;
while ([Link]()) {
User user = [Link]();
[Link]("Processing user: " + [Link]());
}
}
}
@Autowired
private UserService userService;
@GetMapping("/process")
public String processUsers() {
[Link]();
return "User processing complete!";
4. Intepreter
📚 Definition
The Interpreter Pattern defines a representation for a grammar
and provides an interpreter to deal with this grammar. It is used
when we want to evaluate language expressions.
⚙️ Use Case
Arithmetic expression evaluators ( 2 + 3 * 4 )
Rule engines
SQL parsing
Mini-language processing
Search filters
@Override
public int interpret() {
return number;
@Override
public int interpret() {
return [Link]() + [Link]();
}
}
@Override
public int interpret() {
return [Link]() - [Link]();
}
}
Custom Query Filters "price > 100 AND stock < 50"
❌ Cons
Becomes complex for large grammars
5. Mediator
🧱 Key Components
Component Description
Mediator Interface that defines communication methods
ConcreteMediator Implements the mediator and coordinates interactions
Colleague Abstract class/component that interacts via the mediator
ConcreteColleague Implements behavior and delegates communication to mediator
✅
Design Patterns Use Cases (Java And Spring) 195
✅ Java Implementation Example
🎯central
Use Case: Chatroom where users send messages through a
mediator
import [Link];
import [Link];
@Override
public void sendMessage(String message, User sender) {
for (User user : users) {
if (user != sender) {
[Link](message);
}
}
}
@Override
public void addUser(User user) {
[Link](user);
}
}
@Override
public void send(String message) {
[Link](name + " sends: " + message);
[Link](message, this);
}
@Override
public void receive(String message) {
[Link](name + " received: " + message);
[Link](user1);
[Link](user2);
[Link](user3);
[Link]("Hello everyone!");
}
}
🧪 Output
Alice sends: Hello everyone!
Bob received: Hello everyone!
Charlie received: Hello everyone!
✅ Benefits
Reduces coupling between components
❌ Drawbacks
Mediator can become too complex and turn into a "God Object" if not handled
carefully
🧱 Project Structure
src/
└─ main/
└─ java/
└─ com/example/mediator/
import [Link];
import [Link];
import [Link];
@Service
public class EmailService {
public void sendConfirmation(Order order) {
[Link]("Email sent to customer for Order: " + [Link]());
package [Link];
import [Link];
import [Link];
@Service
public class InventoryService {
public void updateStock(Order order) {
[Link]("Inventory updated for Product: " + [Link]
());
}
}
package [Link];
import [Link];
import [Link];
@Service
public class ShippingService {
public void prepareShipment(Order order) {
[Link]("Shipment prepared for Order: " + [Link]());
}
}
// Constructor, getters
public Order(String id, String product) {
[Link] = id;
[Link] = product;
}
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
@Component
public class OrderMediator implements Mediator {
@Override
public void notifyServices(Order order) {
[Link](order);
[Link](order);
[Link](order);
}
}
import [Link];
import [Link];
import [Link].*;
@RestController
@RequestMapping("/orders")
public class OrderController {
@PostMapping("/create")
public String createOrder(@RequestParam String id, @RequestParam String
product) {
Order order = new Order(id, product);
[Link](order);
import [Link];
import [Link];
@SpringBootApplication
public class MediatorApplication {
public static void main(String[] args) {
[Link]([Link], args);
}
}
✅ Intent
“Without exposing the object’s internal structure, you can save
and restore its state.
🧱 Key Components
Role Description
Originator The object whose state needs to be saved and restored
Memento Stores the internal state of the Originator ; it’s immutable to others
🔹 3. Caretaker (History)
import [Link];
🔹 4. Client (Demo)
public class MementoPatternDemo {
public static void main(String[] args) {
TextEditor editor = new TextEditor();
EditorHistory history = new EditorHistory();
[Link]("Hello ");
[Link](editor); // Save 1
[Link]("World!");
[Link](editor); // Save 2
[Link](editor);
[Link]("After Undo 1: " + [Link]());
[Link](editor);
[Link]("After Undo 2: " + [Link]());
✅ Output:
Current Content: Hello World! This will be undone.
After Undo 1: Hello World!
After Undo 2: Hello
Database transactions Rollback logic where a memento stores object state before
(manual) a transaction
✅ Benefits
Preserves encapsulation
❌ Drawbacks
Can consume a lot of memory if many states are saved
✅
Design Patterns Use Cases (Java And Spring) 208
✅ Project Structure
memento-spring-boot/
├── controller/
│ └── [Link]
├── model/
│ └── [Link]
├── service/
│ ├── [Link]
│ └── [Link]
└── [Link]
🔹 1. [Link] (Model)
package [Link];
🔹 2. [Link] (Originator)
import [Link];
import [Link];
@Service
public class EditorService {
private String content = "";
🔹 3. [Link] (Caretaker)
package [Link];
import [Link];
import [Link];
@Service
public class HistoryService {
private final Stack<Memento> history = new Stack<>();
🔹 4. [Link]
import [Link];
import [Link];
import [Link];
import [Link].*;
@RestController
@RequestMapping("/editor")
public class EditorController {
@PostMapping("/type")
public String type(@RequestParam String text) {
[Link](text);
return "Typed: " + text;
}
@PostMapping("/save")
public String save() {
[Link]([Link]());
return "State saved.";
}
@PostMapping("/undo")
public String undo() {
if ([Link]()) {
[Link]([Link]());
return "Undo successful.";
}
return "No history to undo.";
}
@GetMapping("/content")
public String content() {
return [Link]();
}
}
🔹
Design Patterns Use Cases (Java And Spring) 212
🔹 5. [Link] (Main App)
package [Link];
import [Link];
import [Link];
@SpringBootApplication
public class MementoApplication {
public static void main(String[] args) {
[Link]([Link], args);
}
}
2. POST /editor/save
3. POST /editor/type?text=World!
5. POST /editor/undo
<dependencies>
<dependency>
<groupId>[Link]</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
✅
Design Patterns Use Cases (Java And Spring) 213
✅ Summary
Component Pattern Role
EditorService Originator
Memento Memento
HistoryService Caretaker
EditorController Client / API
7. Null State
The Null Object Design Pattern provides an object as a surrogate for the absence
of a real object. Instead of returning null , return a special object that implements
the expected interface but does nothing (a “do-nothing” implementation). This
helps eliminate null checks and NullPointerExceptions .
✅ When to Use
To avoid null checks and reduce conditional logic.
✅ Structure
Abstract Class / Interface: Declares the common operations.
Null Object: Implements the same interface with empty or default behavior.
@Override
public String getName() {
return name;
}
@Override
public boolean isNull() {
return false;
}
}
@Override
public boolean isNull() {
return true;
}
}
🔹 4. CustomerFactory
public class CustomerFactory {
private static final String[] names = {"Alice", "Bob", "Charlie"};
🔹 5. Client Code
public class Main {
public static void main(String[] args) {
Customer c1 = [Link]("Bob");
Customer c2 = [Link]("Unknown");
8. Observer
The Observer Design Pattern is a behavioral design pattern that defines a one-
to-many dependency between objects so that when one object (the Subject)
changes state, all its dependents (Observers) are notified and updated
automatically.
✅
Design Patterns Use Cases (Java And Spring) 217
✅ Key Components
Role Description
✅ Real-world Analogy
A YouTube Channel (Subject) notifies all its Subscribers (Observers) when a new
video is uploaded.
🔹 1. [Link] (Interface)
🔹 2. [Link] (Interface)
🔹 3. [Link] (ConcreteSubject)
@Override
public void subscribe(Observer observer) {
[Link](observer);
}
@Override
public void unsubscribe(Observer observer) {
[Link](observer);
}
@Override
public void notifyObservers(String message) {
for (Observer observer : observers) {
[Link](message);
}
}
🔹 4. [Link] (ConcreteObserver)
@Override
public void update(String message) {
[Link](name + " received update: " + message);
}
}
🔹 5. Main Class
public class Main {
public static void main(String[] args) {
NewsAgency agency = new NewsAgency();
[Link](alice);
[Link](bob);
✅ Output
Breaking News: Observer pattern rocks!
Alice received update: Breaking News: Observer pattern rocks!
Bob received update: Breaking News: Observer pattern rocks!
✅ Use Cases
Use Case Description
Here’s how you can implement the Observer Design Pattern using Spring Boot,
simulating a news publishing system where:
✅ Overview
We'll build:
Register subscribers
Publish news
🔹 2. [Link]
package [Link];
🔹 3. [Link]
package [Link];
@Override
public void update(String message) {
[Link]("[" + name + "] received: " + message);
}
🔹 4. [Link]
package [Link];
import [Link];
import [Link];
import [Link];
@Component
public class NewsAgency {
🔹 5. [Link]
import [Link];
import [Link].*;
import [Link];
@RestController
@RequestMapping("/news")
public class NewsController {
@PostMapping("/subscribe")
public String subscribe(@RequestParam String name) {
[Link](new Subscriber(name));
return "Subscribed: " + name;
}
@DeleteMapping("/unsubscribe")
public String unsubscribe(@RequestParam String name) {
[Link](name);
return "Unsubscribed: " + name;
}
@GetMapping("/subscribers")
public List<String> getSubscribers() {
return [Link]();
}
}
🔹 6. [Link]
package [Link];
import [Link];
import [Link];
@SpringBootApplication
public class ObserverApplication {
public static void main(String[] args) {
[Link]([Link], args);
}
}
# Unsubscribe
curl -X DELETE "[Link]
9. State
The State Design Pattern is a behavioral pattern that allows an object to change
its behavior when its internal state changes. It appears as if the object changed
its class.
✅ Intent
Encapsulate state-based behavior and delegate to the current state object.
✅ Real-world Analogy
Consider a Vending Machine:
✅ Key Participants
Role Description
State (interface) Defines the interface for behavior associated with a state of Context
🔹 2. [Link]
@Override
public String getColor() {
return "Red";
}
}
@Override
public String getColor() {
return "Green";
}
}
🔹 4. [Link]
@Override
public String getColor() {
return "Yellow";
}
}
🔹 5. [Link] (Context)
🔹 6. Main Class
public class Main {
public static void main(String[] args) {
TrafficLight light = new TrafficLight();
✅ Output
makefile
CopyEdit
✅ 1. [Link] (interface)
package [Link];
import [Link];
✅ 2. Concrete States
🔸 [Link]
import [Link];
import [Link];
@Component
public class RedState implements State {
@Override
public void handle(TrafficLightContext context) {
[Link]([Link]());
}
@Override
public String getColor() {
return "Red";
}
}
🔸 [Link]
package [Link];
import [Link];
import [Link];
@Component
public class GreenState implements State {
@Override
public void handle(TrafficLightContext context) {
[Link]([Link]());
}
@Override
🔸 [Link]
package [Link];
import [Link];
import [Link];
@Component
public class YellowState implements State {
@Override
public void handle(TrafficLightContext context) {
[Link]([Link]());
}
@Override
public String getColor() {
return "Yellow";
}
}
✅ 3. [Link]
package [Link];
import [Link].*;
import [Link];
import [Link];
@Component
@PostConstruct
public void init() {
currentState = redState; // initial state
}
✅ 4. [Link]
package [Link];
import [Link];
import [Link].*;
@RestController
@RequestMapping("/traffic-light")
public class TrafficLightController {
@GetMapping("/state")
public String getCurrentState() {
return "Current State: " + [Link]();
}
@PostMapping("/next")
public String goToNextState() {
[Link]();
return "Transitioned to: " + [Link]();
}
}
✅ 5. [Link]
import [Link];
import [Link];
@SpringBootApplication
public class StatePatternApplication {
public static void main(String[] args) {
[Link]([Link], args);
}
}
✅ Sample Output
GET /state → Current State: Red
POST /next → Transitioned to: Green
POST /next → Transitioned to: Yellow
POST /next → Transitioned to: Red
10. Strategy
Fastest route
Shortest distance
Avoid tolls
✅ Participants
Component Role
Strategy Interface for all supported algorithms
ConcreteStrategy Implementation of the algorithm
Context Uses a Strategy object to call the algorithm
Credit Card
PayPal
UPI
interface PaymentStrategy {
void pay(double amount);
}
@Override
public void pay(double amount) {
[Link]("Paid ₹" + amount + " using Credit Card: " + cardNum
ber);
}
}
[Link]
@Override
public void pay(double amount) {
[Link]("Paid ₹" + amount + " using PayPal: " + email);
}
}
[Link]
@Override
public void pay(double amount) {
[Link]("Paid ₹" + amount + " using UPI: " + upiId);
}
}
🔹 3. [Link] (Context)
🔹 4. Main Method
[Link](new CreditCardPayment("1234-5678-9012-
3456"));
[Link](500.0);
[Link](new PayPalPayment("user@[Link]
m"));
[Link](250.0);
[Link](new UPIPayment("user@upi"));
[Link](100.0);
}
}
✅ Output
Credit Card: 1234-5678-9012-3456
Paid ₹250.0 using PayPal: user@[Link]
Paid ₹100.0 using UPI: user@upi
Authentication
JWT, OAuth2, Basic Auth, API Key strategies
mechanisms
✅ Goal
Create a Spring Boot app where:
A user can choose a strategy via API (e.g., PayPal, Credit Card, UPI).
✅ 1. Project Structure
spring-strategy-demo/
├── controller/
│ └── [Link]
├── strategy/
│ ├── [Link]
│ ├── [Link]
│ ├── [Link]
│ ├── [Link]
│ └── [Link]
└── [Link]
import [Link];
@Component("paypal")
public class PayPalPayment implements PaymentStrategy {
@Override
public void pay(double amount) {
[Link]("Paid ₹" + amount + " using PayPal");
}
}
// strategy/[Link]
package [Link];
import [Link];
@Component("creditcard")
public class CreditCardPayment implements PaymentStrategy {
@Override
public void pay(double amount) {
[Link]("Paid ₹" + amount + " using Credit Card");
}
}
// strategy/[Link]
package [Link];
import [Link];
@Component("upi")
import [Link];
import [Link];
import [Link];
@Component
public class PaymentStrategyFactory {
@Autowired
public PaymentStrategyFactory(Map<String, PaymentStrategy> strategyMa
p) {
[Link] = strategyMap;
}
✅ 5. REST Controller
// controller/[Link]
package [Link];
import [Link];
import [Link];
import [Link];
import [Link].*;
@RestController
@RequestMapping("/api/payment")
public class PaymentController {
@Autowired
private PaymentStrategyFactory strategyFactory;
@PostMapping
public String pay(@RequestParam double amount, @RequestParam String
method) {
PaymentStrategy strategy = [Link](method);
[Link](amount);
return "Payment of ₹" + amount + " via " + method + " completed.";
}
}
✅ 6. Main Class
package [Link];
import [Link];
@SpringBootApplication
public class SpringStrategyDemoApplication {
public static void main(String[] args) {
[Link]([Link], args);
}
}
Output (console):
11. Template
✅ What it means
You have a general algorithm with fixed steps, but some steps can vary depending
on the subclass. You put the fixed sequence in a method (the template method)
and let subclasses provide the implementation for the variable steps.
✅ Real-world analogy
Think of making a tea or coffee. The sequence is:
1. Boil water
Steps 1, 3 are fixed, but steps 2 and 4 vary depending on the drink.
✅ Structure
Component Description
Java Example
2. Concrete Classes
@Override
protected void brew() {
[Link]("Steeping the tea leaves");
}
@Override
protected void brew() {
[Link]("Dripping coffee through filter");
}
@Override
protected void addCondiments() {
[Link]("Adding sugar and milk");
}
}
3. Client code
[Link]();
Boiling water
Steeping the tea leaves
Pouring into cup
Adding lemon
Boiling water
Dripping coffee through filter
Pouring into cup
Adding sugar and milk
Frameworks where basic workflow E.g., JUnit testing framework uses template methods
is fixed for test lifecycle ( setUp , tearDown ).
Algorithms with invariant structure Sorting algorithms that share common parts but vary
but variable steps the pivot choice.
Code reuse with common Base classes define the skeleton, subclasses override
algorithm parts details.
Workflow engines and batch jobs Fixed steps with flexible sub-tasks inside a process.
Scenario
We have a service that sends notifications. The basic flow (algorithm) to send a
notification is:
Steps 1 and 4 are fixed for all notifications. Steps 2 and 3 vary for different
notification types like Email and SMS.
package [Link];
// Template method
public final void sendNotification(String to, String message) {
validate(to, message);
String preparedMessage = prepareMessage(message);
send(preparedMessage, to);
log();
}
package [Link];
import [Link];
@Service("emailNotificationService")
public class EmailNotificationService extends NotificationService {
@Override
protected String prepareMessage(String message) {
return "Email Content: " + message;
}
@Override
protected void send(String message, String to) {
[Link]("Sending Email to " + to + " with message: " + messag
e);
}
}
package [Link];
import [Link];
@Service("smsNotificationService")
public class SmsNotificationService extends NotificationService {
@Override
protected void send(String message, String to) {
[Link]("Sending SMS to " + to + " with message: " + messag
e);
}
}
package [Link];
import [Link];
import [Link];
import [Link];
import [Link].*;
@RestController
@RequestMapping("/api/notify")
public class NotificationController {
@Autowired
@Qualifier("emailNotificationService")
private NotificationService emailNotificationService;
@Autowired
@Qualifier("smsNotificationService")
private NotificationService smsNotificationService;
@PostMapping
NotificationService service;
switch ([Link]()) {
case "email":
service = emailNotificationService;
break;
case "sms":
service = smsNotificationService;
break;
default:
throw new IllegalArgumentException("Invalid notification type");
}
[Link](to, message);
return "Notification sent via " + type;
}
}
package [Link];
import [Link];
import [Link];
@SpringBootApplication
public class NotificationTemplateApp {
public static void main(String[] args) {
[Link]([Link], args);
How to test
Start the app and send POST requests:
Output console:
Output console:
✅ Summary
Template Method defines fixed steps + customizable hooks.
12. Visitor
✅ Structure
Component Description
1. Element interface
@Override
public void accept(ShoppingCartVisitor visitor) {
[Link](this);
}
}
3. Visitor Interface
@Override
public void visit(Book book) {
int cost = [Link]();
[Link]("Book ISBN::" + [Link]() + " cost = " +
cost);
}
@Override
public void visit(Fruit fruit) {
int cost = [Link]() * [Link]();
[Link]([Link]() + " cost = " + cost);
}
}
5. Client code
int total = 0;
ShoppingCartVisitor visitor = new ShoppingCartVisitorImpl();
Output
Summary
Visitor decouples operations from object structure.
Adding operations without When you need to add new functionality frequently to
changing classes unrelated classes but want to avoid modifying their code.
Scenario
Imagine a document processing system with different types of documents:
Invoice
Report
We want to:
package [Link];
package [Link];
@Override
public void accept(DocumentVisitor visitor) {
[Link](this);
}
}
package [Link];
@Override
public void accept(DocumentVisitor visitor) {
[Link](this);
}
}
package [Link];
import [Link];
import [Link];
package [Link];
import [Link];
@Component("summaryVisitor")
public class SummaryVisitor implements DocumentVisitor {
@Override
public void visit(Invoice invoice) {
[Link]("Invoice Summary: Invoice #" + [Link]
mber() + ", Amount: $" + [Link]());
}
@Override
public void visit(Report report) {
[Link]("Report Summary: Title - " + [Link]());
}
}
package [Link];
import [Link];
import [Link];
import [Link];
@Component("detailedVisitor")
public class DetailedVisitor implements DocumentVisitor {
@Override
public void visit(Invoice invoice) {
[Link]("Invoice Detailed Report:\nInvoice Number: " + invoic
[Link]() + "\nAmount Due: $" + [Link]());
}
@Override
public void visit(Report report) {
package [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link].*;
@RestController
@RequestMapping("/documents")
public class DocumentController {
@Autowired
@Qualifier("summaryVisitor")
private DocumentVisitor summaryVisitor;
@Autowired
@Qualifier("detailedVisitor")
private DocumentVisitor detailedVisitor;
@GetMapping("/process")
public String processDocument(@RequestParam String type,
@RequestParam String visitorType) {
Document document;
// Choose visitor
DocumentVisitor visitor;
if ("summary".equalsIgnoreCase(visitorType)) {
visitor = summaryVisitor;
} else if ("detailed".equalsIgnoreCase(visitorType)) {
visitor = detailedVisitor;
} else {
return "Invalid visitor type";
}
// Accept visitor
[Link](visitor);
return "Processed " + type + " with " + visitorType + " visitor";
}
}
package [Link];
import [Link];
import [Link];
@SpringBootApplication
Call:
GET [Link]
mary
Console output:
GET [Link]
led
Console output:
Summary
Visitor separates operations from document object structure.