当前位置:网站首页>Advanced usage of the responsibility chain pattern
Advanced usage of the responsibility chain pattern
2022-07-19 02:56:00 【The sound of the waves is still loud】
One 、 The opening
What knowledge can you learn by reading this article ?
- Combined with specific cases , appreciate The chain of responsibility model The charm of .
- The realization of responsibility chain model Process planning 、 Dynamic expansion .
- Use
Sping
@Resource
Note the injection of Operation . - Use A recursive algorithm Set up responsibility link .
Two 、 brief introduction
The chain of responsibility model , In short , Is to assemble multiple operations into a link for processing . The request is passed on the link , Every node on the link is a processor , Each processor can process requests , Or pass it to the next processor on the link for processing .
3、 ... and 、 Application scenarios
The application scenario of the responsibility chain pattern , In practice , There are usually two application scenarios .
- The operation needs to go through a series of checks , After passing the verification, some operations are performed .
- workflow . Enterprises usually develop many workflow , Deal with tasks level by level .
Let's learn about the responsibility chain model through two cases .
Case a : Create a multi-level verification scenario
Take creating goods as an example , Suppose that the logic of product creation is divided into the following three steps :① Create merchandise 、② Verify the product parameters 、③ Save item .
The first ② Step verification of goods is divided into verification of various situations , Required field verification 、 Specification verification 、 Price check 、 Inventory verification, etc . These inspection logics are like an assembly line , To create a product , Must pass these checks . As shown in the flow chart below :
The pseudocode is as follows :
Create product steps , It needs to go through a series of parameter verification , If parameter validation fails , Directly return the result of failure ; After all parameters are verified , Finally save the product information .
The above code seems to be ok , It's very neat , And the code logic is very clear .(PS: I didn't list all the verification codes in one method , That's more comparable , But I think Abstract and separate functions with a single responsibility It should be the most basic specification for every programmer !)
But as business needs continue to stack , There are more and more related verification logic , New functions make the code more and more Overstaffed , Poor maintainability . What's worse is , These calibration components Do not reuse , When you have other requirements and need to use some verification , You have become Ctrl+C
, Ctrl+V
The programmer , The maintenance cost of the system is also getting higher . As shown in the figure below :
Pseudo code is the same as above , I won't repeat it here .
One day at last , You can't stand it , decision Refactor this code .
Use the responsibility chain model to optimize : Each verification step of creating an item can be used as a separate processor , Separate into a single class , Easy Reuse . These processors form a call chaining , Requests are passed on the processor chain , If the verification conditions fail , Then the processor will no longer pass down requests , Return error message directly ; If all processors pass the test , Then execute the steps of saving goods .
Case 1 actual combat : The responsibility chain mode realizes the creation of commodity verification
UML chart : The hills are small
AbstractCheckHandler
Represents the processor abstract class , Responsible for abstracting processor behavior . Its have 3 A subclass , Namely :
NullValueCheckHandler
: Null check processorPriceCheckHandler
: Price verification processingStockCheckHandler
: Inventory verification processor
AbstractCheckHandler
In an abstract class , handle()
Defines the abstract method of the processor , Its subclasses need rewrite handle()
Method to implement special processor verification logic ;
protected ProductCheckHandlerConfig config
Is the dynamic configuration class of the processor , Use protected
Statement , Each subclass processor holds the object . This object is used to declare Current processor 、 And the current processor Next processor nextHandler
, In addition, you can configure some special properties , for instance Interface degradation
To configure 、 Timeout time
Configuration etc. .
AbstractCheckHandler nextHandler
Is the reference of the next processor held by the current processor , When the current processor finishes executing , Then call nextHandler
Execute the next processor handle()
Verification method ;
protected Result next()
Is defined in an abstract class , Execute the method of the next processor , Use protected
Statement , Each subclass processor holds the object . When the subclass processor finishes executing ( adopt ) when , Call the method of the parent class to execute the next processor nextHandler
.
HandlerClient
Is the client that executes the processor link ,HandlerClient.executeChain()
Method is responsible for initiating the whole link call , And receive the return value of the processor link .
Roll up your sleeves and start rolling up the code ~
Commodity parameter object : Save the input parameters of the product
ProductVO
It is the parameter object of creating goods , Contains the basic information of the product . And it is the input of multiple processors in the responsibility chain mode , Multiple processors are in ProductVO
Perform specific logical processing for input parameters . In real business , Commodity objects are particularly complex . Let's simplify things , The simplified product parameters are as follows :
/** * Commodity object */
@Data
@Builder
public class ProductVO {
/** * goods SKU, only */
private Long skuId;
/** * Name of commodity */
private String skuName;
/** * Product picture path */
private String imgPath;
/** * Price */
private BigDecimal price;
/** * stock */
private Integer stock;
}
Abstract class processor : Abstract behavior , Subclasses have common attributes 、 Method
AbstractCheckHandler
: Processor abstract class , And use @Component
The annotation is registered as Spring
Managed Bean
object , The advantage of this is , We can use it easily Spring
To manage these processors Bean
.
/** * Abstract class processor */
@Component
public abstract class AbstractCheckHandler {
/** * The current processor holds a reference to the next processor */
@Getter
@Setter
protected AbstractCheckHandler nextHandler;
/** * Processor configuration */
@Setter
@Getter
protected ProductCheckHandlerConfig config;
/** * Processor execution method * @param param * @return */
public abstract Result handle(ProductVO param);
/** * Link delivery * @param param * @return */
protected Result next(ProductVO param) {
// The next link has no processor , Go straight back to
if (Objects.isNull(nextHandler)) {
return Result.success();
}
// Execute the next processor
return nextHandler.handle(param);
}
}
stay AbstractCheckHandler
Abstract class processor , Use protected
Declare the visible properties and methods of subclasses . Use @Component annotation
, Declare it as Spring
Of Bean
object , The advantage of this is that you can take advantage of Spring
Easily manage all subclasses , Below you will see how to use . The properties and methods of the abstract class are described as follows :
public abstract Result handle()
: Represents an abstract verification method , Every processor should inheritAbstractCheckHandler
Abstract class processor , Pay equal attention to write themhandle
Method , Each processor thus realizes special verification logic , It's actually polymorphic Thought .protected ProductCheckHandlerConfig config
: Represents the dynamics of each processor Configuration class , Can pass “ Configuration center ” Dynamically modify the configuration , Implement processor “ Dynamic arrangement ” and “ Sequence control ”. Configuration class can be configured The name of the processor 、 Next processor 、 as well as processor Whether to downgrade Equal attribute .protected AbstractCheckHandler nextHandler
: Indicates that the current processor holds a reference to the next processor , If the current processorhandle()
The verification method is completed , Then execute the next processornextHandler
Ofhandle()
The verification method executes the verification logic .protected Result next(ProductVO param)
: This method is used for processor link delivery , After the subclass processor finishes executing , Calling thenext()
The method is implemented inconfig
The next processor on the configured link , If all processors are finished , The result is returned .
ProductCheckHandlerConfig Configuration class
:
/** * Processor configuration class */
@AllArgsConstructor
@Data
public class ProductCheckHandlerConfig {
/** * processor Bean name */
private String handler;
/** * Next processor */
private ProductCheckHandlerConfig next;
/** * Whether to downgrade */
private Boolean down = Boolean.FALSE;
}
Subclass processor : Handle the unique verification logic
AbstractCheckHandler
Abstract class processors have 3 The subcategories are :
NullValueCheckHandler
: Null check processorPriceCheckHandler
: Price verification processingStockCheckHandler
: Inventory verification processor
Each processor inherits AbstractCheckHandler
Abstract class processor , Pay equal attention to write them handle()
Processing method to realize the unique verification logic .
NullValueCheckHandler
: Null check processor . Pertinence check the required parameters in the creation of goods . If the verification fails , The error code is returnedErrorCode
, The chain of responsibility is broken here ( stop it ), Create a product and return the verified error message . Note the degraded configuration in the code !super.getConfig().getDown()
Is to obtainAbstractCheckHandler
Configuration information stored in the processor object , If the processor is configured with degradation , Then skip the processor , callsuper.next()
Execute the next processor logic .
Again , Use @Component
Registered as Spring
Managed Bean
object ,
/** * Null check processor */
@Component
public class NullValueCheckHandler extends AbstractCheckHandler{
@Override
public Result handle(ProductVO param) {
System.out.println(" Null check Handler Start ...");
// Downgrade : If downgrade is configured , Then skip this processor , Execute the next processor
if (super.getConfig().getDown()) {
System.out.println(" Null check Handler Degraded , Skip null check Handler...");
return super.next(param);
}
// Parameter is required
if (Objects.isNull(param)) {
return Result.failure(ErrorCode.PARAM_NULL_ERROR);
}
//SkuId Commodity primary key parameter is required
if (Objects.isNull(param.getSkuId())) {
return Result.failure(ErrorCode.PARAM_SKU_NULL_ERROR);
}
//Price Price parameter must be verified
if (Objects.isNull(param.getPrice())) {
return Result.failure(ErrorCode.PARAM_PRICE_NULL_ERROR);
}
//Stock Inventory parameter is required
if (Objects.isNull(param.getStock())) {
return Result.failure(ErrorCode.PARAM_STOCK_NULL_ERROR);
}
System.out.println(" Null check Handler adopt ...");
// Execute the next processor
return super.next(param);
}
}
PriceCheckHandler
: Price verification processing . Verify the price parameters of the created goods . Here is just a simple price judgment >0 The check , The actual business is more complicated , such as “ Price door ” These preventive measures .
/** * Price verification processor */
@Component
public class PriceCheckHandler extends AbstractCheckHandler{
@Override
public Result handle(ProductVO param) {
System.out.println(" Price check Handler Start ...");
// Illegal price verification
boolean illegalPrice = param.getPrice().compareTo(BigDecimal.ZERO) <= 0;
if (illegalPrice) {
return Result.failure(ErrorCode.PARAM_PRICE_ILLEGAL_ERROR);
}
// Other verification logic ...
System.out.println(" Price check Handler adopt ...");
// Execute the next processor
return super.next(param);
}
}
StockCheckHandler
: Inventory verification processor . Verify the inventory parameters of the created goods .
/** * Inventory verification processor */
@Component
public class StockCheckHandler extends AbstractCheckHandler{
@Override
public Result handle(ProductVO param) {
System.out.println(" Inventory verification Handler Start ...");
// Illegal inventory verification
boolean illegalStock = param.getStock() < 0;
if (illegalStock) {
return Result.failure(ErrorCode.PARAM_STOCK_ILLEGAL_ERROR);
}
// Other verification logic ..
System.out.println(" Inventory verification Handler adopt ...");
// Execute the next processor
return super.next(param);
}
}
client : Execute processor link
HandlerClient
The client class is responsible for initiating the execution of the entire processor link , adopt executeChain()
Method . If the processor link returns an error message , That is, the verification fails , Then the entire link is truncated ( stop it ), Return the corresponding error message .
public class HandlerClient {
public static Result executeChain(AbstractCheckHandler handler, ProductVO param) {
// Execution processor
Result handlerResult = handler.handle(param);
if (!handlerResult.isSuccess()) {
System.out.println("HandlerClient If the execution of the responsibility chain fails, return :" + handlerResult.toString());
return handlerResult;
}
return Result.success();
}
}
above , The classes related to the responsibility chain pattern have been created . Next, you can create products .
Create merchandise : Abstract steps , Change numerous for brief
createProduct()
The method of creating goods is abstracted as 2 A step :① Parameter checking
、② Create merchandise
. Parameter verification uses the responsibility chain mode for verification , contain : Null check
、 Price check
、 Inventory verification
wait , Only all processors on the chain pass the verification , Only called saveProduct()
Create product method ; Otherwise, the verification error message is returned . stay createProduct()
In the method of creating goods , adopt The chain of responsibility model , We will check the logic decoupling .createProduct()
In the method of creating goods No need to pay attention Which verification processors must pass , And the details of the verification processor .
/** * Create merchandise * @return */
@Test
public Result createProduct(ProductVO param) {
// Parameter checking , Use the responsibility chain model
Result paramCheckResult = this.paramCheck(param);
if (!paramCheckResult.isSuccess()) {
return paramCheckResult;
}
// Create merchandise
return this.saveProduct(param);
}
Parameter checking : The chain of responsibility model
Parameter checking paramCheck()
Method use the responsibility chain mode to verify the parameters , The method does not declare the specific verifications , Which parameter verification logic is through multiple Processor chain passing Of . as follows :
/** * Parameter checking : The chain of responsibility model * @param param * @return */
private Result paramCheck(ProductVO param) {
// Get processor configuration : Generally, the unified configuration center is used for storage , Support dynamic change
ProductCheckHandlerConfig handlerConfig = this.getHandlerConfigFile();
// Get processor
AbstractCheckHandler handler = this.getHandler(handlerConfig);
// Responsibility chain : Execute processor link
Result executeChainResult = HandlerClient.executeChain(handler, param);
if (!executeChainResult.isSuccess()) {
System.out.println(" Create merchandise Failure ...");
return executeChainResult;
}
// Processor links are all successful
return Result.success();
}
paramCheck()
The method steps are described as follows :
step 1: Get processor configuration .
adopt getHandlerConfigFile()
Method to get the processor configuration class object , The configuration class saves the configuration of the upper and lower nodes of each processor in the chain , Support Process planning 、 Dynamic expansion . Usually, the configuration is through Ducc( JD self developed configuration center )
、Nacos( Alibaba open source configuration center )
And so on , Support Dynamic change 、 In real time . Based on this , We can realize the arrangement of the verification processor 、 And dynamically expand . I do not use the configuration of the storage processor link in the configuration center , But use JSON
Simulate configuration in the form of string , What you are interested in can be realized by yourself .
/** * Get processor configuration : Generally, the unified configuration center is used for storage , Support dynamic change * @return */
private ProductCheckHandlerConfig getHandlerConfigFile() {
// Configuration stored in the configuration center
String configJson = "{\"handler\":\"nullValueCheckHandler\",\"down\":true,\"next\":{\"handler\":\"priceCheckHandler\",\"next\":{\"handler\":\"stockCheckHandler\",\"next\":null}}}";
// Turn into Config object
ProductCheckHandlerConfig handlerConfig = JSON.parseObject(configJson, ProductCheckHandlerConfig.class);
return handlerConfig;
}
ConfigJson
Storage processor link configuration JSON
strand , It may not be easy to see in the code , We can use json.cn Wait for formatting , as follows , The rules of the whole call link configured are particularly clear .
getHandlerConfigFile()
The structure of the configuration class obtained by the class is as follows , You can see , The configuration rules stored in the configuration center , Convert to configuration class ProductCheckHandlerConfig
object , For program processing . Be careful , At this time, only the processor is stored in the configuration class Spring
Bean
Of name
nothing more , Not an actual processor object .
Next , Get the actual processor to be executed through the configuration class .
step 2: Get the processor according to the configuration .
above step 1
adopt getHandlerConfigFile()
Method after obtaining the processor link configuration rules , Call again getHandler()
Get processor .getHandler()
The parameters are as above ConfigJson
Configured rules , namely step 1
Converted ProductCheckHandlerConfig
object ; according to ProductCheckHandlerConfig
The configuration rule is converted to Processor link object . The code is as follows :
/** * Use Spring Inject : All inherited AbstractCheckHandler The abstract class Spring Bean It's going to pour in .Map Of Key Corresponding Bean Of name,Value yes name Corresponding to the corresponding Bean */
@Resource
private Map<String, AbstractCheckHandler> handlerMap;
/** * Get processor * @param config * @return */
private AbstractCheckHandler getHandler (ProductCheckHandlerConfig config) {
// Configuration check : The processor link is not configured , The verification logic is not executed
if (Objects.isNull(config)) {
return null;
}
// Configuration error
String handler = config.getHandler();
if (StringUtils.isBlank(handler)) {
return null;
}
// Configured a non-existent processor
AbstractCheckHandler abstractCheckHandler = handlerMap.get(config.getHandler());
if (Objects.isNull(abstractCheckHandler)) {
return null;
}
// Processor settings configuration Config
abstractCheckHandler.setConfig(config);
// Set the link processor recursively
abstractCheckHandler.setNextHandler(this.getHandler(config.getNext()));
return abstractCheckHandler;
}
step 2-1: Configuration check .
Code 14~27 That's ok , Some check operations of configuration are carried out . If the configuration is wrong , Then the corresponding processor cannot be obtained . Code 23 That's ok handlerMap.get(config.getHandler())
Is mapped from all processors Map
Get the corresponding processor in Spring Bean
.
Pay attention to the 5 Line code ,handlerMap
All processor mappings are stored , It's through Spring
@Resource
Annotation injection Come in . The rule of injection is : All inherited AbstractCheckHandler
abstract class ( It is Spring Managed Bean) Subclasses of ( Subclasses are also Spring Managed Bean) It's going to pour in .
Infused with handlerMap
in Map
Of Key
Corresponding Bean
Of name
,Value
yes name
Corresponding Bean
example , That is, the actual processor , Here it means Null check processor 、 Price verification processor 、 Inventory verification processor . as follows :
According to the configuration ConfigJson
( step 1: Get processor configuration
) in handler:"priceCheckHandler"
Configuration of , Use handlerMap.get(config.getHandler())
You can get the corresponding processor Spring
Bean
Object .
step 2-2: Save processor rules .
Code 29 That's ok , Save the configuration rule to the corresponding processor abstractCheckHandler.setConfig(config)
, Subclass processors hold the rules of configuration .
step 2-3: Recursively set the processor link .
Code 32 That's ok , Recursively set the processor on the link .
// Set the link processor recursively abstractCheckHandler.setNextHandler(this.getHandler(config.getNext()));
This step may not be easy to understand , combination ConfigJson
According to the configured rules , It seems very easy to understand .
From top to bottom ,NullValueCheckHandler
The null check processor passed setNextHandler()
Method to set the processor of the next node that you own , That is, the price processor PriceCheckHandler
.
next ,PriceCheckHandler
Price processor , It also needs to go through step 2-1 Configuration check
、 step 2-2 Save configuration rules
, And most importantly , It also needs to set the processor of the next node StockCheckHandler
Inventory verification processor .
StockCheckHandler
The same is true for the inventory verification processor , It also needs to go through step 2-1 Configuration check
、 step 2-2 Save configuration rules
, But please pay attention to StockCheckHandler
Configuration of , its next
The rules are configured null
, This means that there is no processor to execute under it , It is the last processing node on the whole link .
Called recursively getHandler()
Get the processor method , The whole processor link object is connected in series . as follows :
Friendship tips : Recursion is fragrant , But when using recursion, we must pay attention to the condition processing of truncating recursion , Otherwise, it may cause a dead cycle !
actually ,getHandler()
obtain Processor object
The code of is to configure the rules in the configuration center ConfigJson
, Convert to configuration class ProductCheckHandlerConfig
object , Then according to the configuration class object , Convert to the actual processor object , This processor object Hold the call sequence of the whole link .
step 3: The client executes the call link .
public class HandlerClient {
public static Result executeChain(AbstractCheckHandler handler, ProductVO param) {
// Execution processor
Result handlerResult = handler.handle(param);
if (!handlerResult.isSuccess()) {
System.out.println("HandlerClient If the execution of the responsibility chain fails, return :" + handlerResult.toString());
return handlerResult;
}
return Result.success();
}
}
getHandler()
After getting the processor , The execution sequence of the whole call link is also determined , here , The client should work !
HandlerClient.executeChain(handler, param)
The method is HandlerClient
The client class executes the entire call link of the processor , And receive the return value of the processor link .
executeChain()
adopt AbstractCheckHandler.handle()
Trigger the whole link processor to execute in sequence , If a processor fails the verification !handlerResult.isSuccess()
, Error message returned ; All processors passed the verification , Then the correct information is returned Result.success()
.
summary : Cascade method call process
Based on the above , Then review the whole call process through the flowchart .
test : Code execution result
scene 1:
Create merchandise
There is a null value in the parameter ( as followsskuId
Parameter isnull
), The link was truncated by the null processor , Return error message
// Create product parameters
ProductVO param = ProductVO.builder()
.skuId(null).skuName(" Huawei mobile phones ").imgPath("http://...")
.price(new BigDecimal(1))
.stock(1)
.build();
test result
scene 2:
Create merchandise
Abnormal price parameters ( as followsprice
Parameters ), Truncated by price processor , Return error message
ProductVO param = ProductVO.builder()
.skuId(1L).skuName(" Huawei mobile phones ").imgPath("http://...")
.price(new BigDecimal(-999))
.stock(1)
.build();
test result
scene 3:
Create merchandise
Abnormal inventory parameters ( as followsstock
Parameters ), Truncated by inventory processor , Return error message .
// Create product parameters , Simulate user incoming
ProductVO param = ProductVO.builder()
.skuId(1L).skuName(" Huawei mobile phones ").imgPath("http://...")
.price(new BigDecimal(1))
.stock(-999)
.build();
test result
scene 4:
Create merchandise
All processors passed the verification , Save item .
// Create product parameters , Simulate user incoming
ProductVO param = ProductVO.builder()
.skuId(1L).skuName(" Huawei mobile phones ").imgPath("http://...")
.price(new BigDecimal(999))
.stock(1).build();
test result
Case 2 : workflow , Expense reimbursement review process
My colleague Xiao Jia just came back from a business trip recently , She can't wait to submit the expense reimbursement process . Depending on the amount , It is divided into the following audit processes . The reimbursement amount is less than 1000 element , The three-level department manager can approve ,1000 To 5000 In addition to the approval of three-level department managers , It also needs the approval of secondary department managers , and 5000 To 10000 Yuan also needs the approval of the first level department manager . There are the following situations :
- Xiao Jia needs to be reimbursed 500 element , The three-level department manager can approve .
- Xiao Jia needs to be reimbursed 2500 element , After the approval of the manager of the third level Department , It also needs the approval of secondary department managers , After the approval of the manager of the secondary Department , To complete the reimbursement approval process .
- Xiao Jia needs to be reimbursed 7500 element , After the approval of the three-level manager , And after the approval of the secondary manager , The process flows to the first level department manager for approval , After being approved by the first level manager , That is, the reimbursement process is completed .
### UML chart
AbstractFlowHandler
As a processor abstract class , Abstract the approve()
Audit method , Class A 、 second level 、 The third level department manager processor inherits the abstract class , Pay equal attention to write them approve()
Audit method , So as to realize the unique audit logic .
The configuration class is as follows , The processor of each layer should be configured with reviewers 、 Price review rules ( The biggest audit 、 Minimum amount )、 Next level handler . Configuration rules can be changed dynamically , If the amount that the third level department manager can audit increases to 2000 element , Modify the configuration to take effect dynamically .
The code implementation is similar to case 1 , If you are interested, please use your hands ~
Four 、 The advantages and disadvantages of the responsibility chain
5、 ... and 、 Source check
github:https://github.com/rongtao7/MyNotes
MyNotes: My summary notes
MyNotes-design:「 Actual design pattern 」 special column
- design-demo-chain: Practical responsibility chain mode
If you like this article , Please don't be stingy with your praise , It's not easy to create , thank !
边栏推荐
猜你喜欢
随机推荐
学习网络基础
Shell programming specifications and variables
2、AsyncTool框架实战使用
RHCE-ansible第二次作业
【NoSQL】NoSQL之redis配置与优化(简单操作)
梦开始的地方----初识c语言
OSPF comprehensive experiment
Conditional statement of shell script
二进制安装kubernetes 1.23.2
ENSP静态路由实验
Firewall firewall
PXE automated installation
PXE自动化安装
MySQL存储引擎详解
Keep two decimal places and take values upward
[solution] the local Group Policy Editor (gpedit.msc) in Win 11 cannot be opened
RHCE Study Guide Chapter 5 VIM editor
Rsync - remote synchronization
多层数据包结构及TCP三次握手
RIP综合实验