薛磊 Job Seeker

23种设计模式——设计模式大PK(三)


行为类模式大PK

  行为类模式包括责任链模式、命令模式、解释器模式、迭代器模式、中介者模式、备忘录模式、观察者模式、状态模式、策略模式、模板方法模式、访问者模式。

1.命令模式 VS 策略模式

  策略模式的意图是封装算法,它认为“算法”已经是一个完整的、不可拆分的原子业务,即其意图是让这些算法独立,并且可以相互替换,让行为的变化独立于拥有行为的客户。

  命令模式则是对动作的解耦,把一个动作的执行分为执行独享(接受者角色)、执行行为(命令角色),让两者互相独立而不相互影响。

关注点不同

  策略模式关注的是算法替换的问题,一个新的算法投产,旧算法退休,或者提供多种算法由调用者自己选择使用,算法的自由更替是它实现的要点,关注的是算法的完整性。封装性。

  命令模式关注的是解耦问题,如何让请求者和执行者解耦是它需要首先解决的,解耦的要求就是把请求的内容封装为一个一个的命令,由接受者执行。由于封装成了命令,就同时可以对命令进行多种处理,例如撤销、记录等。

角色功能不同

  策略模式中的具体算法是负责一个完整算法逻辑,它是不可再拆分的院子业务单元,一旦变更就是对算法整体的变更。

  命令模式关注命令的实现,也就是功能的实现,接收者的影响范围仅仅是抽象命令和具体命令,对它的修改不会扩散到模式外的模块。

使用场景不同

  策略模式适用于算法要求变幻的场景,而命令模式适用于解耦两个有紧耦合关系的对象场合或者多命令多撤销的场景。

策略模式示例代码:

//抽象压缩算法
public interface Algorithm {
	//压缩算法
	public boolean compress(String source,String to);
	//解压缩算法
	public boolean uncompress(String source,String to);
}

//zip压缩算法
public class Zip implements Algorithm {
	//zip格式的压缩算法
	public boolean compress(String source,String to) {
		System.out.println(source+"-->"+to+" ZIP压缩成功!");
		return true;
	}
	//zip格式的解压缩算法
	public boolean uncompress(String source,String to) {
		System.out.println(source+"-->"+to+" ZIP解压缩成功!");
		return true;
	}
}

//gzip压缩算法
public class Gzip implements Algorithm {
	//gzip格式的压缩算法
	public boolean compress(String source,String to) {
		System.out.println(source+"-->"+to+" GZIP压缩成功!");
		return true;
	}
	//gzip格式的解压缩算法
	public boolean uncompress(String source,String to) {
		System.out.println(source+"-->"+to+" GZIP解压缩成功!");
		return true;
	}
}
//环境角色
public class Context {
	//指向抽象算法
	private Algorithm al;
	//构造函数传递具体的算法
	public Context(Algorithm al){	this.al = al;}
	//执行压缩算法
	public boolean compress(String source,String to) {
		return al.compress(source,to);
	}
	//执行解压缩算法
	public boolean uncompress(String source,String to) {
		return al.uncompress(source,to);
	}
}

//场景类
public class Client{
	public static vod main(String[] args) {
		//定义环境角色
		Context context;
		//对文件执行zip压缩算法
		context = new Context(new Zip());
		context.compress("c:\\windows","d:\\window.zip");
		context.uncompress("c:\\windows.zip","d:\\windows");
	}
}

命令模式示例代码:

//抽象压缩命令
public abstract class AbstractCmd {
	//对接受者的引用
	protected IReceiver zip = new ZipReceiver();
	protected IReceiver gzip = new GzipReceiver();
	//抽象方法,命令的具体单元
	public abstract boolean execute(String source,String to);
}

//zip压缩命令
public class ZipCompressCmd extends AbstractCmd {
	public boolean execute(String source,String to) {
		return super.zip.compress(source,to);
	}
}
//zip解压缩命令
public class ZipUnCompressCmd extends AbstractCmd {
	public boolean execute(String source,String to) {
		return super.zip.uncompress(source,to);
	}
}

