行为类模式大PK |
- 行为类模式包括责任链模式、命令模式、解释器模式、迭代器模式、中介者模式、备忘录模式、观察者模式、状态模式、策略模式、模板方法模式、访问者模式。该组设计模式众多,如下我们着重介绍一下命令模式VS策略模式、状态模式VS策略模式、观察者模式VS责任链模式。
命令模式VS策略模式 |
命令模式和策略模式类图很相似,只是命令模式多了一个接收者(Receiver)角色,通过确切的Command类调用Receiver类,实现了调用者和具体的接收者的解耦。策略模式的意图是封装算法,它认为“算法”已经是一个完整的、不可拆分的原子业务,通过对算法的封装实现算法的独立,并且可以相互转换,让行为的变化独立于拥有行为的客户,如我们在策略模式中的例子:诸葛亮给赵云的锦囊妙计,其中每个计策就是一个算法,对妙计进行封装,在不同的时间调用执行不同的计策;而命令模式是对动作解耦,把一个动作的执行分为执行对象(Receiver)和执行行为(Command),让两者相互独立而不相互影响,我们在命令模式中举出了软件开发的例子,软件开发中包括美工组、界面组、编码组,这就是各个执行对象,还包括一个产品经理,就是那个执行行为,客户通过只跟Command打交道实现各个执行对象的行为。
接下来我们从对文件压缩的业务需求出发,分别按照命令模式和策略模式设计出一套实现,来看看他们侧重点的不同。文件的压缩其中有两种格式zip和gzip,他们是两种不同的压缩格式,我们将分别实现不同格式的压缩和解压缩功能。
- 命令模式实现对文件的压缩和解压缩
命令模式的主旨是封装命令,使请求者和实现者解耦。我们先看看命令模式实现文件压缩和解压缩的类图:
首先介绍一下命令的抽象执行类(IReceiver),该类包括两个方法,对文件的压缩方法和解压缩方法。本类图中是按照压缩格式来设计接口,还可以依照职责设计接口,那么有两个类(CompressReceiver和UncompressReceiver)实现该接口,接口中有两个方法(zipExecute和gZipExecute方法),该类图请读者自己思考,在此没有给出相关设计。如上类图中IReceiver的源码如下:
public interface IReceiver {
//包含两个方法,压缩和解压缩
public boolean compress(String source, String to);
public boolean uncompress(String source, String to);
}
那么当然两种压缩格式的具体执行类中包括两个方法的具体实现,ZipReceiver和GzipReceiver源码如下:
public class ZipReceiver implements IReceiver{ @Override
public boolean compress(String source, String to) {
// TODO Auto-generated method stub
System.out.println("this is the Zip compress method");
/**
* 对文件的具体压缩代码
*/
return true;
} @Override
public boolean uncompress(String source, String to) {
// TODO Auto-generated method stub
System.out.println("this is the zip uncompress method");
/**
* 对文件的具体解压缩代码
*/
return true;
} } public class GzipReceiver implements IReceiver{ @Override
public boolean compress(String source, String to) {
// TODO Auto-generated method stub
System.out.println("this is the Gzip compress method");
return true;
} @Override
public boolean uncompress(String source, String to) {
// TODO Auto-generated method stub
System.out.println("this is the Gzip uncompress method");
return true;
} }
命令抽象类中包含两个私有对象,分别为压缩和解压缩类对象,还有一个抽象方法(execute方法)。源码如下:
public abstract class AbstractCmd {
protected ZipReceiver zip = new ZipReceiver();
protected GzipReceiver gzip = new GzipReceiver();
public abstract boolean execute(String source, String to);
}
抽象命令类的抽象方法在子类中实现,具体源码如下:
public class ZipCompressCmd extends AbstractCmd{ @Override
public boolean execute(String source, String to) {
// TODO Auto-generated method stub
return super.zip.compress(source, to);
}
} public class ZipUncompressCmd extends AbstractCmd{ @Override
public boolean execute(String source, String to) {
// TODO Auto-generated method stub
return super.zip.uncompress(source, to);
} } public class GzipCompress extends AbstractCmd{ @Override
public boolean execute(String source, String to) {
// TODO Auto-generated method stub
return super.gzip.compress(source, to);
} } public class GzipUncompress extends AbstractCmd{ @Override
public boolean execute(String source, String to) {
// TODO Auto-generated method stub
return super.gzip.uncompress(source, to);
} }
最后就剩下Invoker类和Client类了,Invoker类中包含一个私有的命令对象和一个执行方法,通用源码如下:
public class Invoker {
//执行的命令
private AbstractCmd command;
public Invoker(AbstractCmd command){
this.command = command;
}
public void execute(String source, String to){
this.command.execute(source, to);
}
} public class Client { /**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
AbstractCmd command = new ZipCompressCmd();
Invoker invoker = new Invoker(command);
invoker.execute("c:/windows","D;/zip");
}
}
命令模式就这么简单的实现了,但是回过头来想想,策略模式又是什么呢?好像就是ZipReceiver和GzipReceiver就是两种策略,而AbstractCmd就是策略的封装。但是如果命令模式中接口按照职责进行设计,那么CompressReceiver和UncompressReceiver就会成为两种策略,如果将这两种包含zip和gzip的压缩和解压缩算法进行封装,当想添加新的rar压缩和解压缩算法则比较麻烦。接下来,我们还是看看策略模式实现文件两种格式的压缩和解压缩吧!
- 策略模式实现对文件的压缩和解压缩
策略模式实现文件的压缩和解压缩也很简单,本例中实现文件的压缩和解压缩有其中两种格式,一种是zip,另一种是gzip,可以把他们看作两种压缩算法,对文件的压缩由高层模块选择采用哪种压缩算法对文件进行压缩,我们首先看一下策略模式的类图:
这个类图的源码如下:
public interface Algorithm{
//包含两个方法,压缩和解压缩
public boolean compress(String source, String to);
public boolean uncompress(String source, String to);
} public class ZipAlgorithm implements Algorithm{ @Override
public boolean compress(String source, String to) {
// TODO Auto-generated method stub
System.out.println("this is the Zip compress method");
/**
* 对文件的具体压缩代码
*/
return true;
} @Override
public boolean uncompress(String source, String to) {
// TODO Auto-generated method stub
System.out.println("this is the zip uncompress method");
/**
* 对文件的具体解压缩代码
*/
return true;
} } public class GzipAlgorithm implements Algorithm{ @Override
public boolean compress(String source, String to) {
// TODO Auto-generated method stub
System.out.println("this is the Zip compress method");
/**
* 对文件的具体压缩代码
*/
return true;
} @Override
public boolean uncompress(String source, String to) {
// TODO Auto-generated method stub
System.out.println("this is the zip uncompress method");
/**
* 对文件的具体解压缩代码
*/
return true;
} } public class Context{
private Algorithm al;
public Context(Algorithm al){
this.al = al;
}
public boolean compress(String source, String to){
this.al.compress(source,to);
}
public boolean uncompress(String source, String to){
this.al.uncompress(source,to);
}
} public class Client{
public static void main(String[] args){
Context text = new Context(new ZipAlgorithm());
text.compress("c:/windows","d:/file");
}
}
- 总结
命令模式和策略模式的相似点和区别:
关注点不同:命令模式关注的是解耦问题,如何让请求者和执行者解耦是他需要首先解决的问题,解耦的要求就是将请求的命令封装为一个个命令,由接收者执行。由于封装成了命令,就同时可以对命令进行多种处理,例如撤销、记录等。策略模式关注的是算法替换为题,一种新的算法投产,旧算法退休,或者提供多种算法由调用者自己选择使用,算法的*更替是它实现的要点。换句话说,策略模式关注的是算法的完整性、封装性,只有具备了这两个条件才能保证其*切换。
角色功能不同:命令模式关注功能实现,命令模式中接收者可以按照不同的原则进行设计(压缩文件格式或者职责),接口设计的改变只影响命令簇的变更,对请求者没有任何影响,接收者对命令负责,而与请求者无关。命令模式中的接收者只要符合六大设计原则,完全不同关系是否完成了一个具体逻辑,它的影响范围也仅仅是抽象命令和具体命令,修改不会扩散到模式外的模块。而策略模式中的具体算法是一个完整的逻辑,是不可拆分的原子业务单元,一旦变更就是对算法整体的变更。
使用场景不同:命令模式适用于解耦两个有紧耦合关系的对象场合或者多命令撤销的场景,而策略模式适用于算法要求变换的场景。
状态模式VS策略模式 |
我们首先看一下策略模式和状态模式的类图吧:
策略模式封装不同的算法,算法之间没有交互,以到达算法之间可以*切换的目的,而状态模式封装不同的状态,以到达状态切换随之行为发生改变的目的,就如同我们在状态模式中介绍的电梯门开关和运行的状态改变。接下来我们通过状态模式实现人生不同阶段的变化。首先是其通用类图如下:
其中Human中包含了人类的三种状态童年、中年和老年,分别在每一次执行work()方法后切换到下一个人生状态。源码如下:
public class Human {
//人生有三种状态童年、成年和老年
public static HumanState childState = new ChildrenState();
public static HumanState adultState = new AdultState();
public static HumanState oldState = new OldState();
public HumanState state = null;
public void setHumanState(HumanState state){
this.state = state;
this.state.setHuman(this);
}
public void work(){
this.state.work();
}
} public abstract class HumanState {
protected static Human human = null;
public void setHuman(Human human){
this.human = human;
}
public abstract void work();
} public class ChildrenState extends HumanState{ @Override
public void work() {
// TODO Auto-generated method stub
System.out.println("童年是最无忧无虑的,在家的港湾的庇护下快乐的成长");
//切换到下一个人生状态
super.human.setHumanState(Human.adultState);
} } public class AdultState extends HumanState{ @Override
public void work() {
// TODO Auto-generated method stub
System.out.println("成为了社会的中坚力量,有了自己的家庭和事业");
super.human.setHumanState(Human.oldState);
} } public class OldState extends HumanState{ @Override
public void work() {
// TODO Auto-generated method stub
System.out.println("一路上收藏点点滴滴的微笑,坐着摇椅和心爱的人慢慢聊");
} } public class Client { /**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Human human = new Human();
human.setHumanState(new ChildrenState());
human.work(); //有状态切换在ChildrenState的work()
human.work();
human.work();
} }
- 总结
从上人生的例子中可以看出,状态模式侧重的是人的生长规律,而策略模式侧重的是人的工作逻辑,不同的人生阶段工作不同。
环境角色的职责不同:两者都有一个叫Context环境角色的类,但是两者的区别很大,策略模式的环境角色只是一个委托作用,负责算法的替换;而状态模式的环境角色不仅仅是委托行为,它还具有登记状态变化的功能,与具体的状态类协作,共同完成状态切换行为随之切换的任务。
解决问题的重点不同:策略模式旨在解决内部算法如何改变的问题,也就是将内部算法的改变对外界的影响降低到最小,保证算法之间的*切换;而状态模式旨在改变内部状态的改变而引起的行为的改变的问题,出发点是事务的状态,状态封装而暴露行为,一个对象状态的改变,从外界来看就好像是行为改变。
解决问题的方法不同:策略模式解决算法*切换,但是什么时候用什么算法它决定不了,而装填模式对外暴露行为,状态的变换一般是由环境角色和具体状态共同完成的,也就是说装填模式封装了状态变化而暴露了不同的行为或行为结果。
应用场景不同:策略模式是算法的封装,可以是一个有意义的对象,也可以是一个无意义的逻辑片段,如MD5加密算法,算法必须是平行的;状态模式则要求有一系列状态发生变化的场景,他要求的是有状态且有行为的场景,也就是一个对象必须有二维(状态和行为)描述才能采用状态模式。
复杂度不同:策略模式结构比较简单,容易扩展,而状态模式通常比较复杂,因为他要从两个角色看到一个对象状态和行为的改变,也就是说封装的是变化。
观察者模式VS责任链模式 |
观察者模式中也存在触发链啊,想想当每一个被观察者有动作时,就会触发被观察者的相应动作,这也叫做观察者链,这与责任链非常相似,都实现了事务的链条化处理,观察者模式中允许传递的事件发生改变,而责任链模式中一般事件的结构传递不发生变化。如你在上课睡觉,打鼾声太大,盖过了老师讲课的声音,所以老师发火了,老师已经处理不了了,然后将你上课打鼾这件事报告给了年级主任,然而年级主任也处理不了,报告给了你的老爸,然后老爸知道你上课睡觉这件事,你的麻烦就大了,在这一系列的事件的处理中,打鼾事件时不变的;当然了老师针对你打鼾这件事没有报告给年级主任,而是老师在收到你打鼾这件事后,老师掏出了扩音器,盖过了你打鼾的声音,其他同学的耳朵就得遭殃了,老师是观察者,观察你是否睡觉打鼾,而一旦收到你打鼾,就用扩音器,同时老师也是一个被观察者,其他同学听老师讲课,老师一旦用扩音器,其他同学耳朵遭殃,在这一系列事件中,首先的事件时你上课打鼾,然后是老师用扩音器讲课。这就是观察者模式中的触发链模式。
- 小结
通过如上例子,我们发现触发链和责任链虽然都是链结构,但是还是有区别的。
链中的消息对象不同:责任链模式中消息在链中传递时,消息的结构基本不会改变,而观察者模式中消息可以*改变,只要上下级节点对传递对象了解即可,观察者模式只要求联众相邻两个结点的消息对象固定。
上下节点的关系不同:在责任链模式中,上下节点没有关系,都是接收同样的对象,所有传递的对象都是从链首传递过来;而观察者模式的上下节点关系密切,链中任意两个相邻接点都是一个牢固的独立团体。
消息的分销渠道不同:在责任链模式中,消息从链首向链尾沿着单一固定的方向传递;而观察者模式则不同,消息的传递灵活性较大,一个消息具体怎传递时不固定的,可以以广播方式传递,也可以以跳跃方式传递,这取决于处理消息的逻辑。