commons chain 是命令与职责链模式的经典实现 commons chain
基本概念
它有如下概念:
- Context
- Command
- Chain
- Filter
- Catalog
1) Context 上下文
Context 表示一个应用程序的状态, 它其实是一个 java.util.map 的标记接口, 上下文是类似一个信封, 包含了完成一个事务所需的属性, 换句话说, 上下文是一个包含成员属性值的有状态对象
2) Command 命令
一个命令代表一个工作单元, 它只有一个入口方法
public boolean execute(Context context)
命令对通过上下文对象传递给它的状态进行操作,但不保留它自己的状态。命令可以组装成一个链,这样就可以从离散的工作单元创建复杂的事务。如果命令返回true,则不应执行链中的其他命令。如果一个命令返回false,那么链中的其他命令(如果有的话)可能会执行。
3) Chain 职责链
chain实现了命令接口,因此chain可以与命令互换使用。应用程序不需要知道它是在调用链还是命令,所以您可以从一个重构到另一个。chain 可以根据需要套入其他链条。这一性质被称为Liskov替换原理。
4) Filter 过滤器
理想情况下,每个command 都是独立的。可在现实生活中,我们有时需要分配资源,并确保不管发生什么,资源都会被释放. Filter 过滤器是一种特殊的命令, 它增加了一个后处理方法 postProcess 。在命令返回之前,职责链 Chain 会调用其中任何 Filter 的 postProcess 方法。一个实现了 Filter 的命令可以通过 postProcess 方法安全地释放它分配的任何资源,即使这些资源是与其他命令共享的
5) Catalog 目录
许多应用程序使用“facades”和“factories”以及其他技术来避免将层与层耦合在一起。层与层之间需要交互,但通常我们不希望它们在类名级别交互。目录是一组命令或链的逻辑上的名称,客户端可以在不知道命令的类名的情况下执行这些命令。
举例如下
package com.cisco.yafan.demo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.chain.Command;
import org.apache.commons.chain.Context;
import org.apache.commons.chain.impl.ChainBase;
import org.apache.commons.chain.impl.ContextBase;
import org.junit.Test;
import static junit.framework.TestCase.assertTrue;
import static org.junit.Assert.assertFalse;
@Slf4j
class IvrCommand implements Command {
private String name;
public IvrCommand(String name) {
this.name = name;
}
@Override
public boolean execute(Context context) throws Exception {
log.info("execute: {} for {}", name, context.get("phoneNumber"));
if("please input your password".equals(name)) {
if("pass".equals(context.get("password"))) {
context.put("balance", 10000);
} else {
log.info("your password is incorrect.");
return true;
}
}
return false;
}
}
class IvrFlow extends ChainBase {
public IvrFlow() {
addCommand(new IvrCommand("welcome"));
addCommand(new IvrCommand("please input account number"));
addCommand(new IvrCommand("please input your password"));
addCommand(new IvrCommand("your account balance is 10000"));
addCommand(new IvrCommand("goodbye"));
}
}
@Slf4j
public class CommonsChainTest {
@Test
public void testIvrFlowCorrectPassword() throws Exception {
Context context = new ContextBase();
context.put("phoneNumber" , "86-123456789");
context.put("password" , "pass");
IvrFlow ivrFlow = new IvrFlow();
ivrFlow.execute(context);
assertTrue(Integer.valueOf("10000").equals(context.get("balance")));
}
@Test
public void testIvrFlowIncorrectPassword() throws Exception {
Context context = new ContextBase();
context.put("phoneNumber" , "86-123456789");
context.put("password" , "abcd");
IvrFlow ivrFlow = new IvrFlow();
ivrFlow.execute(context);
assertFalse(Integer.valueOf("10000").equals(context.get("balance")));
}
}
结果输出如下
#testIvrFlowCorrectPassword
execute: welcome for 86-123456789
execute: please input account number for 86-123456789
execute: please input your password for 86-123456789
execute: your account balance is 10000 for 86-123456789
execute: goodbye for 86-123456789
#testIvrFlowIncorrectPassword
execute: welcome for 86-123456789
execute: please input account number for 86-123456789
execute: please input your password for 86-123456789
your password is incorrect.
网友评论