Software Architect's Design Patterns
Original

ZenTao Content
2025-11-27 21:00:00
6
Summary : This article explores seven essential Java design patterns—Singleton, Factory, Strategy, Observer, Decorator, Adapter, and Template Method—to address scalability, maintainability, and reusability challenges. Each pattern is analyzed through its core concepts, practical applications, and limitations. The guide emphasizes design principles such as open-closed and low coupling for effective pattern selection, assisting developers in constructing robust, extensible systems that align with object-oriented design and SOLID principles.
ZenTao: 15 years of dedication to building open source project management software
Download Now

In Java software development, the increasing scale of systems and growing business complexity have made code extensibility, maintainability, and reusability critical considerations. Architects frequently employ design patterns to address common challenges. This article elaborates on seven design patterns—Singleton, Factory, Strategy, Observer, Decorator, Adapter, and Template Method—analyzing their core concepts and applicability in Java contexts to provide guidance for developers.

1. Singleton Pattern

The Singleton pattern ensures that a class has only one instance throughout the application lifecycle and provides a global access point. In Java, thread safety must be addressed. The enumeration implementation is considered the safest approach, as it prevents reflection and serialization from breaking the singleton, as demonstrated in the Config Manager class. This pattern is suitable for scenarios requiring global management of resources or states, including:

  • Configuration management: Ensures configuration is loaded only once, avoiding resource waste and data inconsistency;
  • Database connection pool: Guarantees a unique connection pool, optimizing connection allocation and reuse;
  • Logger: Prevents log confusion caused by multiple instances and ensures complete logging.

The Singleton pattern has limitations: it is unsuitable when a class requires multiple stateful instances and may increase coupling, making unit testing challenging.

2. Factory Pattern

The Factory pattern encapsulates object creation logic, separating object creation from usage. It defines a product interface, concrete product classes, and a factory class, where the factory creates corresponding product instances based on specific conditions. For example, in a notification system, the Notification Factory can generate Email Notification or SMS Notification objects. This pattern is suitable for scenarios with complex object creation, diverse product types, and requirements for extensibility, including:

  • Payment channel selection: Adding a new payment channel only requires implementing the corresponding product class and factory logic, adhering to the open-closed principle;
  • Logger creation: Creates different types of loggers based on environments (e.g., development or production), centralizing creation logic;
  • Database connection creation: Generates various database connections based on configuration, facilitating database switching and extension.

The Factory pattern's limitations include increased complexity in factory class logic when dealing with numerous product types and potential code redundancy in simple object creation scenarios.

3. Strategy Pattern

The Strategy pattern defines and encapsulates a family of algorithms, enabling them to be interchangeable. It consists of a strategy interface, concrete strategy classes, and a context class. Clients dynamically select the required strategy through the context class, such as Discount Context choosing between Vip Discount or New User Discount in a discount calculation scenario. This pattern is suitable for scenarios with multiple algorithm choices requiring flexible switching, including:

  • Payment method selection: Adding a new payment method only requires implementing the corresponding strategy class, decoupling payment logic from the client;
  • Sorting algorithm switching: Enables selection of appropriate sorting algorithms based on data scale, facilitating optimization and extension;
  • Discount strategy calculation: Modifying or adding discount strategies doesn't require changes to core code, reducing coupling.

The Strategy pattern's limitations include proliferation of classes with numerous strategies and the requirement for clients to understand all available strategies to make informed selections.

4. Observer Pattern

The Observer pattern defines a one-to-many dependency relationship where changes to a subject's state automatically notify all dependent observers. It includes an observer interface, concrete observer classes, and a subject class. The subject maintains a list of observers and initiates notifications upon state changes, such as Order Subject notifying Email Service and SMS Service in an order system. This pattern is suitable for scenarios with one-to-many dependencies where multiple objects need to respond to state changes, including:

  • Message subscription systems: Decouples publishers and subscribers, supporting dynamic addition or removal of subscribers;
  • Event listening mechanisms: Examples include button click events in Swing or event handling in Servlet, separating event generation from processing logic;
  • Data change notifications: Notifies relevant modules to update caches or refresh statistics when database data changes.

