设计模式之禅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版;
- 仅供学习,严禁商业用途;
- 代码手写,没有经编译器编译,有个别错误,自行根据上下文改正;