//gzip压缩命令
public class GzipCompressCmd extends AbstractCmd {
	public boolean execute(String source,String to) {
		return super.gzip.compress(source,to);
	}
}
//gzip解压缩命令
public class GzipUnCompressCmd extends AbstractCmd {
	public boolean execute(String source,String to) {
		return super.gzip.uncompress(source,to);
	}
}
//抽象接收者
public interface IReceiver {
	//压缩
	public boolean compress(String source,String to);
	//解压缩
	public boolean uncompress(String source,String to);
}
//zip接收者
public class ZipReceiver implements IReceiver {
	public boolean compress(String source,String to){
		System.out.println(source+"-->"+to+" ZIP压缩成功!");
		return true;
	}
	public boolean uncompress(String source,String to){
		System.out.println(source+"-->"+to+" ZIP解压缩成功!");
		return true;
	}
}
//gzip接收者
public class GzipReceiver implements IReceiver {
	public boolean compress(String source,String to){
		System.out.println(source+"-->"+to+" GZIP压缩成功!");
		return true;
	}
	public boolean uncompress(String source,String to){
		System.out.println(source+"-->"+to+" GZIP解压缩成功!");
		return true;
	}
}
//调用者
public class Invoker {
	//抽象命令的引用
	private AbstractCmd cmd;
	public Invoker(AbstractCmd cmd){
		this.cmd = cmd;
	}
	//执行命令
	public boolean execute(String source,String to){
		return cmd.execute(source,to);
	}
}

//场景类
public class Client {
	public static void main(String[] args) {
		//定义一个命令
		Abstract cmd = new ZipCompressCmd();
		//定义调用者
		Invoker invoker = new Invoker(cmd);
		invoker.execute("c:\\windows","d:\\windows.zip");
	}
}

2.策略模式 VS 状态模式

  在行为类设计模式中,状态模式和策略模式是亲兄弟,它们的类图非常相似,都是通过Context类封装一个具体的行为,都提供了一个封装的方法,是高扩展性的设计模式。区别:

  策略模式封装的是不同的算法,算法之间没有交互,以达到算法可以切换的目的。

  状态模式封装的是不同的状态,以达到状态切换行为随之发生改变的目的。

环境角色的职责不同

  策略模式的环境角色只是一个委托作用,负责算法的替换。

  状态模式的环境角色不仅仅是委托行为,它还具有登记状态变化的功能,与具体的状态类协作,共同完成随之切换的任务。

解决问题的重点不同

  策略模式旨在解决内部算法如何改变的问题,也就是将内部算法的改变对外界的影响降低到最小,它保证的是算法可以自由的切换。

  状态模式旨在解决内在状态的改变而引起行为改变的问题,它的出发点是事物的状态,封装状态而暴露行为。

解决问题的方法不同

  策略模式只是确保算法可以自由切换,但是什么时候用什么算法它决定不了。

  状态模式对外暴露的是行为,状态的变化一般是由环境角色和具体状态共同完成的,也就是说状态模式封装了状态的变化而暴露了不同的行为和行为结果。

应用场景不同

  策略模式是一系列平行的、可相互替换的算法封装后的结果

  状态模式要求有一系列状态发生变化的场景,它要求的是有状态且有行为的场景。

复杂度不同

  策略模式结构比较简单,扩展比较容易,代码也容易阅读。

  状态模式比较复杂,因为它要从连个角色看到一个对象状态和行为的改变。

状态算法实现人生

//人的抽象状态
public abstract class HumanState {
	//指向一个具体的人
	protected Human human;
	//设置一个具体的人
	public void setHuman(Human human){
		this.human = human;
	}
	//不管人是什么状态都要工作
	public abstract void work();
}

//孩童状态
public class ChildState extends HumanState{
	public void work(){
		System.out.println("玩耍");
		super.human.setState(Human.ADULT_STATE);
	}
}
//成年人状态
public class AdultState extends HumanState{
	public void work(){
		System.out.println("养活全家");
		super.human.setState(Human.OLD_STATE);
	}
}
//孩童状态
public class OldState extends HumanState{
	public void work(){
		System.out.println("享乐");
	}
}
//环境角色
public class Human{
	//定义人类都有哪些状态
	public static final HumanState CHILD_STATE = new ChildState();
	public static final HumanState ADULT_STATE = new ADULTState();
	public static final HumanState OLD_STATE = new OLDState();
	//定义一个人的状态
	private HumanState state;
	//设置一个状态
	pubilc void setState(HumanState state){
		this.state = state;
		this.state.setHuman(this);
	}
	//人类的工作
	public void work(){
		this.state.work();
	}
}

