四、Chain of Responsibility(责任链)
描述:一系列类(classes)试图处理一个请求request,这些类之间是一个松散的耦合,唯一共同点是在他们之间传递request. 也就是说,来了一个请求,A类先处理,如果没有处理,就传递到B类处理,如果没有处理,就传递到C类处理,就这样象一个链条(chain)一样传递下去好处:降低了类之间的耦合性例子:
抽象处理者角色public abstract class Handler { /** * 持有后继的责任对象 */ protected Handler successor; /** * 示意处理请求的方法,虽然这个示意方法是没有传入参数的 * 但实际是可以传入参数的,根据具体需要来选择是否传递参数 */ public abstract void handleRequest(); /** * 取值方法 */ public Handler getSuccessor() { return successor; } /** * 赋值方法,设置后继的责任对象 */ public void setSuccessor(Handler successor) { this.successor = successor; }}具体处理者角色
public class ConcreteHandler extends Handler { /** * 处理方法,调用此方法处理请求 */ @Override public void handleRequest() { /** * 判断是否有后继的责任对象 * 如果有,就转发请求给后继的责任对象 * 如果没有,则处理请求 */ if(getSuccessor() != null) { System.out.println("放过请求"); getSuccessor().handleRequest(); }else { System.out.println("处理请求"); } }}
客户端类
public class Client {public static void main(String[] args) {
//组装责任链 Handler handler1 = new ConcreteHandler(); Handler handler2 = new ConcreteHandler(); handler1.setSuccessor(handler2); //提交请求 handler1.handleRequest(); }}
可以看出,客户端创建了两个处理者对象,并指定第一个处理者对象的下家是第二个处理者对象,而第二个处理者对象没有下家。然后客户端将请求传递给第一个处理者对象。 由于本示例的传递逻辑非常简单:只要有下家,就传给下家处理;如果没有下家,就自行处理。因此,第一个处理者对象接到请求后,会将请求传递给第二个处理者对象。由于第二个处理者对象没有下家,于是自行处理请求。五、Command(命令模式)
描述:对命令进行封装,将发出命令的责任和执行命令的责任分割开好处:面向接口编程、能实现Undo功能。例子:
典型的Command模式需要有一个接口.接口中有一个统一的方法,这就是"将命令/请求封装为对象":public interface Command {
public abstract void execute ( );} 具体不同命令/请求代码是实现接口Command,下面有三个具体命令public class Engineer implements Command {
public void execute( ) {
//do Engineer's command }}public class Programmer implements Command {
public void execute( ) {
//do programmer's command }}public class Politician implements Command {
public void execute( ) {
//do Politician's command }}
按照通常做法,我们就可以直接调用这三个Command,但是使用Command模式,我们要将他们封装起来,扔到黑盒子List里去:
public class producer{
public static List produceRequests() { List queue = new ArrayList(); queue.add( new DomesticEngineer() ); queue.add( new Politician() ); queue.add( new Programmer() ); return queue; }}
这三个命令进入List中后,已经失去了其外表特征,以后再取出,也可能无法分辨出谁是Engineer 谁是Programmer了,看下面如何调用Command模式:
public class TestCommand {
public static void main(String[] args) { List queue = Producer.produceRequests(); for (Iterator it = queue.iterator(); it.hasNext(); ) //取出List中东东,其他特征都不能确定,只能保证一个特征是100%正确, // 他们至少是接口Command的"儿子".所以强制转换类型为接口Command((Command)it.next()).execute();
}
}由此可见,调用者基本只和接口打交道,不合具体实现交互,这也体现了一个原则,面向接口编程,这样,以后增加第四个具体命令时,就不必修改调用者TestCommand中的代码了.
六、State(状态模式)
描述:不同的状态,不同的行为;或者说,每个状态有着相应的行为优点:封装转换过程,也就是转换规则、枚举可能的状态,因此,需要事先确定状态种类。例子:
请看下例:public class Context{
private Color state=null;
public void push(){
//如果当前red状态 就切换到blue
if (state==Color.red) state=Color.blue;//如果当前blue状态 就切换到green
else if (state==Color.blue) state=Color.green;//如果当前black状态 就切换到red
else if (state==Color.black) state=Color.red;//如果当前green状态 就切换到black
else if (state==Color.green) state=Color.black; Sample sample=new Sample(state); sample.operate(); }public void pull(){
//与push状态切换正好相反
if (state==Color.green) state=Color.blue;
else if (state==Color.black) state=Color.green; else if (state==Color.blue) state=Color.red; else if (state==Color.red) state=Color.black;Sample2 sample2=new Sample2(state);
sample2.operate(); }}
在上例中,我们有两个动作push推和pull拉,这两个开关动作,改变了Context颜色,至此,我们就需要使用State模式优化它.
另外注意:但就上例,state的变化,只是简单的颜色赋值,这个具体行为是很简单的,State适合巨大的具体行为,因此在,就本例,实际使用中也不一定非要使用State模式,这会增加子类的数目,简单的变复杂.
例如: 银行帐户, 经常会在Open 状态和Close状态间转换.
例如: 经典的TcpConnection, Tcp的状态有创建 侦听 关闭三个,并且反复转换,其创建 侦听 关闭的具体行为不是简单一两句就能完成的,适合使用State
例如:信箱POP帐号, 会有四种状态, start HaveUsername Authorized quit,每个状态对应的行为应该是比较大的.适合使用State
例如:在工具箱挑选不同工具,可以看成在不同工具中切换,适合使用State.如 具体绘图程序,用户可以选择不同工具绘制方框 直线 曲线,这种状态切换可以使用State.
如何使用
State需要两种类型实体参与:1.state manager 状态管理器 ,就是开关 ,如上面例子的Context实际就是一个state manager, 在state manager中有对状态的切换动作.
2.用抽象类或接口实现的父类,,不同状态就是继承这个父类的不同子类.以上面的Context为例.我们要修改它,建立两个类型的实体.
第一步: 首先建立一个父类:public abstract class State{
public abstract void handlepush(Context c);
public abstract void handlepull(Context c); public abstract void getcolor();}
父类中的方法要对应state manager中的开关行为,在state manager中 本例就是Context中,有两个开关动作push推和pull拉.那么在状态父类中就要有具体处理这两个动作:handlepush() handlepull(); 同时还需要一个获取push或pull结果的方法getcolor()
下面是具体子类的实现:
public class BlueState extends State{
public void handlepush(Context c){
//根据push方法"如果是blue状态的切换到green" ; c.setState(new GreenState());}
public void handlepull(Context c){//根据pull方法"如果是blue状态的切换到red" ;
c.setState(new RedState());}
public abstract void getcolor(){ return (Color.blue)}
}
同样 其他状态的子类实现如blue一样.
第二步: 要重新改写State manager 也就是本例的Context:
public class Context{
private Sate state=null; //我们将原来的 Color state 改成了新建的State state;
//setState是用来改变state的状态 使用setState实现状态的切换
pulic void setState(State state){this.state=state;
}
public void push(){
//状态的切换的细节部分,在本例中是颜色的变化,已经封装在子类的handlepush中实现,这里无需关心
state.handlepush(this); //因为sample要使用state中的一个切换结果,使用getColor() Sample sample=new Sample(state.getColor()); sample.operate();}
public void pull(){
state.handlepull(this);
Sample2 sample2=new Sample2(state.getColor()); sample2.operate();}
}
至此,我们也就实现了State的refactorying过程