Pattern Examples
Pattern Examples
encapsulate each one, and make them interchangeable. It allows the algorithm to vary
independently from the clients that use it. Here are some scenarios where the Strategy pattern
can be applied:
Scenario: An e-commerce platform that supports multiple payment methods (Credit Card,
PayPal, Bitcoin, etc.).
Context: The system needs to allow users to select their preferred payment method
during checkout. Different payment methods have different processes, validation, and
transaction fees.
Strategy: Define a PaymentStrategy interface with a method like
processPayment(). Each specific payment method (e.g., CreditCardPayment,
PayPalPayment, BitcoinPayment) implements this interface, and the context (e-
commerce system) selects the appropriate payment strategy based on the user's
choice.
Example:
java
Copy code
interface PaymentStrategy {
void processPayment(double amount);
}
class ShoppingCart {
private PaymentStrategy paymentStrategy;
2. Sorting Algorithms
Scenario: An application that allows users to sort a list of items in different ways (e.g.,
ascending, descending, by date, by price, etc.).
Context: The sorting logic needs to be flexible so that users can choose their
preferred sorting criteria dynamically.
Strategy: Define a SortStrategy interface with a method like sort(List<T>
items). Implement multiple sorting strategies like BubbleSort, QuickSort,
MergeSort, and DateSort, which all implement the SortStrategy interface. The
context (sorting engine) chooses the appropriate strategy based on the user's input.
Example:
java
Copy code
interface SortStrategy {
<T> List<T> sort(List<T> items);
}
class Sorter {
private SortStrategy sortStrategy;
Scenario: An online store that applies different discount strategies based on the type of
customer or promotion.
Context: The store needs to apply various discount types, such as a percentage off, a
fixed discount, or special promotional discounts.
Strategy: Define a DiscountStrategy interface with a method like
applyDiscount(double price). Implement various discount strategies like
PercentageDiscount, FixedAmountDiscount, and PromotionalDiscount. The
context (shopping cart) selects the appropriate discount strategy based on the user’s
profile or promotional rules.
Example:
java
Copy code
interface DiscountStrategy {
double applyDiscount(double price);
}
class ShoppingCart {
private DiscountStrategy discountStrategy;
Scenario: A navigation system that can calculate the best route for different types of travel
(driving, walking, biking, public transport, etc.).
Context: The application needs to switch between various algorithms for calculating
routes depending on the user’s selected mode of transportation.
Strategy: Define a RouteCalculationStrategy interface with a method like
calculateRoute(Map<String, Object> params). Implement multiple strategies
like DrivingRouteCalculation, WalkingRouteCalculation, and
BikeRouteCalculation, and let the context (navigation system) select the
appropriate strategy based on the user’s transportation choice.
Example:
java
Copy code
interface RouteCalculationStrategy {
String calculateRoute(Map<String, Object> params);
}
class NavigationSystem {
private RouteCalculationStrategy routeStrategy;
5. Compression Algorithms
Scenario: A file compression application that supports multiple compression formats (e.g.,
ZIP, TAR, GZIP).
Context: The user can choose the type of compression format they want to use to
compress or decompress files.
Strategy: Define a CompressionStrategy interface with methods like
compress(File file) and decompress(File file). Implement strategies like
ZipCompression, TarCompression, and GzipCompression. The context
(compression utility) selects the appropriate strategy based on the user’s format
choice.
Example:
java
Copy code
interface CompressionStrategy {
void compress(File file);
void decompress(File file);
}
class CompressionUtility {
private CompressionStrategy compressionStrategy;
Scenario: A financial application that calculates taxes based on various regional tax laws.
Scenario: A reporting or document generation system that can generate different types of
documents (e.g., PDF, Word, Excel) based on user input.
Context: The system needs to generate different types of reports based on the user's
selection, but the exact class to instantiate depends on the user’s choice.
Factory: A DocumentFactory class can be created to instantiate different document
types (PDFDocument, WordDocument, ExcelDocument) based on the input parameters.
Example:
java
Copy code
interface Document {
void create();
}
class DocumentFactory {
public Document createDocument(String type) {
if (type.equalsIgnoreCase("pdf")) {
return new PDFDocument();
} else if (type.equalsIgnoreCase("word")) {
return new WordDocument();
} else if (type.equalsIgnoreCase("excel")) {
return new ExcelDocument();
} else {
throw new IllegalArgumentException("Unknown document type");
}
}
}
public class Main {
public static void main(String[] args) {
DocumentFactory factory = new DocumentFactory();
Document doc = factory.createDocument("pdf");
doc.create();
}
}
Example:
java
Copy code
interface DatabaseConnection {
void connect();
}
class DatabaseConnectionFactory {
public DatabaseConnection createConnection(String dbType) {
if (dbType.equalsIgnoreCase("mysql")) {
return new MySQLConnection();
} else if (dbType.equalsIgnoreCase("postgresql")) {
return new PostgreSQLConnection();
} else if (dbType.equalsIgnoreCase("oracle")) {
return new OracleConnection();
} else {
throw new IllegalArgumentException("Unknown database type");
}
}
}
Scenario: A cross-platform application that needs to create different types of user interface
components (buttons, text fields, etc.) that behave differently on different operating systems
(e.g., Windows, macOS, Linux).
Context: The system needs to instantiate UI components like buttons, text fields, or
checkboxes that have different implementations for each platform.
Factory: A UIComponentFactory can be used to create platform-specific UI
components (e.g., WindowsButton, MacOSButton, LinuxButton) based on the current
operating system.
Example:
java
Copy code
interface Button {
void render();
}
class ButtonFactory {
public Button createButton(String osType) {
if (osType.equalsIgnoreCase("windows")) {
return new WindowsButton();
} else if (osType.equalsIgnoreCase("macos")) {
return new MacOSButton();
} else if (osType.equalsIgnoreCase("linux")) {
return new LinuxButton();
} else {
throw new IllegalArgumentException("Unknown OS type");
}
}
}
Scenario: A drawing application that allows users to create various shapes (e.g., circles,
squares, rectangles) dynamically.
Context: The application needs to instantiate different types of shapes based on user
input, and each shape has its own specific rendering and behavior.
Factory: A ShapeFactory class can create instances of different shapes (Circle,
Square, Rectangle) depending on the user’s input or selection.
Example:
java
Copy code
interface Shape {
void draw();
}
class Circle implements Shape {
public void draw() {
System.out.println("Drawing a circle...");
class ShapeFactory {
public Shape createShape(String shapeType) {
if (shapeType.equalsIgnoreCase("circle")) {
return new Circle();
} else if (shapeType.equalsIgnoreCase("square")) {
return new Square();
} else if (shapeType.equalsIgnoreCase("rectangle")) {
return new Rectangle();
} else {
throw new IllegalArgumentException("Unknown shape type");
}
Scenario: An application that needs to log messages to different destinations (e.g., a file, the
console, a database).
Example:
java
Copy code
interface Logger {
void log(String message);
}
class LoggerFactory {
public Logger createLogger(String loggerType) {
if (loggerType.equalsIgnoreCase("file")) {
return new FileLogger();
} else if (loggerType.equalsIgnoreCase("console")) {
return new ConsoleLogger();
} else if (loggerType.equalsIgnoreCase("database")) {
return new DatabaseLogger();
} else {
throw new IllegalArgumentException("Unknown logger type");
}
}
}
Scenario: An automotive company that manufactures different types of vehicles (e.g., cars,
bikes, trucks) based on customer orders.
Context: The system needs to produce different types of vehicles with varying
features and manufacturing processes, but the vehicle type is determined at runtime.
Factory: A VehicleFactory class can abstract the process of creating different
vehicle types (Car, Bike, Truck) based on customer specifications.
The Decorator Design Pattern is used to dynamically add behavior to an object without
altering its class. It is particularly useful when you want to extend the functionalities of a
class in a flexible and reusable way. Here are some scenarios where the Decorator pattern
would be an ideal solution:
1. UI Component Styling
Scenario: You are developing a UI framework where you have basic UI components
(e.g., buttons, text fields, labels) and want to add various visual styles or behavior like
borders, background colors, padding, or hover effects without modifying the core
components themselves.
Decorator Application: You could create decorators like BorderDecorator,
PaddingDecorator, BackgroundColorDecorator, and HoverEffectDecorator to
add specific visual features to the UI elements dynamically.
Scenario: You have a system where you need to log information or monitor activities
for certain methods or operations, but you don't want to clutter the core business logic
with logging code.
Decorator Application: You could create a LoggingDecorator or
MonitoringDecorator that wraps the original objects and logs method calls or
performance metrics before calling the original method.
Scenario: You are building a system where objects represent users, and you need to
assign roles or permissions dynamically (e.g., a basic user, admin, or guest user). The
permissions could vary at runtime, and you don't want to modify the core user class
for each role.
Decorator Application: You could create decorators like AdminDecorator,
GuestDecorator, or PremiumMemberDecorator that wrap a base user object and add
additional permission checks or behaviors as needed.
Scenario: In a file handling system, you might want to apply different filters or
transformations on files (e.g., compression, encryption, or logging operations) before
reading or writing them, without altering the core file operations.
Decorator Application: You could have decorators like CompressionDecorator,
EncryptionDecorator, or LoggingDecorator that wrap file objects and modify
their behavior (such as encrypting or compressing file contents) on the fly.
5. Input Validation
Scenario: A form or user input system where fields may require multiple validations
like range checking, format validation, or uniqueness. You don’t want to modify the
input classes directly but need to apply these validations dynamically.
Decorator Application: You could create decorators such as
RangeValidationDecorator, FormatValidationDecorator, or
UniquenessValidationDecorator that wrap the original field and add specific
validation logic.
Scenario: A printing system where printers can be enhanced with additional features
such as double-sided printing, color printing, or advanced paper quality. You don’t
want to modify the printer class directly but still need to allow different combinations
of behaviors.
Decorator Application: You could create decorators like
DoubleSidedPrintingDecorator, ColorPrintingDecorator, or
HighQualityPaperDecorator to dynamically modify printer behavior.
Scenario: You are building a vehicle rental application where different types of cars
can be upgraded with features like GPS navigation, air conditioning, or leather seats.
Instead of creating many subclasses for each combination of features, you want to
apply features dynamically.
Decorator Application: You could use decorators like GPSDecorator,
AirConditioningDecorator, or LeatherSeatsDecorator to apply features to a
basic car object at runtime.
Scenario: In a text editor, you want to provide various formatting options (bold,
italic, underline, etc.) or even custom behavior like auto-correction or spell check, but
you don’t want to change the core text object for each type of feature.
Decorator Application: You could create decorators like BoldDecorator,
ItalicDecorator, SpellCheckDecorator, or AutoCorrectDecorator to add
specific behavior to the text object dynamically.
Scenario: You are working with a video processing system where videos can have
multiple filters applied (e.g., grayscale, sepia, brightness adjustments) but don’t want
to change the core video processing logic.
Decorator Application: You can create decorators like GrayscaleDecorator,
SepiaFilterDecorator, or BrightnessAdjustmentDecorator to apply different
visual effects to a video object at runtime.
Flexibility: Allows adding behaviors at runtime, making the system more flexible and
extensible.
Avoids Class Explosion: You don't have to create numerous subclasses for every
combination of behaviors, keeping the class hierarchy cleaner.
Separation of Concerns: Each decorator focuses on a specific concern, allowing
better separation of responsibilities
The Observer Design Pattern is widely used when one object (the "subject") needs to notify
other objects (the "observers") about changes in its state without knowing who or what those
observers are. This pattern is useful when you need to decouple the components in a system
and allow them to evolve independently.
Here are some practical scenarios where the Observer Design Pattern can be applied:
Scenario: A UI component (like a button or a form) changes state, and several other
components need to update or react accordingly.
Example: In a graphical application, a Button (subject) might be pressed, and various
components (observers) such as a StatusBar, Logger, or Toolbar might need to
update based on the state change.
How it works: When the Button is clicked, it notifies its observers to refresh or take
action. The StatusBar could show a message, the Logger might log the event, and
the Toolbar could enable or disable related options.
Scenario: A stock exchange system tracks stock prices, and multiple users (observers)
want to be notified about price changes for stocks they are interested in.
Example: A stock trading platform (StockTicker) could track stock prices for
various companies. Observers, such as users’ dashboards, financial analysis tools, or
email/notification systems, would update in real-time whenever a stock price changes.
How it works: Users subscribe to price updates for specific stocks, and when the
price for that stock changes, only the relevant observers are notified.
Scenario: A social media platform needs to notify users about new activities (e.g.,
likes, comments, followers).
Example: A UserProfile object represents a user, and different observers, such as
the NotificationService, ActivityFeed, and EmailSystem, need to react to
activities like receiving a new comment or like on a post.
How it works: The UserProfile notifies all observers whenever a new activity
happens (e.g., someone likes or comments on a post), triggering appropriate updates
on the observers' side.
Scenario: A text editor or word processor with multiple views that need to reflect
changes made to the document in real-time.
Example: The Document (subject) might be updated (e.g., text added or modified),
and various views like a PrintView, WebView, and MobileView (observers) should be
updated simultaneously.
How it works: When the content of the Document changes, all its observers are
notified and updated to reflect the new content.
Scenario: A BankAccount object changes its balance, and multiple interested parties
(observers) need to react to this change (e.g., sending notifications, updating financial
records).
Example: A BankAccount object might have several observers, including a
TransactionHistory display, CreditScoreTracker, and FraudDetectionSystem,
all of which need to be updated when a transaction occurs.
How it works: Whenever a transaction is made (deposit or withdrawal), the
BankAccount notifies all registered observers, allowing each to perform its specific
task (e.g., updating the history, triggering fraud alerts).
Scenario: In a smart home, various devices need to respond to events, such as motion
detection, temperature changes, or door lock/unlock actions.
Example: A MotionSensor could detect movement, and observers like a
SecuritySystem, LightingSystem, and HeatingSystem may all need to take action.
How it works: When motion is detected, the MotionSensor notifies its observers.
The SecuritySystem may trigger an alarm, the LightingSystem may turn on the
lights, and the HeatingSystem might adjust the temperature.
9. Chat Application
Scenario: A chat application needs to notify users about new messages or events.
Example: The ChatRoom (subject) needs to notify all participants (observers) when a
new message is sent, when someone joins or leaves the room, or when the chat is
archived.
How it works: The ChatRoom notifies all users (observers) about new messages, so
they can update their UI, highlight new messages, or show notifications.
In summary, the Observer Design Pattern is particularly useful in scenarios where one
object’s state change needs to be communicated to many other objects w
ithout tight coupling between them. It facilitates real-time updates and decouples the logic
between the subject and its observers.
The Facade Design Pattern provides a simplified interface to a complex subsystem, hiding
its complexity and making it easier to use. This pattern is useful when you want to provide a
simple interface to a set of interfaces in a subsystem, making the subsystem easier to use for
the client.
Here are some common scenarios where the Facade Design Pattern can be applied:
Scenario: A home theater system might consist of multiple components like a DVD player,
projector, sound system, and lights. Each component might have its own complex interface.
Facade: You can create a HomeTheaterFacade class that simplifies the interaction. The
client would just call turnOn() or playMovie(), and the facade would coordinate with all
the components to turn on the system, adjust settings, and start the movie.
Scenario: A payment gateway can involve several complex systems: processing payments,
verifying fraud checks, checking customer data, and interacting with banks. Facade: A
PaymentFacade can provide a simplified interface for users to make a payment, with the
facade handling the integration of different subsystems like fraud detection, payment
processing, and bank interaction.
Scenario: A library management system may have several subsystems like managing books,
tracking borrowers, handling fines, and generating reports. Facade: A LibraryFacade can
provide simplified methods for common actions, such as borrowing a book or generating a
fine report, while hiding the complexity of interacting with multiple subsystems.
4. Database Operations
Scenario: A complex application that interacts with multiple databases, where operations like
queries, transactions, data validation, and logging are required. Facade: A DatabaseFacade
can provide a single, unified API to execute queries, handle transactions, and manage
connections, abstracting the complexity of database interactions.
Scenario: An e-commerce platform with a complex order management system may require
several steps like inventory check, payment verification, packing, and shipping. Facade: An
OrderFacade can encapsulate the order lifecycle into a single placeOrder() method, which
interacts with various subsystems such as inventory, payment, packaging, and shipping.
Scenario: A building may have subsystems for lighting, heating, ventilation, and air
conditioning (HVAC), security systems, and elevator management. Each subsystem might
have different interfaces and functions. Facade: A BuildingFacade can provide an interface
to control various aspects of the building. For example, a user could adjust the temperature,
turn on lights, and secure the building through a single interface.
Scenario: A CI/CD pipeline can involve many tools and services like code repositories, build
servers, testing frameworks, artifact repositories, and deployment servers. Facade: A
DeploymentFacade can simplify the process of deploying a new version of software. The
user can just trigger a deploy() method, while the facade orchegs the necessary tasks like
code checkout, build, testing, and deployment.
Subsystems: Version control system, build tool, test framework, deployment tool.
Facade: DeploymentFacade (deploy(), rollback()).
8. Cloud Storage System
Scenario: Cloud storage systems can have a complex set of features, including uploading
files, retrieving files, managing access control, and syncing data. Facade: A
CloudStorageFacade can offer a simplified API for the user, where actions like uploading a
file or sharing access are abstracted into simple method calls like uploadFile() or
shareAccess().
Scenario: An online hotel or flight booking system involves interacting with subsystems such
as payment gateways, flight schedules, hotel availability, and customer notifications. Facade:
A BookingFacade can offer a simple interface for users to book flights and hotels,
abstracting away the complexity of checking availability, processing payments, and sending
confirmations.
Subsystems: Payment system, flight schedule service, hotel availability service, email
notifications.
Facade: BookingFacade (bookHotel(), bookFlight()).
Scenario: A typical checkout system in e-commerce involves interacting with the shopping
cart, inventory, payment gateway, tax calculation, and shipment options. Facade: A
CheckoutFacade can simplify the entire checkout process into a single method call, hiding
the complexity of validating cart items, calculating taxes, processing payment, and arranging
shipment.
Scenario: A complex image processing system might involve several subsystems for reading
images, applying filters, resizing, and saving the processed image. Facade: An
ImageProcessingFacade can provide a simple interface to users for common image
processing tasks, like applyFilter() or resizeImage(), abstracting away the underlying
complexities.
In each of these scenarios, the facade pattern hides the complexity of the underlying
subsystems and provides a higher-level, user-friendly interface for the client to interact with,
reducing the amount of work the client has to do and making the system easier to maintain
and extend.
The **Adapter** design pattern is a structural pattern that allows incompatible interfaces to
work together. It acts as a bridge between two interfaces by converting one interface into
another expected by the client. This pattern is often used when you need to integrate new
components into a system that were not designed to work together initially.
Here are some scenarios where the Adapter pattern can be useful:
- **Scenario:** You have a modern application that needs to interface with an old legacy
system that has a different interface.
- **Example:** A new e-commerce system needs to integrate with an old payment gateway
that uses a different API format (e.g., SOAP instead of REST). The Adapter pattern can be
used to convert between the old SOAP-based API and the new RESTful interface.
- **Scenario:** You are working with multiple data formats or protocols that are
incompatible with each other.
- **Example:** You are building a system that communicates with different data sources:
one that provides data in JSON format, another in XML. An Adapter can be created to
convert between the different formats (e.g., converting XML data into JSON).
- **Scenario:** You need to communicate with devices or hardware components that have
different interfaces or protocols.
- **Example:** In a smart home system, different devices (lights, thermostats, and security
cameras) might communicate using different protocols (Zigbee, Wi-Fi, Bluetooth). An
adapter can be used to unify the interface and enable the system to control all devices in a
consistent manner.
- **Scenario:** When a system or library evolves over time and introduces breaking
changes, the Adapter pattern allows you to keep your old code working while migrating to
the new API.
- **Example:** A payment gateway's API is updated, but you still have clients using the
older version of the API. The Adapter pattern can be used to support both versions of the API
during the transition period.
- **Scenario:** You are developing a UI framework that needs to support different third-
party UI components or libraries that have incompatible interfaces.
- **Example:** You are building a web application that uses different third-party
components for displaying charts (one uses D3.js, another uses Chart.js). An adapter can be
created to standardize the API for rendering charts, allowing you to switch between different
libraries without affecting the rest of the application.
- **Scenario:** You have a complex system with a large API, but your client only needs a
small subset of the functionality.
- **Example:** An application uses a database with a specific query language, but for
performance reasons, you want to adapt the query language to a more optimized format. An
Adapter could convert the original queries into optimized queries that execute more
efficiently.
- **Example:** A mobile app needs to interact with platform-specific APIs (Android and
iOS), but the core application code should be agnostic of the platform. An Adapter can
abstract platform-specific APIs to a common interface that the core application can work
with.
- **Scenario:** Your application uses a custom data repository or database, but it needs to
integrate with standard data storage APIs or frameworks.
- **Example:** Your application uses a custom NoSQL database, but it needs to interface
with a framework like Hibernate, which expects an SQL-based interface. The Adapter can
translate the NoSQL queries into SQL-like queries, enabling compatibility.
- **Scenario:** You are using an existing testing framework that has a different interface
than what your test suite expects.
- **Example:** Your application is using a testing framework that doesn’t provide the
exact methods or structure your application’s test cases rely on. You can use the Adapter
pattern to make the testing framework compatible with your test cases without altering the
framework itself.
### Conclusion:
The Adapter pattern is ideal when you have existing code or components that you can't (or
don't want to) modify, but need to integrate with new code or systems that have incompatible
interfaces. It helps maintain flexibility, reduces dependencies between systems, and promotes
reusability and scalability by allowing you to plug in new components without disturbing the
existing architecture.
The Composite Design Pattern is a structural pattern that is useful when dealing with
objects that are part of a tree-like structure, where individual objects and compositions of
objects should be treated uniformly. In the context of a factory design, this pattern can be
applied when creating hierarchical structures of objects where both individual objects and
groups of objects need to be created, handled, and treated similarly.
Here are several potential scenarios where you could apply the Composite Factory Design
Pattern:
In a graphic editor application, you might have a hierarchy of shapes, where some shapes are
individual (e.g., circles, squares) and others are composites (e.g., a group of shapes forming a
complex object, like a logo or a scene).
Component (Shape): Defines an interface for all objects in the composition (e.g.,
draw(), resize()).
Leaf (Shape types): Simple objects like Circle, Rectangle, Triangle.
Composite (Shape Group): A Group of shapes that implements the draw() and
resize() methods and calls these operations on its child shapes.
Example Usage:
You can create a group of shapes (composite) and ask the factory to add or remove shapes
from the group, or even create pre-defined groups like "House" (a composite shape) or "Tree"
(another composite).
A file system typically has a hierarchical structure where files and directories are both part of
the same structure. Directories can contain files or other directories (subdirectories).
Example Usage:
A client can ask the factory to create files or directories and then later add them to other
directories (composites). The system allows the user to manipulate files and directories
uniformly, regardless of whether they are individual files or nested directories.
3. UI Components (Widgets)
In user interfaces, a "widget" might be a button, label, or other interactive element, and
complex UI components can be created by grouping smaller widgets together.
Component (UIWidget): A common interface that all widgets (simple and complex)
implement, such as render() or click().
Leaf (Button, Label, TextField): Individual UI elements.
Composite (Panel, Window, Form): A collection of widgets.
Example Usage:
A client could ask the factory to create a complex form (composite) with multiple text fields,
buttons, and labels. The composite widget can be rendered or interacted with as a whole, but
the client can also interact with individual elements.
In a document editing system, you may have different types of content (e.g., paragraphs,
images, tables) and more complex documents can contain a mix of these content types.
Example Usage:
A client could ask for a full document, a section of a document, or individual elements (e.g.,
an image or a paragraph). The factory manages the creation of both simple and composite
objects, and the client interacts with them uniformly.
In an e-commerce or retail system, products are organized into categories. Each product can
be an individual item, but categories can contain multiple products or even sub-categories.
Example Usage:
A client could ask the factory to create a category for "Electronics," which includes products
like "Laptop" and "Phone." A sub-category of "Laptops" could also be created to contain
more specific product types.
Uniformity: Both leaf and composite objects can be created using the same API,
simplifying the client code.
Flexibility: The client can work with individual objects or groups of objects in the
same way.
Scalability: New types of composites or leaf objects can be easily added without
changing existing code.
These are just a few scenarios where you can apply the Composite Factory Design Pattern.
The idea is to simplify the creation and management of complex, hierarchical object
structures, treating both individual objects and their compositions uniformly.