前言
本文主要阐述下commons-chain的一些思想和使用,主要内容来自commons-chain的cookbook,有些英文优美的我都不忍心翻译。
The essence of computing might be that for any expected input (A), we return the expected output (B). The challenge is getting from (A) to (B). For a simple program, (A) to (B) might be a single transformation. Say, shifting a character code 32 digits so that “a” becomes “A”. In a complex application, A to B can be a long and winding road.
To be useful, most applications need to run a process and then tell the client what happened. In practice, we find mixing “running” and “telling” together creates code that can be hard to test and maintain. If we can have one component run (or execute) the process, and another component report (or present) the result, then we can test, create, and maintain each component separately. But, how can we cleanly separate the execution and presentation layers without complicating the design of an application?
Most application frameworks, especially web application frameworks, rely on the Command pattern. An incoming HTTP request is mapped to some type of “command” object. The command object takes whatever action is required, using information passed in the HTTP request.
大部分application frameworks使用的是命令模式,这里举HTTP的例子不太恰当,观察一下FTP的java实现,可以清晰的发现其完全基于命令模式。
In practice, there are usually commands within commands. A Command object in a web application often looks like a sandwich. First, it does some things for the benefit of the presentation layer, then it executes the business logic, and then it does some more presentation layer things. The problem many developers face is how to cleanly separate the business logic in the middle of a web command from other necessary tasks that are part of the request/response transaction.
在实践中,一个Command类通常很复杂,贯穿显示层、业务逻辑层和其它必要的任务。我们面临的问题是,将位于中间位置的业务逻辑代码从request/response的组装和写出中隔离出来。
The Chain of Responsibility package combines the Command pattern with the classic Chain of Responsibility pattern(将命令模式与经典的责任链模式结合起来) to make it easy to call a business command as part of a larger application command.
To implement the patterns, the Chain package defines five key interfaces:
- Context
- Command
- Chain
- Filter
- Catalog
Context. A Context represents the state of an application. In the Chain package, Context is a marker interface for a java.util.Map. The Context is an envelope containing the attributes needed to complete a transaction. In other words, a Context is a stateful object with member values.
Command. A Command represents a unit of work. A Command has a single entry method: public boolean execute(Context context). A Command acts upon the state passed to it through a context object, but retains no state of its own.(一个command在一个传给它的state上工作,但自身是无状态的) Commands may be assembled into a Chain, so that a complex transaction can be created from discrete(离散的、不连续的) units of work. If a Command returns true, then other Commands in a Chain should not be executed. If a Command returns false, then other Commands in the Chain (if any) may execute.
Chain.Chain implements the Command interface, so a Chain can be used interchangeably(可交换的) with a Command. An application doesn’t need to know if it’s calling a Chain or a Command, so you can refactor from one to the other. A Chain can nest other Chains as desired. This property is known as the Liskov substitution principle.
Filter. Ideally, every command would be an island. In real life, we sometimes need to allocate resources and be assured the resources will be released no matter what happens. A Filter is a specialized Command that adds a postProcess method. A Chain is expected to call the postProcess method of any filters in the chain before returning. A Command that implements Filter can safely release any resources it allocated through the postProcess method, even if those resources are shared with other Commands.
Catalog. Many applications use “facades” and “factories” and other techniques to avoid binding layers too closely together. Layers need to interact, but often we don’t want them to interact at the classname level. A Catalog is a collection of logically named Commands (or Chains) that a client can execute, without knowing the Command’s classname.
详解介绍
ChainBase
维护一个Command数组,触发Command执行。责任链模式的经典实现,不再赘述。
比较有意思的是,Command完全执行完毕后或抛出异常时,触发Filter(跟Web中的Filter不同)执行,一般用来释放资源。
catalogBase
public interface Catalog {
void addCommand(String name, Command command);
Command getCommand(String name);
Iterator getNames();
}
以name的形式来管理Command
contextBase
public interface Context extends Map {}
Many components already use a “context”.Architects will often choose a Map-style context because they are easy to implement and very easy to extend. Usually, developers can add their own entries to a Map-style context at will.Other components also use what amounts to a context but predefine the entries as object properties.(通常一个context是map-style,但是呢,有时候想事先定义好一些key)
ContextBase,In addition to the minimal functionality required by the Context interface, this class implements the recommended support for Attribute-Property Transparency. This is implemented by analyzing the available JavaBeans properties of this class (or its subclass), exposes them as key-value pairs in the Map,with the key being the name of the property itself.
举个例子
public class UserContext extends ContextBase{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class TestUserContext {
@Test
public void testGet(){
UserContext context = new UserContext();
context.setName("abc");
System.out.println(context.get("name"));
}
}
此处,我们setName时,name ==> abc 就加入到了map中。
简单说就是,context有很多种风格,Map-Style或者JavaBean,ContextBase将两者结合起来,在predefine一些属性的同时,保留了map的扩展性。
其它
commons-chain中还提供了许多web工具类,用来与Web开发集成。
通过commons-digester与commons-chain结合,可以将Chain的生成配置化。参见commons-chain 应用记录
引用
http://commons.apache.org/proper/commons-chain/cookbook.html