//场景类
public class Client{
	public static void mian(String[] args{
		//定义一个普通的人
		Human human = new Human);
		//设置一个人的初始状态
		human.setState(new ChiledState());
		//儿童状态,之后转到成人状态,在转到老人状态
		human.work();
		human.work();
		human.work();
	}
}

3.观察者模式 VS 责任链模式

链中的消息对象不同

  责任链模式基本上不改变消息对象的结构,比如从首节点传进来一个String对象或者Person对象,不会到链尾的时候变成int对象或者Human对象。

  触发链模式中传递的对象可以自由变化,只要上下级节点对传递对象了解即可,不要求链中的消息对象不变化,它只要求链中相邻两个节点的消息对象固定。

上下节点的关系不同

  在责任链模式中,上下节点没有关系,都是接收同样的对象,所有传递的对象都是从链首传递过来,上一节点是什么没有关系,只要按照自己的逻辑处理就行。

  在触发链模式中,它的上下级关系很亲密,下级对上机顶礼膜拜,上级对下级绝对信任,链中的任意两个相邻节点都是一个固定的独立团体。

消息的分销渠道不同

  在责任链模式中,一个消息从链首传递进来后,就开始沿着链条向链尾运动,方向是单一的、固定的。

  触发链模式,因为采用的是观察者模式,所以有很大的灵活性,一个消息传递到链首后,具体怎么传递是不固定的,可以以广播方式传递,也可以以跳跃方式传递,这取决于处理消息的逻辑。

  DNS协议,将域名翻译成IP地址,每个区域的DNS服务器只保留自己区域的域名解析,对于不能解析的域名,则提交上级域名解析器解析,最终由一台位于美国洛杉矶的顶级域名服务器进行解析,返回结果。

  请求者 –> 区域DNS –> 中国顶级DNS –> 全球顶级DNS

  责任链模式,谁解析,便由谁返回给请求者结果。

  触发链模式,谁解析,便返回给上一级,最终由第一个DNS返回给请求者结果。

  所有的解析结果都是由上海服务器返回的,这才是真正的DNS解析过程。

//解析记录
public class Recorder {
	//域名
	private String domain;
	//IP地址
	pirvate String ip;
	//属主
	private String owner;
	
	//javabean规范...
}

责任链模式实现DNS解析过程

//抽象域名服务器
public abstract class DnsServer{
	//上级DNS是谁
	private DnsServer upperServer;
	//解析域名
	public final Recorder resolve(String domain){
		Recoder recoder = null;
		if(isLocal(domain)){//是本服务器能解析的域名
			recorder = echo(domain);
		}else{//本服务器不能解析
			//提交上级DNS进行解析
			recoder = upperServer.resolve(domain);
		}
		return recorder;
	}
	//指向上级DNS
	public void setUpperServer(DnsServer upperServer){
		this.upperServer = upperServer;
	}
	//每个DNS都有一个数据处理区(ZONE),检查域名是否在本区中
	protected abstract boolean isLocal(String domain);
	//每个DNS服务器都必须实现解析任务
	portected Recorder echo(String domain){
		Recorder recorder = new Recorder();
		//获得IP地址
		recorder.setIp(getIpAddress());
		recorder.setDomain(domain);
		return recorder;
	}
	//随机产生一个IP地址,工具类
	private String getIpAddress(){
		Random rand = new Random();
		String address = rand.nextInt(255)+"."+rand.nextInt(255)+"."+rand.nextnt(255)+"."+rand.nextnt(255);
		return address;
	}
}

//上海DNS服务器
public class SHDnsServer extends DnsServer{
	public Recorder echo(String domain){
		Recorder recorder = super.echo(domain);
		recorder.setOwner("上海DNS服务器");
		return recorder;
	}
	//定义上海的DNS服务器能处理的级别
	protected boolean isLocal(String domain){
		return domain.endsWith(".sh.cn");
	}
}

//中国顶级DNS服务器
public class ChinaTopDnsServer extends DnsServer{
	public Recorder echo(String domain){
		Recorder recorder = super.echo(domain);
		recorder.setOwner("中国顶级DNS服务器");
		return recorder;
	}
	protected boolean isLocal(String domain){
		return domain.endsWith(".cn");
	}
}

