Chain of Responsibility Meets Strategy: A Cleaner Way to Slash if-else Sprawl
1. Definition of the Chain of Responsibility Pattern
Chain of Responsibility Pattern is a behavioral design pattern. It constructs a transmission path for a request, and the request is passed along this processing chain until an object on the chain handles it. This pattern means the sender of the request does not need to know which object will handle the request. Each processing object on the chain can either attempt to handle the request or pass it on to the next object.
Follow the public account: 码猿技术专栏, reply with the keyword: 责任链 to get the source code!
Chain of Responsibility Pattern Diagram
Core Point: This pattern aims to decouple the sender and receiver of a request. By using a chain of multiple objects, the request is passed along the chain in sequence until it is handled.
2. Characteristics of the Chain of Responsibility Pattern
- Decoupling Sending and Processing: The sender of the request does not need to know the specific processing object, which greatly enhances the system's flexibility and scalability, allowing the system to cope with changes more calmly.
- Dynamic Logic Composition: The structure of the responsibility chain can be dynamically adjusted according to actual needs, flexibly adding or removing handlers to adapt to different business scenarios.
- Single Responsibility Principle: The Chain of Responsibility pattern encapsulates each validation logic into an independent handler. Each handler is only responsible for a single validation task, conforming to the Single Responsibility Principle in software development and improving code maintainability.
- Good Scalability: When new validation logic needs to be added, the handler only needs to inherit a unified interface and add a new handler, without modifying existing code, reducing code coupling.
- Clear Process Structure: Organizing all validation logic together makes the code structure clearer and easier to understand, facilitating development and maintenance.
3. The Value of Combining the Chain of Responsibility and Strategy Patterns
- Role of the Chain of Responsibility Pattern: Mainly used for dynamically processing requests, connecting multiple processing logics in a certain order to form a complete processing flow.
- Role of the Strategy Pattern: Its core is to encapsulate a set of algorithms, allowing the dynamic selection of an appropriate algorithm for processing at runtime based on specific needs.
Advantages of combining the two:
- The Chain of Responsibility pattern is responsible for passing requests to each handler in sequence, while the Strategy pattern defines the specific processing logic for each handler. The two complement each other.
- By combining them, it is possible to dynamically build a responsibility chain while also flexibly applying different strategies to handle requests, improving the system's adaptability and flexibility.
4. Problems Solved by the Chain of Responsibility Pattern
- Reducing Coupling: Separates the request handler from the request sender, allowing handlers to be independently extended or changed, reducing dependencies between modules.
- Simplifying Multi-Conditional Judgments: Avoids using a large number of
if-elseorswitch-casestatements in the code, making the code more concise and readable, and improving maintainability. - Enhancing Flexibility: Through the dynamic composition of the responsibility chain, the request transmission logic can be easily adjusted, and new handlers can be conveniently inserted to meet changing business needs. Follow the public account: 码猿技术专栏, reply with keyword: 1111 to get Alibaba's internal Java performance tuning manual!
- Reducing Code Duplication: Each handler only focuses on its own part, avoiding code duplication and improving code reusability.
5. Analysis of the Chain of Responsibility Pattern in Code
Scenario 1: Product Listing Logic (Multiple Validations)
Taking the product listing logic (such as validating product information, inventory information, etc.) as an example, the following details how to implement the Chain of Responsibility pattern.
- Define the Chain of Responsibility Abstract Interface
- Define the Chain of Responsibility Identifier for Product Listing
- Define the Common Behavior for Each Handler
- Define the Chain of Responsibility Handler for Product Listing
- Call the Chain of Responsibility for Processing
The above code builds an e-commerce system based on the Chain of Responsibility Pattern, mainly used for handling complex business logic, such as creating product listing templates. This pattern encapsulates each business logic into an independent handler, connects these handlers into a chain, and executes each processing step through a unified entry point, improving the system's maintainability and scalability.
1. Analysis of Code Components and Responsibilities
(1) Chain of Responsibility Abstract Interface: MerchantAdminAbstractChainHandler
- This interface defines the basic behavior in the chain of responsibility:
void handler(T requestParam): This is the core method of the chain of responsibility. Each handler receives the incoming parameterrequestParamand performs corresponding processing based on specific business logic. Follow the public account: 码猿技术专栏, reply with keyword: 1111 to get Alibaba's internal Java performance tuning manual!- Design Idea:
Tis a generic parameter, adaptable to different types of business scenarios, such as object validation, data processing, etc. If a handler does not meet the conditions, it can interrupt the execution of subsequent handlers by throwing an exception or providing a return value. Each handler is only responsible for completing its own part of the logic, following the principle of modular design.
(2) Abstract Handler Interface: MerchantAdminAbstractChainHandler
- This interface defines the common behavior for each node in the chain of responsibility:
void handler(T requestParam): As the core method of the chain of responsibility, it defines how to handle the incoming request parameterrequestParam. Each implementation class implements its own processing logic in this method according to specific business needs, such as parameter validation, data transformation, etc. If an error occurs in a processing step, the execution of the chain can be interrupted by throwing an exception.String mark(): Returns the chain of responsibility identifier (Mark) to which the current handler belongs. Different chains can be grouped and managed by themark()value. For example, in the product listing creation chain,mark()can returnMERCHANT_ADMIN_CREATE_PRODUCT_TEMPLATE_KEY.int getOrder(): Used to define the execution order of the handler. By implementing thegetOrder()method of theOrderedinterface, developers can flexibly control the execution order of each handler in the chain. The default value isOrdered.LOWEST_PRECEDENCE(lowest priority), and this method can be overridden to return a higher priority (smaller numerical value means higher priority) as needed.
(2) Chain of Responsibility Context: MerchantAdminChainContext
- This class is responsible for managing the initialization and execution of the chain of responsibility:
- When the Spring container starts (
CommandLineRunner), it scans all Spring Beans that implement theMerchantAdminAbstractChainHandlerinterface and categorizes them into different chains based on theirmark()attribute. - Within the chain, handlers are sorted according to the priority of
Ordered. - Provides a unified
handler()method to execute the corresponding chain of responsibility based on the identifier (Mark).
- When the Spring container starts (
(3) Business Service Layer: ProductInventoryCheckChainFilter
- Calls the corresponding chain of responsibility through
MerchantAdminChainContextto complete the business parameter validation logic. - After the chain of responsibility completes validation, other specific business logic can continue to be executed subsequently.
Execution Flow of the Chain of Responsibility
With MerchantAdminChainContext, the above two handlers are automatically scanned and loaded into the chain of responsibility. At runtime, the system automatically executes them in order based on the values of mark() and getOrder().
Chain of Responsibility Execution Flow
5. Java Implementation of Chain of Responsibility Pattern + Strategy Pattern
Below is a complete Java example implementing the Chain of Responsibility + Strategy Pattern.
Scenario: Simulate a user request approval process (such as normal user approval, admin approval, super admin approval), combined with different strategies to handle requests.
1. Define the Interface for Handling Requests
2. Define the User Request Class
3. Define Different Strategies (Processing Logic)
4. Implement the Handler for the Chain of Responsibility Pattern
5. Test the Chain of Responsibility + Strategy Pattern
6. Why Combine the Chain of Responsibility and Strategy Patterns?
- Separation of Flow Control and Logic Definition: The Chain of Responsibility pattern connects the logic for handling requests into a chain, making it easy to dynamically adjust the request transmission flow; the Strategy pattern encapsulates the processing logic into independent strategies, which can be flexibly reused and replaced, making flow control and specific logic processing independent of each other.
- Clear Separation of Responsibilities: The Chain of Responsibility pattern focuses on managing the transmission of requests, while the Strategy pattern focuses on implementing specific business logic. Combining the two makes the code structure clearer, the allocation of responsibilities more explicit, and improves code maintainability.
- Enhanced System Flexibility and Scalability: The chain of responsibility can dynamically add or remove handlers, and strategies can dynamically select or extend new processing logic. The combination of the two greatly enhances the system's adaptability and scalability, enabling the system to better cope with ever-changing business needs.
By combining the Chain of Responsibility pattern with the Strategy pattern, it is possible to effectively handle complex processing flows and changing business needs while maintaining code simplicity and a highly cohesive design structure, providing an efficient solution for software development.