设计模式之禅之行为类PK【观察者模式VS责任链模式】
2018-01-12 16:29
387 查看
设计模式之禅PK之行为类
行为类设计模式
行为类模式:责任链模式
命令模式
迭代器模式
中介者模式
备忘录模式
观察者模式
状态模式
策略模式
模板方法模式
访问者模式
【观察者模式】VS【责任链模式】
为什么把观察者模式和责任链模式放在一起对比呢?看起来这两个模式没有太多的相似性,真的没有吗?我们在观察者中提到了触发链(也叫做观察者链),一个具体的角色既可以是观察者,也可以是被观察者,这就形成了一个观察者链,这与责任链模式非常类似,他们都实现了事务的链条化处理。
责任链的举例:
当你在学校里犯错的时候,老师会给校长汇报,然后校长给你的父亲打电话,然后你的魔鬼的日子来了
那什么事触发链呢?--观察者/被观察者
当你在学校犯错的时候,老师没有汇报给校长,而是自己把它给解决了---这时候老师既是观察者,也是被观察者,事件从你犯错到老师处理,这也是一个链条结构,但是链结构中传递的事件改变了。
以一个具体的例子来说明两者的区别:
DNS【域名---IP地址】
数据量很大的DNS服务器它是怎么设计的呢?
规定了每个区域的DNS服务器(Local DNS)只保留了自己区域的域名解析。对于不能解析的域名,则提交上级域名解析器解析,最终是由一台位于美国洛杉矶的顶级域名服务器进行解析,返回结果。
示意图比较
观察者模式 | 责任链模式 |
---|---|
![]() | ![]() |
类图比较
观察者模式 | 责任链模式 |
---|---|
![]() | ![]() |
代码来实现
观察者模式DnsServer
package com.peng.pk_gc2; import java.util.Observable; import java.util.Observer; import java.util.Random; /** * @author kungfu~peng * @data 2017年12月15日 * @description */ public abstract class DnsServer extends Observable implements Observer { // 处理请求,也就是接收到事件后的处理 public void update(Observable ard0, Object arg1) { Recorder recorder = (Recorder) arg1; // 如果本机能解析 if (isLocal(recorder)) { recorder.setIp(genIpAddress()); } else { // 本机不能解析,则提交到上级DNS responUpperFromServer(recorder); } // 签名 sign(recorder); } // 作为被观察者,允许添加观察者,这里的上级一般只有一个 public void setUpperServer(DnsServer ds) { // 先清空,然后再增加 super.deleteObservers(); super.addObserver(ds); } // 向父Dns请求解析,也就是通知观察者 private void responUpperFromServer(Recorder recorder) { super.setChanged(); super.notifyObservers(recorder); } // 每个DNS服务器签上自己的名字 protected abstract void sign(Recorder recorder); // 每个DNS服务器都必须定义自己的处理级别 protected abstract boolean isLocal(Recorder recorder); // 随机产生一个IP地址,工具类 private String genIpAddress() { Random rmd = new Random(); String address = rmd.nextInt(255) + "." + rmd.nextInt(255) + "." + rmd.nextInt(255) + "." + rmd.nextInt(255); return address; } }
SHDnsServer
package com.peng.pk_gc2; /** * @author kungfu~peng * @data 2017年12月15日 * @description */ public class SHDnsServer extends DnsServer { @Override protected void sign(Recorder recorder) { recorder.setOwner("山西服务器"); } // 定义山西的处理级别 @Override protected boolean isLocal(Recorder recorder) { return recorder.getDomain().endsWith("sh.cn"); } }
ChinaTopNdnServer
package com.peng.pk_gc2; /** * @author kungfu~peng * @data 2017年12月15日 * @description */ public class ChinaTopDnsServer extends DnsServer { @Override protected void sign(Recorder recorder) { recorder.setOwner("中国顶级NDS服务器"); } @Override protected boolean isLocal(Recorder recorder) { return recorder.getDomain().endsWith(".ch"); } }
TopDnsServer
package com.peng.pk_gc2; /** * @author kungfu~peng * @data 2017年12月15日 * @description */ public class TopDnsServer extends DnsServer { @Override protected void sign(Recorder recorder) { recorder.setOwner("顶级NDS服务器"); } @Override protected boolean isLocal(Recorder recorder) { return true; } }
Recorder
package com.peng.pk_gc2; /** * @author kungfu~peng * @data 2017年12月15日 * @description */ public class Recorder { // 域名 private String domain; // IP地址 private String ip; // 雇主 private String owner; public String getDomain() { return domain; } public void setDomain(String domain) { this.domain = domain; } public String getIp() { return ip; } public void setIp(String ip) { this.ip = ip; } public String getOwner() { return owner; } public void setOwner(String owner) { this.owner = owner; } // 输出记录信息 @Override public String toString() { String str = "域名:" + this.domain + "\nIp地址:" + this.ip + "\n解析者:" + this.owner; return str; } }
Client
package com.peng.pk_gc2; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; /** * @author kungfu~peng * @data 2017年12月15日 * @description */ public class Client { /** * 功能: 返回值类型: 参数列表: * * @param args * @throws IOException */ public static void main(String[] args) throws IOException { // 山西域名服务器 DnsServer sh = new SHDnsServer(); // 中国顶级服务器 DnsServer china = new ChinaTopDnsServer(); // 顶级服务器 DnsServer top = new TopDnsServer(); // 定义查询路径 china.setUpperServer(top); sh.setUpperServer(china); // 解析域名 System.out.println("====解析域名===="); 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("=====DNS服务器解析结果====="); System.out.println(recorder); } } }
执行结果
====解析域名==== 请输入域名: sh.ch =====DNS服务器解析结果===== 域名:sh.ch Ip地址:172.16.10.53 解析者:山西服务器
注:
下级节点对上级节点的顶礼膜拜
上级节点对下级节点的绝对信任
责任链模式
DnsServer
package com.peng.pk_zr; import java.util.Random; /** * @author kungfu~peng * @data 2017年12月15日 * @description */ public abstract class DnsServer { // 上级DNS是谁 private DnsServer upperServer; // 解析域名 public final Recorder resolve(String domain) { Recorder Recorder = null; if (isLocal(domain)) { // 本服务器解析 Recorder = echo(domain); } else { // 本服务器不能解析 Recorder = upperServer.resolve(domain); } return Recorder; } // 指向上级DNS public void setUpperServer(DnsServer upperServer) { this.upperServer = upperServer; } // 每个DNS都有一个数据处理区 protected abstract boolean isLocal(String domain); // 每个DNS服务器都必须实现的解析任务 protected Recorder echo(String domain) { Recorder recorder = new Recorder(); // 获得IP地址 recorder.setIp(genIpAddress()); recorder.setDomain(domain); return recorder; } // 随机产生一个IP地址,工具类 private String genIpAddress() { Random rmd = new Random(); String address = rmd.nextInt(255) + "." + rmd.nextInt(255) + "." + rmd.nextInt(255) + "." + rmd.nextInt(255); return address; } }
SHDnsServer
package com.peng.pk_zr; /** * @author kungfu~peng * @data 2017年12月15日 * @description */ public class SHDnsServer extends DnsServer { @Override protected Recorder echo(String domain) { Recorder recorder = super.echo(domain); recorder.setOwner("山西NDS服务器"); return recorder; } // 定义山西的处理范围 @Override protected boolean isLocal(String domain) { return domain.endsWith("sh.ch"); } }
ChinaTopDnsServer
package com.peng.pk_zr; /** * @author kungfu~peng * @data 2017年12月15日 * @description */ public class ChinaTopDnsServer extends DnsServer { @Override protected Recorder echo(String domain) { Recorder recorder = super.echo(domain); recorder.setOwner("中国顶级NDS服务器"); return recorder; } // 定义山西的处理范围 @Override protected boolean isLocal(String domain) { return domain.endsWith(".ch"); } }
TopDnsServer
package com.peng.pk_zr; /** * @author kungfu~peng * @data 2017年12月15日 * @description */ public class TopDnsServer extends DnsServer { @Override protected Recorder echo(String domain) { Recorder recorder = super.echo(domain); recorder.setOwner("全球顶级服务器"); return recorder; } // 定义山西的处理范围 @Override protected boolean isLocal(String domain) { return true; } }
Recorder
package com.peng.pk_zr; /** * @author kungfu~peng * @data 2017年12月15日 * @description */ public class Recorder { // 域名 private String domain; // IP地址 private String ip; // 雇主 private String owner; public String getDomain() { return domain; } public void setDomain(String domain) { this.domain = domain; } public String getIp() { return ip; } public void setIp(String ip) { this.ip = ip; } public String getOwner() { return owner; } public void setOwner(String owner) { this.owner = owner; } // 输出记录信息 @Override public String toString() { String str = "域名:" + this.domain + "\nIp地址:" + this.ip + "\n解析者:" + this.owner; return str; } }
Client
package com.peng.pk_zr; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; /** * @author kungfu~peng * @data 2017年12月15日 * @description */ public class Client { /** * 功能: 返回值类型: 参数列表: * * @param args * @throws IOException */ public static void main(String[] args) throws IOException { // 山西域名服务器 DnsServer sh = new SHDnsServer(); // 中国顶级服务器 DnsServer china = new ChinaTopDnsServer(); // 顶级服务器 DnsServer top = new TopDnsServer(); // 定义查询路径 china.setUpperServer(top); sh.setUpperServer(china); // 解析域名 System.out.println("====解析域名===="); while (true) { System.out.println("\n请输入域名:"); String domain = (new BufferedReader( new InputStreamReader(System.in))).readLine(); if (domain.equalsIgnoreCase("n")) { return; } Recorder recoder = sh.resolve(domain); System.out.println("=====DNS服务器解析结果====="); System.out.println(recoder); } } }
执行结果
请输入域名: www.sh =====DNS服务器解析结果===== 域名:www.sh Ip地址:111.244.65.69 解析者:全球顶级服务器 请输入域名: sh.ch =====DNS服务器解析结果===== 域名:sh.ch Ip地址:198.80.218.93 解析者:山西NDS服务器
最佳实践
比较项 | 责任链模式 | 观察者模式【触发链】 |
---|---|---|
链中消息的对象 | 基本上不改变对象的结构 | 对象的结构是可以任意改变的 |
上下节点的关系 | 上下节点没有关系,都是接收同样的对象,所传递的对象都是从链首传过来的 | 上下级的关系和亲密,上级对下级绝对相信,下级对上级顶礼膜拜,链中相连的节点是一个牢固的独立团体 |
消息的分销渠道 | 方向单一、固定--链首到链尾 | 方向不固定,可以不同方向、跳跃都可以,取决于逻辑 |
声明
摘自秦小波《设计模式之禅》第2版;仅供学习,严禁商业用途;
代码手写,没有经编译器编译,有个别错误,自行根据上下文改正;
相关文章推荐
- 《设计模式之禅》——行为类模式大PK
- 【设计模式】 模式PK:观察者模式VS责任链模式
- Java 设计模式之观察者模式的详解(行为模式)
- 5.7 观察者模式observer(行为模式)
- [设计模式](十一):观察者模式|迭代器模式|责任链模式|命令模式(四种类间行为模式)
- 《设计模式之禅》——观察者模式
- 设计模式之禅【观察者模式】
- 行为模式之观察者模式
- Java设计模式_行为型_观察者模式_任意行为的监听
- 设计模式之禅——观察者模式
- 设计模式之禅之行为类PK【命令模式VS策略模式】
- 观察者模式(状态改变-通知-更新行为)
- 观察者——对象行为模式
- 设计模式之禅之行为类PK【策略模式VS状态模式】
- 观察者模式(Observer)(行为模式)
- 行为模式之观察者模式
- Java设计模式(17)——行为模式之观察者模式(Observer)
- 行为模式---之观察者设计模式
- 观察者模式-对象行为模式
- 行为模式之观察者模式