The Observer pattern's limitations include potential performance degradation with numerous observers and the risk of infinite loops if circular dependencies exist between observers and subjects.

5. Decorator Pattern

The Decorator pattern dynamically adds functionality to objects without altering their original structure. It consists of a component interface, concrete component classes, an abstract decorator class, and concrete decorator classes. Functionality is extended by nesting decorators around components, such as Milk Decorator and Sugar Decorator enhancing Basic Coffee in a coffee system. This pattern is suitable for scenarios requiring dynamic functionality addition without inheritance, including:

  • IO stream processing: Decorators like Buffered Input Stream and Data Input Stream can be combined to create IO streams with multiple functionalities;
  • Middleware chains: Examples include Servlet filters or RPC interceptors, enabling dynamic combination of processing logic;
  • Function enhancement: Adds capabilities like logging or performance monitoring to user services without modifying original service code.

The Decorator pattern's limitations include increased system complexity with excessive decorators and the challenge of tracing complete decorator call chains during debugging.

6. Adapter Pattern

The Adapter pattern converts a class's interface into another interface clients expect, resolving interface incompatibility issues. In the object adapter pattern, the adapter class implements the target interface and holds a reference to the adapted class, such as Payment Adapter adapting Legacy Payment to Payment Processor in a payment system. This pattern is suitable for integrating incompatible interfaces while enabling code reuse and system compatibility, including:

  • Third-party library integration: For example, Log4j Adapter adapts Log4j to custom logging interfaces, facilitating future library replacements;
  • Legacy system transformation: Uses adapters as transitions to adapt old system interfaces to new systems, reducing transformation risks;
  • Interface standardization: Unifies disparate interfaces across modules, such as Cache Service adapting various caching interfaces.

The Adapter pattern's limitations include potential system structure confusion with excessive adapters and its inability to resolve fundamental system design flaws.

7. Template Method Pattern

The Template Method pattern defines an algorithm's skeleton while deferring implementation of specific steps to subclasses. The abstract class includes a template method (defining fixed steps), abstract steps (implemented by subclasses), concrete steps, and hook methods. For example, in data processing, Data Processor's Csv Processor and Json Processor implement different data reading and writing logic. This pattern is suitable for scenarios with fixed algorithm processes but flexible step implementations, including:

  • Workflow definition: Examples include order processing workflows where variable steps (e.g., order validation) are implemented by subclasses;
  • Data processing pipelines: In ETL processes, steps like data extraction and transformation can be customized based on specific scenarios;
  • Business process templates: In SaaS applications, tenants can customize process details without altering the overall framework.

The Template Method pattern's limitations include impacts on all subclasses when algorithm processes frequently change and increased class hierarchy complexity.

8. Design Pattern Selection Principles and Recommendations

Design pattern selection should adhere to the following principles:

  • Problem-oriented principle: Select patterns based on core issues (e.g., complex object creation, incompatible interfaces);
  • Open-closed priority principle: Prefer patterns that support extension while avoiding modification;
  • Low coupling, high cohesion principle: Emphasize patterns that reduce code coupling and enhance module cohesion;
  • Scenario adaptation principle: Choose patterns based on specific scenario characteristics and requirements.

In practice, it is essential to: Thoroughly study design pattern concepts and implementations while analyzing their applications in open-source frameworks; Avoid over-engineering and apply patterns appropriately based on project scale and requirements; Combine SOLID principles to ensure single responsibility and dependence on abstractions rather than concretions; Establish internal team guidelines for design pattern usage to ensure consistency and maintain comprehensive documentation.


The seven design patterns discussed above can effectively address common design challenges in Java development and enhance code quality. In practice, developers should apply these patterns judiciously based on project requirements and specific scenarios, following design pattern selection principles while incorporating object-oriented design principles to build high-quality, extensible Java systems capable of adapting to technological advancements and business evolution. An exceptional architect must not only comprehend these patterns but also deeply understand the design principles they embody, such as the Open-Closed, Dependency Inversion, and Single Responsibility Principles. Furthermore, they must be able to flexibly apply these concepts at the architectural design level to select or construct suitable frameworks and components, thereby ensuring the software system's scalability, maintainability, and reusability at a fundamental level.

Write a Comment
Comment will be posted after it is reviewed.