//全球顶级DNS服务器
public class TopDnsServer extends DnsServer{
	public Recorder echo(String domain){
		Recorder recorder = super.echo(domain);
		recorder.setOwner("全球顶级DNS服务器");
		return recorder;
	}
	protected boolean isLocal(String domain){
		//所有的域名最终的解析地点
		return true;
	}
}

//场景类
public class Client{
	public static void main(String[] args){
		//上海域名服务器
		DnsServer sh = new SHDnsServer();
		//中国顶级域名服务器
		DnsServer china = new ChinaTopDnsServer();
		//全球顶级域名服务器
		DnsServer top = new TopDnsServer();
		//定义查询路径
		china.setUpperServer(top);
		sh.setUpperServer(china);
		//解析域名
		while(true){
			System.out.println("域名解析模拟器(输入N退出):");
			String domain = (new BufferedReader(new InputStreamReader(System.in))).readLine();
			if(domain.equalsIgnoreCase("n")){
				return ;
			}
			Recorder recorder = sh.resolve(domain);
			System.out.println(recorder);
		}
	}
}

触发链模式实现DNS解析过程

//抽象域名服务器
public abstract class DnsServer extends Observable implements Observer{
	//处理请求,也是接受到事件后的处理
	public void update(Observable arg0,Object arg1){
		Recorder recorder = (Recorder)arg1;
		//如果本机能解析
		if(isLocal(recorder)){
			recorder.setIp(getIpAddress());
		}else{//本机不能解析,则提交到上级DNS
			responseFromUpperServer(recorder);
		}
		//签名
		sign(recorder);
	}
	//作为被观察者,允许增加观察者,这里上级DNS一般只有一个
	public void setUpperServer(DnsServer dnsServer){
		//先清空,然后在增加
		super.deleteObservers();
		super.addObserver(dnsServer);
	}
	//向父DNS请求解析,也就是通知观察者
	private void responseFromUpperServer(Recorder){
		super.setChanged();
		super.notifyObservers(recorder);
	}
	//每个DNS服务器签上自己的名字
	protected abstract void sign(Recorder recorder);
	//每个DNS服务器都必须定义自己的处理级别
	protected abstract boolean isLocal(Recorder recorder);
	//随机产生一个IP地址,工具类
	private String getIpAddress(){
		Random rand = new Random();
		String address = rand.nextInt(255)+"."+rand.nextInt(255)+"."+rand.nextnt(255)+"."+rand.nextnt(255);
		return address;
	}
}

//上海DNS服务器
public class SHDnsServer extends DnsServer{
	public void sign(Recorder recorder){
		recorder.setOwner("上海DNS服务器");
	}
	//定义上海的DNS服务器能处理的级别
	protected boolean isLocal(Recorder recorder){
		return recorder.getDomain().endsWith(".sh.cn");
	}
}
//中国顶级DNS服务器
public class ChinaTopDnsServer extends DnsServer{
	public void sign(Recorder recorder){
		recorder.setOwner("中国顶级DNS服务器");
	}
	//定义上海的DNS服务器能处理的级别
	protected boolean isLocal(Recorder recorder){
		return recorder.getDomain().endsWith(".cn");
	}
}
//全球顶级DNS服务器
public class TopDnsServer extends DnsServer{
	public void sign(Recorder recorder){
		recorder.setOwner("全球顶级DNS服务器");
	}
	//定义上海的DNS服务器能处理的级别
	protected boolean isLocal(Recorder recorder){
		//所有域名最终的解析地点
		return true;
	}
}

//场景类
public class Client{
	public static void main(String[] args){
		//上海域名服务器
		DnsServer sh = new SHDnsServer();
		//中国顶级域名服务器
		DnsServer china = new ChinaTopDnsServer();
		//全球顶级域名服务器
		DnsServer top = new TopDnsServer();
		//定义查询路径
		china.setUpperServer(top);
		sh.setUpperServer(china);
		//解析域名
		while(true){
			System.out.println("域名解析模拟器(输入N退出):");
			String domain = (new BufferedReader(new InputStreamReader(System.in))).readLine();
			if(domain.equalsIgnoreCase("n")){
				return ;
			}
			Recorder recorder = new Recorder();//虚拟了观察者触发动作
			recorder.setDomain(domain);
			sh.update(null,recorder);
			System.out.println(recorder);
		}
	}
}

Comments

Content