设计模式概述及分类:https://blog.csdn.net/qq_34896730/article/details/105324092
面向对象设计原则:https://editor.csdn.net/md/?articleId=105352240
在软件系统运行时对象并不是孤立存在的,它们可以通过相互通信协作完成某些功能,一个对象在运行时也将影响到其他对象的运行。行为型模式(Behavioral Pattern)关注系统中对象之间的交互,研究系统在运行时对象之间的相互通信与协作,进一步明确对象的职责。行为型模式不仅仅关注类和对象本身,还重点关注它们之间的相互作用和职责划分。
行为型模式分为类行为型模式和对象行为型模式两种,其中类行为型模式使用继承关系在几个类之间分配行为,主要通过多态等方式来分配父类与子类的职责;而对象行为型模式则使用对象的关联关系来分配行为,主要通过对象关联等方式来分配两个或多个类的职责。根据合成复用原则,在系统中复用功能时要尽量使用关联关系来取代继承关系,因此大部分行为型设计模式属于对象行为型设计模式。
在GOF设计模式中包含11种行为型模式,它们的名称、定义、学习难度和使用频率如表1-1所示。
模式名称 | 定 义 | 学习难度 | 使用频率 |
---|---|---|---|
责任链模式 (Chain of Responsibility Pattern) | 避免将一个请求的发送者与接收者耦合在一起,让多个对象都有机会处理请求。将接收请求的对象连成一条链,并且沿着这条链传递请求,直到有一个对象能够处理它为止 | ★★★☆☆ | ★★☆☆☆ |
命令模式 (Command Pattern) | 将一个请求封装为一个对象,从而可用不同的请求对客户进行参数化,对请求排队或者记录请求日志,以及支持可撤销的操作 | ★★★☆☆ | ★★★★☆ |
解释器模式 (Interpreter Pattern) | 给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子 | ★★★★★ | ★☆☆☆☆ |
迭代器模式 (Iterator Pattern) | 提供一种方法顺序访问一个聚合对象中的各个元素,而又不用暴露该对象的内部表示 | ★★★☆☆ | ★★★★★ |
中介者模式 (Mediator Pattern) | 定义一个对象来封装一系列对象的交互。中介者模式使各对象之间不需要显示地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互 | ★★★☆☆ | ★★☆☆☆ |
备忘录模式 (Memento Pattern) | 在不破坏封装的前提下捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态 | ★★☆☆☆ | ★★☆☆☆ |
观察者模式 ( | |||
Observer Pattern) | 定义对象之间的一种一对多依赖关系,使得每当一个对象状态发生改变时其相关依赖对象皆得到通知并被自动更新 | ★★★☆☆ | ★★★★★ |
状态模式 (State Pattern) | 允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类 | ★★★☆☆ | ★★★☆☆ |
策略模式 (Strategy Pattern) | 定义一系列算法,将每一个算法封装起来,并让它们可以相互替换,策略模式让算法可以独立于使用它们的客户而变化 | ★☆☆☆☆ | ★★★★☆ |
模板方法模式 (Template Method Pattern) | 定义一个操作中算法的框架,而将一些步骤延迟到子类中,模板方法模式使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤 | ★★☆☆☆ | ★★★☆☆ |
访问者模式 (Visitor Pattern) | 表示一个作用于某对象结构中的各个元素的操作。访问者模式让用户可以在不改变各元素的前提下定义作用于这些元素的新操作 | ★★★★☆ | ★☆☆☆☆ |
在很多情况下可以处理某个请求的对象并不止一个,例如大学里的奖学金审批,学生在向辅导员提交审批表之后首先是辅导员签字审批,然后交给系主任签字审批,接着是院长审批,最后可能是校长来审批,在这个过程中奖学金申请表可以看成是一个请求对象,而不同级别的审批者都可以处理请求对象,除了辅导员之外学生不需要一一与其他审批者交互,只需要等待结果即可。在审批过程中如果某一个审批者认为不符合条件,则请求中止;否则将请求递交给下一个审判者,最后由校长来确定能否授予奖学金。该过程可以用如下图2-1所示的示意图来表示。
在图2-1中,辅导员、系主任、院长、校长都可以处理奖学金申请表,他们构成一个处理申请表的链式结构,申请表沿着这条链进行传递,这条链就称为责任链。
责任链可以是一条直线、一个环或者一个树形结构,最常见的责任链是直线型,即沿着一条单向的链来传递请求。链上的每一个对象都是请求处理者,责任链模式可以将请求的处理者组织成一条链,并让请求沿着链传递,由链上的处理者对请求进行相应的处理,客户端无须关心请求的处理细节以及请求的传递,只需要将请求发送到链上即可,将请求的发送者和请求的处理者解耦。这就是责任链模式的模式动机。
责任链模式的定义: 避免将一个请求的发送者与接收者耦合在一起,让多个对象都有机会处理请求。将接收请求的对象连接成一条链,并且沿着这条链传递请求,直到有一个对象能够处理它为止。
责任链模式结构的核心在于引入了一个抽象处理者,其结构图如图3-1所示。
由图3-1可知,责任链模式包含以下两个角色。
(1) Handler(抽象处理者):它定义了一个处理请求的接口,一般设计为抽象类,由于不同的具体处理者处理请求的方式不同,因此在其中定义了抽象请求处理方法。每一个处理者的下家还是一个处理者,故在抽象处理者中定义了一个抽象处理者类型的对象(如结构图中的nextHandler)作为其对下家的引用,通过该引用处理者可以连成一条链。
(2) ConcreteHandler(具体处理者):它是抽象处理者的子类,可以处理用户请求,在具体处理者类中实现了抽象处理者中定义的抽象请求处理方法,在处理请求之前需要进行判断,看是否有相应的处理权限,如果可以处理请求就处理它,否则将请求转发给后继者;在具体处理者中可以访问链中的下一个对象,以便请求的转发。
在责任链模式中很多对象由每一个对象对其下家的引用连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织和分配责任。
责任链模式的核心在于抽象处理者类的设计,抽象处理者的典型代码如下:
public abstract class Handler { //维持对下家的引用 protected Handler nextHandler; public void setNextHandler(Handler nextHandler) { this.nextHandler= nextHandler; } public abstract void handleRequest(String request); }
在上述代码中,抽象处理者类定义了对下家的引用对象,以便将请求转发给下家,该对象的访问符可设计为protected,在其子类中可以使用。在抽象处理者类中中声明了抽象的请求处理方法,具体实现由子类完成。
具体处理者是抽象处理者的子类,它有两个作用:一是处理请求,不同的具体处理者以不同的形式实现抽象处理方法handleRequest();二是转发请求,如果该请求超出了当前处理者的权限,可以将请求转发给下家。具体处理者类的典型代码如下:
public class ConcreteHandler extends Handler { @Override public void handleRequest(String request) { if(请求满足条件){ //处理请求 }else{ this.nextHandler.handleRequest(request);//转发请求 } } }
在具体处理类中通过对请求进行判断可以做出相应的处理。
需要注意的是,责任链模式并不负责创建责任链,责任链的创建工作必须由系统的其他部分来完成,一般是在使用该责任链的客户端中创建责任链。责任链模式降低了请求的发送端和接收端之间的耦合,使多个对象都有机会处理这个请求。典型的客户端代码片段如下:
...... Handler handler1,handler2,handler3; handler1=new ConcreteHandlerA(); handler2=new ConcreteHandlerB(); handler3=new ConcreteHandlerC(); //创建责任链 handler1.setNextHandler(handler2); handler2.setNextHandler(handler3); //发送请求,请求对象通常为自定义类型 handler1.setNextHandler("请求对象"); ......
1. 实例说明
某企业的SCM(Supply Chain Management,供应链管理系统)系统中包含一个采购审批子系统。该企业的采购审批是分级进行的,即根据采购金额的不同由不同层次的主管人员来审批,主任可以审批5万元以下(不包括5万元)的采购单,副董事长可以审批5万元至10万元(不包括10万元)的采购单,董事长可以审批10万元至50万元(不包括50万元)的采购单,50万元以上的采购单则需要开董事会讨论决定。
2. 实例类图
通过分析,本实例的结构图如图4-1所示。
3. 实例代码
(1) PurchaseRequest:采购单类,充当请求类。
public class PurchaseRequest { private double amount;//采购金额 private int number;//采购单编号 private String purpose;//采购目的 public PurchaseRequest(double amount, int number, String purpose) { this.amount = amount; this.number = number; this.purpose = purpose; } public double getAmount() { return amount; } public void setAmount(double amount) { this.amount = amount; } public int getNumber() { return number; } public void setNumber(int number) { this.number = number; } public String getPurpose() { return purpose; } public void setPurpose(String purpose) { this.purpose = purpose; } }
(2) Approval:审批者类,充当抽象处理者。
public abstract class Approval { protected Approval successor;//定义后继对象 protected String name;//审批者姓名 public Approval(String name) { this.name = name; } //设置后继者 public void setSuccessor(Approval successor) { this.successor = successor; } //抽象请求处理方法 public abstract void processRequest(PurchaseRequest request); }
(3) Director:主任类,充当具体处理者。
public class Director extends Approval { public Director(String name) { super(name); } //具体请求处理方式 @Override public void processRequest(PurchaseRequest request) { if (request.getAmount() < 50000) { System.out.println("主任" + this.name + "审批采购单:" + request.getNumber() + ",金额:" + request.getAmount() + "元,采购目的:" + request.getPurpose() + "."); //处理请求 } else { this.successor.processRequest(request);//转发请求 } } }
(4) VicePresident:副董事长类,充当具体处理者。
public class VicePresident extends Approval { public VicePresident(String name) { super(name); } //具体请求方式 @Override public void processRequest(PurchaseRequest request) { if (request.getAmount() < 100000) { System.out.println("副董事长" + this.name + "审批采购单:" + request.getNumber() + ",金额:" + request.getAmount() + "元,采购目的:" + request.getPurpose() + "."); //处理请求 } else { this.successor.processRequest(request);//转发请求 } } }
(5) President:董事长类,充当具体处理者。
public class President extends Approval { public President(String name) { super(name); } //具体请求方式 @Override public void processRequest(PurchaseRequest request) { if (request.getAmount() < 500000) { System.out.println("董事长" + this.name + "审批采购单:" + request.getNumber() + ",金额:" + request.getAmount() + "元,采购目的:" + request.getPurpose() + "."); //处理请求 } else { this.successor.processRequest(request);//转发请求 } } }
(6) Congress:董事会类,充当具体处理者。
public class Congress extends Approval { public Congress(String name) { super(name); } //具体请求方式 @Override public void processRequest(PurchaseRequest request) { System.out.println("召开董事会审批采购单:" + request.getNumber() + ",金额:" + request.getAmount() + "元,采购目的:" + request.getPurpose() + "."); } }
(7) Client:客户端测试类。
public class Client { public static void main(String[] args) { Approval wjzhang, gyang, jguo, meeting; wjzhang = new Director("张无忌"); gyang = new VicePresident("杨过"); jguo = new President("郭靖"); meeting = new Congress("董事会"); //创建责任链 wjzhang.setSuccessor(gyang); gyang.setSuccessor(jguo); jguo.setSuccessor(meeting); //创建采购单 PurchaseRequest pr1 = new PurchaseRequest(45000, 10001, "购买倚天剑"); wjzhang.processRequest(pr1); PurchaseRequest pr2 = new PurchaseRequest(60000, 10002, "购买葵花宝典"); wjzhang.processRequest(pr2); PurchaseRequest pr3 = new PurchaseRequest(160000, 10003, "购买金刚经"); wjzhang.processRequest(pr3); PurchaseRequest pr4 = new PurchaseRequest(800000, 10004, "购买桃花岛"); wjzhang.processRequest(pr4); } }
4. 运行结果
主任张无忌审批采购单:10001,金额:45000.0元,采购目的:购买倚天剑. 副董事长杨过审批采购单:10002,金额:60000.0元,采购目的:购买葵花宝典. 董事长郭靖审批采购单:10003,金额:160000.0元,采购目的:购买金刚经. 召开董事会审批采购单:10004,金额:800000.0元,采购目的:购买桃花岛.
如果需要在系统中增加一个新的具体处理者,例如增加一个经理(Manager)角色可以审批5万元至8万元(不包括8万元)的采购单,需要编写一个新的具体处理者类Manager作为抽象处理者Approval的子类,实现在Approval类中定义的抽象处理方法,如果采购金额大于等于8万元,则将请求转发给下家。其代码如下:
public class Manager extends Approval { public Manager(String name) { super(name); } //具体请求方式 @Override public void processRequest(PurchaseRequest request) { if (request.getAmount() < 80000) { System.out.println("经理" + this.name + "审批采购单:" + request.getNumber() + ",金额:" + request.getAmount() + "元,采购目的:" + request.getPurpose() + "."); //处理请求 } else { this.successor.processRequest(request);//转发请求 } } }
由于链的创建过程由客户端负责,因此增加新的具体处理者对原有类库无任何影响,无须修改已有类的源代码,符合开闭原则。
在客户端代码中,如果要将新的具体请求处理者应用在系统中,需要创建新的具体处理者对象,然后将该对象加入到责任链中。在客户端测试代码中增加以下代码:
Approval rhuang; rhuang = new Manager("黄蓉");
将建链代码改为:
//创建责任链 wjzhang.setSuccessor(rhuang);//将"黄蓉"作为"张无忌"的下家 rhuang.setSuccessor(gyang);//将"杨过"作为"黄蓉"的下家 gyang.setSuccessor(jguo); jguo.setSuccessor(meeting);
运行结果:
主任张无忌审批采购单:10001,金额:45000.0元,采购目的:购买倚天剑. 经理黄蓉审批采购单:10002,金额:60000.0元,采购目的:购买葵花宝典. 董事长郭靖审批采购单:10003,金额:160000.0元,采购目的:购买金刚经. 召开董事会审批采购单:10004,金额:800000.0元,采购目的:购买桃花岛.
责任链模式可分为纯的责任链模式和不纯的责任链模式两种。
1.纯的责任链模式
一个纯的责任链模式要求一个具体处理者对象只能在两个行为中选择一个,要么承担全部责任,要么将责任推给下家,不允许出现某一个具体处理者对象在承担了一部分或全部责任后由将责任向下传递的情况。而且在纯的责任链模式中要求一个请求必须被某一个处理者对象所接收,不能出现某个请求未被任何一个处理者对象处理的情况。在第4节的采购审批实例中应用的是纯责任链模式。
2、不纯的责任链模式
在一个不纯的责任链模式中允许某个请求被一个具体处理者部分处理后再向下传递,或者一个具体处理者处理完某请求后其后继处理者可以继续处理该请求,而且一个请求可以最终不被任何处理者对象所接收并处理。
Java AWT1.0中的事件处理模型应用的是不纯的责任链模式,其基本原理如下:由于窗口组件(如按钮、文本框等)一般位于容器组件中,因此当时间发生在某一个组件上时先通过组件对象的handleEvent()方法将事件传递给相应的事件处理方法,该事件处理方法将处理此事件,然后决定是否将事件向上一级容器组件传播;上级容器组件在接到事件之后可以继续处理此事件,而无论此事件是否在上一级已得到处理,还存在事件未被处理的情况。显然,这就是不纯的责任链模式,早期的Java AWT事件模型(JDK1.0及更早)中的这种事件处理机制又叫事件浮升(Event Bubbling)机制。从Java1.1以后,JDK使用观察者模式代替责任链模式来处理事件。
责任链模式通过建立一条链来组织请求的处理者,请求沿着链进行传递,请求发送者无须知道请求在何时、何处以及如何被处理,实现了请求发送者与处理者的解耦。在软件开发中,如果遇到多个对象可以处理同一请求可以应用责任链模式,例如在Web应用开发中创建多个过滤器(Filter)链来对请求数据进行过滤,在工作流系统中实现公文的分级审批等,使用责任链可以较好地解决此类问题。Java语言中异常处理(Exception Handlers)机制也是责任链模式的典型应用之一,不同的catch子句可以处理不同类型的异常,这些catch子句构成了一条处理异常对象的责任链。
(1) 责任链模式使得一个对象无须知道是其他哪一个对象处理其请求,对象仅需知道该请求会被处理即可,接受者和发送者都没有对方的明确信息,并且链中的对象不需要知道链的结构,由客户端负责链的创建,降低了系统的耦合度。
(2) 请求处理对象仅需维持一个指向其后继者的引用,而不需要维持它对所有候选处理者的引用,可简化对象之间的耦合度。
(3) 在给对象分派职责时,责任链可以带来更多的灵活性,可以通过在运行时对该链进行动态的增加或修改来增加或改变处理一个请求的职责。
(4) 在系统中增加一个新的具体请求处理者时无须修改原有系统的代码,只需要在客户端重新建链即可,从这一点来看是符合开闭原则的。
(1) 由于一个请求没有明确的接收者,那么可能不能保证它一定会被处理,该请求可能一直到链的末端都得不到处理;一个请求也可能因职责链没有被正确配置而得不到处理。
(2) 对于较长的责任链,请求的处理可能涉及多个处理对象,系统性能将受到一定的影响,而且在进行代码调试时不太方便。
(3) 如果建链不当,可能会造成循环调用,将导致系统陷入死循环。
(1) 有多个对象可以处理同一个请求,具体哪个对象处理该请求待运行时刻再确定,客户端只需要将请求提交到链上,而无须关心请求的处理对象是谁以及它是如何处理的。
(2) 在不明确指定接收者的情况下向多个对象中的一个提交一个请求。
(3) 可动态指定一组对象处理请求,客户端可以动态地创建责任链来处理请求,还可以改变链中处理者之间的先后次序。