当前位置: 首页 > news >正文

广西建设工程质检安全网站大连线上教学

广西建设工程质检安全网站,大连线上教学,简述网站制作过程,做网站什么价位四、行为型模式 行为型模式用于描述程序在运行时复杂的流程控制#xff0c;即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务#xff0c;它涉及算法与对象间职责的分配。 行为型模式分为类行为模式和对象行为模式#xff0c;前者采用继承机制来在…四、行为型模式 行为型模式用于描述程序在运行时复杂的流程控制即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务它涉及算法与对象间职责的分配。 行为型模式分为类行为模式和对象行为模式前者采用继承机制来在类间分派行为后者采用组合或聚合在对象间分配行为。由于组合关系或聚合关系比继承关系耦合度低满足“合成复用原则”所以对象行为模式比类行为模式具有更大的灵活性。 行为型模式分为 模板方法模式策略模式命令模式职责链模式状态模式观察者模式中介者模式迭代器模式访问者模式备忘录模式解释器模式 以上 11 种行为型模式除了模板方法模式和解释器模式是类行为型模式其他的全部属于对象行为型模式。 1、模板方法模式 在面向对象程序设计过程中程序员常常会遇到这种情况设计一个系统时知道了算法所需的关键步骤而且确定了这些步骤的执行顺序但某些步骤的具体实现还未知或者说某些步骤的实现与具体的环境相关。 例如去银行办理业务一般要经过以下4个流程取号、排队、办理具体业务、对银行工作人员进行评分等其中取号、排队和对银行工作人员进行评分的业务对每个客户是一样的可以在父类中实现但是办理具体业务却因人而异它可能是存款、取款或者转账等可以延迟到子类中实现。 定义 定义一个操作中的算法骨架而将算法的一些步骤延迟到子类中使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。 模板方法Template Method模式包含以下主要角色 抽象类Abstract Class负责给出一个算法的轮廓和骨架。它由一个模板方法和若干个基本方法构成。 模板方法定义了算法的骨架按某种顺序调用其包含的基本方法。 基本方法是实现算法各个步骤的方法是模板方法的组成部分。基本方法又可以分为三种 抽象方法(Abstract Method) 一个抽象方法由抽象类声明、由其具体子类实现。 具体方法(Concrete Method) 一个具体方法由一个抽象类或具体类声明并实现其子类可以进行覆盖也可以直接继承。 钩子方法(Hook Method) 在抽象类中已经实现包括用于判断的逻辑方法和需要子类重写的空方法两种。 一般钩子方法是用于判断的逻辑方法这类方法名一般为isXxx返回值类型为boolean类型。 具体子类Concrete Class实现抽象类中所定义的抽象方法和钩子方法它们是一个顶级逻辑的组成步骤。 例子 炒菜的步骤是固定的分为倒油、热油、倒蔬菜、倒调料品、翻炒等步骤。现通过模板方法模式来用代码模拟。类图如下 注意为防止恶意操作一般模板方法都加上 final 关键词。 public abstract class AbstractClass {public final void cookProcess() {//第一步倒油this.pourOil();//第二步热油this.heatOil();//第三步倒蔬菜this.pourVegetable();//第四步倒调味料this.pourSauce();//第五步翻炒this.fry();}public void pourOil() {System.out.println(倒油);}//第二步热油是一样的所以直接实现public void heatOil() {System.out.println(热油);}//第三步倒蔬菜是不一样的一个下包菜一个是下菜心public abstract void pourVegetable();//第四步倒调味料是不一样public abstract void pourSauce();//第五步翻炒是一样的所以直接实现public void fry(){System.out.println(炒啊炒啊炒到熟啊);} }public class ConcreteClass_BaoCai extends AbstractClass {Overridepublic void pourVegetable() {System.out.println(下锅的蔬菜是包菜);}Overridepublic void pourSauce() {System.out.println(下锅的酱料是辣椒);} }public class ConcreteClass_CaiXin extends AbstractClass {Overridepublic void pourVegetable() {System.out.println(下锅的蔬菜是菜心);}Overridepublic void pourSauce() {System.out.println(下锅的酱料是蒜蓉);} }public class Client {public static void main(String[] args) {//炒手撕包菜ConcreteClass_BaoCai baoCai new ConcreteClass_BaoCai();baoCai.cookProcess();//炒蒜蓉菜心ConcreteClass_CaiXin caiXin new ConcreteClass_CaiXin();caiXin.cookProcess();} }1.1 优缺点及使用场景 优点 提高代码复用性 将相同部分的代码放在抽象的父类中而将不同的代码放入不同的子类中。 实现了反向控制 通过父类调用子类扩展的不同的方法实现了反向控制。 缺点 对每个不同的实现都需要定义一个子类这会导致类的个数增加系统更加庞大设计也更加抽象。父类中的抽象方法由子类实现子类执行的结果会影响父类的结果这导致一种反向的控制结构它提高了代码阅读的难度。 适用场景 算法的整体步骤很固定但其中个别部分易变时这时候可以使用模板方法模式将容易变的部分抽象出来供子类实现。需要通过子类来决定父类算法中某个步骤是否执行实现子类对父类的反向控制。 1.2 JDK 源码分析 InputStream类就使用了模板方法模式。在InputStream类中定义了多个 read() 方法如下 read(byte b[]) 方法调用了 模板方法 read(byte b[], int off, int len) , 在其内部又调用了无参的 read() 方法 来读取一个字节的数据具体如何读取由子类去实现。 public abstract class InputStream implements Closeable {//抽象方法要求子类必须重写public abstract int read() throws IOException;public int read(byte b[]) throws IOException {return read(b, 0, b.length);}// 模板方法: 定义了基本方法的执行流程public int read(byte b[], int off, int len) throws IOException {if (b null) {throw new NullPointerException();} else if (off 0 || len 0 || len b.length - off) {throw new IndexOutOfBoundsException();} else if (len 0) {return 0;}int c read(); //调用了无参的read方法该方法是每次读取一个字节数据if (c -1) {return -1;}b[off] (byte)c;int i 1;try {for (; i len ; i) {c read();if (c -1) {break;}b[off i] (byte)c;}} catch (IOException ee) {}return i;} }2、策略模式 先看下面的图片我们去旅游选择出行模式有很多种可以骑自行车、可以坐汽车、可以坐火车、可以坐飞机。 无论选择哪一种出行方式都是一种策略。而人 就是策略的使用者可以理解为策略的环境。 作为一个程序猿开发需要选择一款开发工具当然可以进行代码开发的工具有很多可以选择Idea进行开发也可以使用eclipse进行开发也可以使用其他的一些开发工具。 定义 ​ 该模式定义了一系列算法【策略】并将每个算法封装起来使它们可以相互替换且算法的变化不会影响使用算法的客户。策略模式属于对象行为模式它通过对算法进行封装把使用算法的责任和算法的实现分割开来并委派给不同的对象对这些算法进行管理。 策略模式的主要角色如下 抽象策略Strategy类这是一个抽象角色通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。具体策略Concrete Strategy类实现了抽象策略定义的接口提供具体的算法实现或行为。环境Context类持有一个策略类的引用最终给客户端调用。 例子 【例】促销活动 一家百货公司在定年度的促销活动。针对不同的节日春节、中秋节、圣诞节推出不同的促销活动由促销员将促销活动展示给客户。类图如下 每个促销活动都是一个策略而销售人员就是策略的使用环境将每种策略介绍给用户。 定义抽象策略接口 //抽象策略 public interface Strategy {void show(); } 定义具体的策略实现抽象策略接口 // 具体的策略 public class StrategyA implements Strategy{Overridepublic void show() {System.out.println(买一送一);} }// 具体的策略 public class StrategyB implements Strategy{Overridepublic void show() {System.out.println(满1000减200);} }// 具体的策略 public class StrategyC implements Strategy{Overridepublic void show() {System.out.println(满1200加一元换购);} } 策略使用的环境 // 环境 public class SaleMan {private Strategy strategy;public SaleMan(Strategy strategy) {this.strategy strategy;}public Strategy getStrategy() {return strategy;}public void setStrategy(Strategy strategy) {this.strategy strategy;}// 由销售人员展示策略public void showStrategy(){strategy.show();} } 测试 public class Client {public static void main(String[] args) {// 第一种策略SaleMan saleMan new SaleMan(new StrategyA());saleMan.showStrategy();System.out.println();// 第二种策略saleMan.setStrategy(new StrategyB());saleMan.showStrategy();System.out.println();// 第三种策略saleMan.setStrategy(new StrategyC());saleMan.showStrategy();} } 2.1 优缺点及使用场景 1优点 策略类之间可以自由切换 由于策略类都实现同一个接口所以使它们之间可以自由切换。 易于扩展 增加一个新的策略只需要添加一个具体的策略类即可基本不需要改变原有的代码符合“开闭原则“ 避免使用多重条件选择语句if else充分体现面向对象设计思想。 2缺点 客户端必须知道所有的策略类并自行决定使用哪一个策略类。策略模式将造成产生很多策略类可以通过使用享元模式在一定程度上减少对象的数量。 3、使用场景 一个系统需要动态地在几种算法中选择一种时可将每个算法封装到策略类中。一个类定义了多种行为并且这些行为在这个类的操作中以多个条件语句的形式出现可将每个条件分支移入它们各自的策略类中以代替这些条件语句。系统中各算法彼此完全独立且要求对客户隐藏具体算法的实现细节时。系统要求使用算法的客户不应该知道其操作的数据时可使用策略模式来隐藏与算法相关的数据结构。多个类只区别在表现行为不同可以使用策略模式在运行时动态选择具体要执行的行为。 3、命令模式 日常生活中我们出去吃饭都会遇到下面的场景。 客户下订单由服务员将订单转交给厨师厨师去准备餐点。 正常情况下服务员对象中调用厨师中方法通知厨师但是这样耦合度太高如果后期饭店更换厨师的话改动太大。因此我们可以使用命令模式将服务员和厨师分隔开通过命令对象通信。 定义 将一个请求封装为一个对象使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通这样方便将命令对象进行存储、传递、调用、增加与管理。 命令模式包含以下主要角色 抽象命令类Command角色 定义命令的接口声明执行的方法。具体命令Concrete Command角色具体的命令实现命令接口通常会持有接收者并调用接收者的功能来完成命令要执行的操作。实现者/接收者Receiver角色 接收者真正执行命令的对象。任何类都可能成为一个接收者只要它能够实现命令要求实现的相应功能。调用者/请求者Invoker角色 要求命令对象执行请求通常会持有命令对象可以持有很多的命令对象。这个是客户端真正触发命令并要求命令执行相应操作的地方也就是说相当于使用命令对象的入口。 例子 将上面的案例用代码实现那我们就需要分析命令模式的角色在该案例中由谁来充当。 服务员 就是调用者角色由她来发起命令。 资深大厨 就是接收者角色真正命令执行的对象。 订单 命令中包含订单。 类图如下 // 抽象命令类: 定义执行命令的方法 public interface Command {// 执行命令void execute(); }// 订单命令对象具体的命令角色 public class OrderCommand implements Command {private SeniorChef chef;private Order order;// 订单命令对象需要有执行者和具体的命令public OrderCommand(SeniorChef chef, Order order) {this.chef chef;this.order order;}// 执行命令Overridepublic void execute() {System.out.println(厨师开始做饭...);MapString, Integer foodDir order.getFoodDic();SetString set foodDir.keySet();for (String foodName : set) {// 厨师开始制作食物chef.makeFood(foodName,foodDir.get(foodName));}} }// 订单对象 public class Order {// 餐桌号private int diningTable;// 订单: 食物数量private MapString,Integer foodDic new HashMap();public int getDiningTable() {return diningTable;}public void setDiningTable(int diningTable) {this.diningTable diningTable;}public MapString, Integer getFoodDic() {return foodDic;}public void setFoodDic(String foodName,int num) {foodDic.put(foodName,num);} }// 厨师 public class SeniorChef {// 制作食物public void makeFood(String name,int num){System.out.println(厨师做了 num 份 name);} }// 服务员: 命令的发出者 public class Waitor {// 服务员可以发出多条命令private ListCommand commands new ArrayList();// 增加命令public void setCommands(Command command) {commands.add(command);}// 发出命令public void orderUp(){System.out.println(服务员发出命令...通知厨师开始做饭...);for (Command command : commands) {command.execute();}} } 3.1 优缺点及使用场景 1优点 降低系统的耦合度。将命令发送者和执行者解耦增加或删除命令非常方便。采用命令模式增加与删除命令不会影响其他类它满足“开闭原则”对扩展比较灵活。可以实现宏命令。命令模式可以与组合模式结合将多个命令装配成一个组合命令即宏命令。方便实现 Undo 和 Redo 操作。命令模式可以与后面介绍的备忘录模式结合实现命令的撤销与恢复。 2缺点 使用命令模式可能会导致某些系统有过多的具体命令类。系统结构更加复杂。 3、使用场景 系统需要将请求调用者和请求接收者解耦使得调用者和接收者不直接交互。系统需要在不同的时间指定请求、将请求排队和执行请求。系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作。 3.2 JDK 源码分析 Runable是一个典型命令模式Runnable 担当命令的角色Thread充当的是调用者start方法就是其执行方法 //命令接口(抽象命令角色) public interface Runnable {public abstract void run(); }//调用者通常持有命令对象。 public class Thread implements Runnable {private Runnable target;public synchronized void start() {if (threadStatus ! 0)throw new IllegalThreadStateException();group.add(this);boolean started false;try {start0();started true;} finally {try {if (!started) {group.threadStartFailed(this);}} catch (Throwable ignore) {}}}private native void start0(); }会调用一个native方法start0(),调用系统方法开启一个线程。而接收者是对程序员开放的可以自己定义接收者。 /*** jdk Runnable 命令模式* TurnOffThread 属于具体*/ public class TurnOffThread implements Runnable{private Receiver receiver;public TurnOffThread(Receiver receiver) {this.receiver receiver;}public void run() {receiver.turnOFF();} }4、责任链模式 在现实生活中常常会出现这样的事例一个请求有多个对象可以处理但每个对象的处理条件或权限不同。例如公司员工请假可批假的领导有部门负责人、副总经理、总经理等但每个领导能批准的天数不同员工必须根据自己要请假的天数去找不同的领导签名也就是说员工必须记住每个领导的姓名、电话和地址等信息这增加了难度。这样的例子还有很多如找领导出差报销、生活中的“击鼓传花”游戏等。 定义 又名职责链模式为了避免请求发送者与多个请求处理者耦合在一起将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链当有请求发生时可将请求沿着这条链传递直到有对象处理它为止。 职责链模式主要包含以下角色: 抽象处理者Handler角色定义一个处理请求的接口包含抽象处理方法和一个后继连接。具体处理者Concrete Handler角色实现抽象处理者的处理方法判断能否处理本次请求如果可以处理请求则处理否则将该请求转给它的后继者。客户类Client角色创建处理链并向链头的具体处理者对象提交请求它不关心处理细节和请求的传递过程 例子 现需要开发一个请假流程控制系统。请假一天以下的假只需要小组长同意即可请假1天到3天的假还需要部门经理同意请求3天到7天还需要总经理同意才行。 类图如下 代码如下 //请假条 public class LeaveRequest {private String name;//姓名private int num;//请假天数private String content;//请假内容public LeaveRequest(String name, int num, String content) {this.name name;this.num num;this.content content;}public String getName() {return name;}public int getNum() {return num;}public String getContent() {return content;} }// 抽象处理者: 定义抽象处理方法和后继处理者 public abstract class Handler {// 请假区间protected final static int LEAVE_ONE 1;protected final static int LEAVE_THREE 3;protected final static int LEAVE_SEVEN 7;// 每个处理者能够处理的区间private int numStart;private int numEnd;// 后继处理者private Handler nextHandler;// 声明下一个处理者public void setNextHandler(Handler nextHandler) {this.nextHandler nextHandler;}public Handler(int numStart, int numEnd) {this.numStart numStart;this.numEnd numEnd;}public Handler(int numStart) {this.numStart numStart;}// 抽象处理方法: 由具体的处理者实现public abstract void handleLeave(LeaveRequest leave);// 该处理者处理不了将请求转交给下一个处理者public final void submit(LeaveRequest leave){if (leave.getNum() 0) {return;}// 如果请假的天数在当前处理者范围之内就由当前处理者处理if (leave.getNum() this.numStart leave.getNum() this.numEnd){// 尝试让当前处理者处理this.handleLeave(leave);}else if (this.nextHandler ! null){// 否则就交给下一个处理者处理this.nextHandler.submit(leave);}else {// 都没有处理完成直接辞职~~啊哈哈System.out.println(超过七天请自动辞职~~~);}} }//小组长 public class GroupLeader extends Handler {public GroupLeader() {//小组长处理1-3天的请假super(Handler.NUM_ONE, Handler.NUM_THREE);}Overrideprotected void handleLeave(LeaveRequest leave) {System.out.println(leave.getName() 请假 leave.getNum() 天, leave.getContent() 。);System.out.println(小组长审批同意。);} }//部门经理 public class Manager extends Handler {public Manager() {//部门经理处理3-7天的请假super(Handler.NUM_THREE, Handler.NUM_SEVEN);}Overrideprotected void handleLeave(LeaveRequest leave) {System.out.println(leave.getName() 请假 leave.getNum() 天, leave.getContent() 。);System.out.println(部门经理审批同意。);} }//总经理 public class GeneralManager extends Handler {public GeneralManager() {//部门经理处理7天以上的请假super(Handler.NUM_SEVEN);}Overrideprotected void handleLeave(LeaveRequest leave) {System.out.println(leave.getName() 请假 leave.getNum() 天, leave.getContent() 。);System.out.println(总经理审批同意。);} }//测试类 public class Client {public static void main(String[] args) {//请假条来一张LeaveRequest leave new LeaveRequest(小花,5,身体不适);//各位领导GroupLeader groupLeader new GroupLeader();Manager manager new Manager();GeneralManager generalManager new GeneralManager();groupLeader.setNextHandler(manager);//小组长的领导是部门经理manager.setNextHandler(generalManager);//部门经理的领导是总经理//之所以在这里设置上级领导是因为可以根据实际需求来更改设置如果实战中上级领导人都是固定的则可以移到领导实现类中。//提交申请groupLeader.submit(leave);} }4.1 优缺点及使用场景 1优点 降低了对象之间的耦合度 该模式降低了请求发送者和接收者的耦合度。 增强了系统的可扩展性 可以根据需要增加新的请求处理类满足开闭原则。 增强了给对象指派职责的灵活性 当工作流程发生变化可以动态地改变链内的成员或者修改它们的次序也可动态地新增或者删除责任。 责任链简化了对象之间的连接 一个对象只需保持一个指向其后继者的引用不需保持其他所有处理者的引用这避免了使用众多的 if 或者 if···else 语句。 责任分担 每个类只需要处理自己该处理的工作不能处理的传递给下一个对象完成明确各类的责任范围符合类的单一职责原则。 2缺点 不能保证每个请求一定被处理。由于一个请求没有明确的接收者所以不能保证它一定会被处理该请求可能一直传到链的末端都得不到处理。对比较长的职责链请求的处理可能涉及多个处理对象系统性能将受到一定影响。职责链建立的合理性要靠客户端来保证增加了客户端的复杂性可能会由于职责链的错误设置而导致系统出错如可能会造成循环调用。 4.2 JDK 源码解析 在javaWeb应用开发中FilterChain是职责链过滤器模式的典型应用以下是Filter的模拟实现分析: 模拟web请求Request以及web响应Response public interface Request{}public interface Response{}模拟web过滤器Filter public interface Filter {public void doFilter(Request req,Response res,FilterChain c);}模拟实现具体过滤器 public class FirstFilter implements Filter {Overridepublic void doFilter(Request request, Response response, FilterChain chain) {System.out.println(过滤器1 前置处理);// 先执行所有request再倒序执行所有responsechain.doFilter(request, response);System.out.println(过滤器1 后置处理);} }public class SecondFilter implements Filter {Overridepublic void doFilter(Request request, Response response, FilterChain chain) {System.out.println(过滤器2 前置处理);// 先执行所有request再倒序执行所有responsechain.doFilter(request, response);System.out.println(过滤器2 后置处理);} }模拟实现过滤器链FilterChain public class FilterChain {private ListFilter filters new ArrayListFilter();private int index 0;// 链式调用public FilterChain addFilter(Filter filter) {this.filters.add(filter);return this;}public void doFilter(Request request, Response response) {if (index filters.size()) {return;}Filter filter filters.get(index);index;filter.doFilter(request, response, this);} }测试类 public class Client {public static void main(String[] args) {Request req null;Response res null ;FilterChain filterChain new FilterChain();filterChain.addFilter(new FirstFilter()).addFilter(new SecondFilter());filterChain.doFilter(req,res);} }5、状态模式 【例】通过按钮来控制一个电梯的状态一个电梯有开门状态关门状态停止状态运行状态。每一种状态改变都有可能要根据其他状态来更新处理。例如如果电梯门现在处于运行时状态就不能进行开门操作而如果电梯门是停止状态就可以执行开门操作。 类图如下 public interface ILift {//电梯的4个状态//开门状态public final static int OPENING_STATE 1;//关门状态public final static int CLOSING_STATE 2;//运行状态public final static int RUNNING_STATE 3;//停止状态public final static int STOPPING_STATE 4;//设置电梯的状态public void setState(int state);//电梯的动作public void open();public void close();public void run();public void stop(); }public class Lift implements ILift {private int state;Overridepublic void setState(int state) {this.state state;}//执行关门动作Overridepublic void close() {switch (this.state) {case OPENING_STATE:System.out.println(电梯关门了。。。);//只有开门状态可以关闭电梯门可以对应电梯状态表来看this.setState(CLOSING_STATE);//关门之后电梯就是关闭状态了break;case CLOSING_STATE://do nothing //已经是关门状态不能关门break;case RUNNING_STATE://do nothing //运行时电梯门是关着的不能关门break;case STOPPING_STATE://do nothing //停止时电梯也是关着的不能关门break;}}//执行开门动作Overridepublic void open() {switch (this.state) {case OPENING_STATE://门已经开了不能再开门了//do nothingbreak;case CLOSING_STATE://关门状态门打开:System.out.println(电梯门打开了。。。);this.setState(OPENING_STATE);break;case RUNNING_STATE://do nothing 运行时电梯不能开门break;case STOPPING_STATE:System.out.println(电梯门开了。。。);//电梯停了可以开门了this.setState(OPENING_STATE);break;}}//执行运行动作Overridepublic void run() {switch (this.state) {case OPENING_STATE://电梯不能开着门就走//do nothingbreak;case CLOSING_STATE://门关了可以运行了System.out.println(电梯开始运行了。。。);this.setState(RUNNING_STATE);//现在是运行状态break;case RUNNING_STATE://do nothing 已经是运行状态了break;case STOPPING_STATE:System.out.println(电梯开始运行了。。。);this.setState(RUNNING_STATE);break;}}//执行停止动作Overridepublic void stop() {switch (this.state) {case OPENING_STATE: //开门的电梯已经是是停止的了(正常情况下)//do nothingbreak;case CLOSING_STATE://关门时才可以停止System.out.println(电梯停止了。。。);this.setState(STOPPING_STATE);break;case RUNNING_STATE://运行时当然可以停止了System.out.println(电梯停止了。。。);this.setState(STOPPING_STATE);break;case STOPPING_STATE://do nothingbreak;}} }public class Client {public static void main(String[] args) {Lift lift new Lift();lift.setState(ILift.STOPPING_STATE);//电梯是停止的lift.open();//开门lift.close();//关门lift.run();//运行lift.stop();//停止} }问题分析 使用了大量的switch…case这样的判断if…else也是一样)使程序的可阅读性变差。扩展性很差。如果新加了断电的状态我们需要修改上面判断逻辑 使用状态模式进行改进… 定义 对有状态的对象把复杂的“判断逻辑”提取到不同的状态对象中允许状态对象在其内部状态发生改变时改变其行为。 状态模式包含以下主要角色。 环境Context角色也称为上下文它定义了客户程序需要的接口维护一个当前状态并将与状态相关的操作委托给当前状态对象来处理。抽象状态State角色定义一个接口用以封装环境对象中的特定状态所对应的行为。具体状态Concrete State角色实现抽象状态所对应的行为。 例子 对上述电梯的案例使用状态模式进行改进。类图如下 //抽象状态类 public abstract class LiftState {//定义一个环境角色也就是封装状态的变化引起的功能变化protected Context context;public void setContext(Context context) {this.context context;}//电梯开门动作public abstract void open();//电梯关门动作public abstract void close();//电梯运行动作public abstract void run();//电梯停止动作public abstract void stop(); }//开启状态 public class OpenningState extends LiftState {//开启当然可以关闭了我就想测试一下电梯门开关功能Overridepublic void open() {System.out.println(电梯门开启...);}Overridepublic void close() {//状态修改super.context.setLiftState(Context.closeingState);//动作委托为CloseState来执行也就是委托给了ClosingState子类执行这个动作super.context.getLiftState().close();}//电梯门不能开着就跑这里什么也不做Overridepublic void run() {//do nothing}//开门状态已经是停止的了Overridepublic void stop() {//do nothing} }//运行状态 public class RunningState extends LiftState {//运行的时候开电梯门你疯了电梯不会给你开的Overridepublic void open() {//do nothing}//电梯门关闭这是肯定了Overridepublic void close() {//虽然可以关门但这个动作不归我执行//do nothing}//这是在运行状态下要实现的方法Overridepublic void run() {System.out.println(电梯正在运行...);}//这个事绝对是合理的光运行不停止还有谁敢做这个电梯估计只有上帝了Overridepublic void stop() {super.context.setLiftState(Context.stoppingState);super.context.stop();} }//停止状态 public class StoppingState extends LiftState {//停止状态开门那是要的Overridepublic void open() {//状态修改super.context.setLiftState(Context.openningState);//动作委托为CloseState来执行也就是委托给了ClosingState子类执行这个动作super.context.getLiftState().open();}Overridepublic void close() {//虽然可以关门但这个动作不归我执行//状态修改super.context.setLiftState(Context.closeingState);//动作委托为CloseState来执行也就是委托给了ClosingState子类执行这个动作super.context.getLiftState().close();}//停止状态再跑起来正常的很Overridepublic void run() {//状态修改super.context.setLiftState(Context.runningState);//动作委托为CloseState来执行也就是委托给了ClosingState子类执行这个动作super.context.getLiftState().run();}//停止状态是怎么发生的呢当然是停止方法执行了Overridepublic void stop() {System.out.println(电梯停止了...);} }//关闭状态 public class ClosingState extends LiftState {Override//电梯门关闭这是关闭状态要实现的动作public void close() {System.out.println(电梯门关闭...);}//电梯门关了再打开逗你玩呢那这个允许呀Overridepublic void open() {super.context.setLiftState(Context.openningState);super.context.open();}//电梯门关了就跑这是再正常不过了Overridepublic void run() {super.context.setLiftState(Context.runningState);super.context.run();}//电梯门关着我就不按楼层Overridepublic void stop() {super.context.setLiftState(Context.stoppingState);super.context.stop();} }//环境角色 public class Context {//定义出所有的电梯状态public final static OpenningState openningState new OpenningState();//开门状态这时候电梯只能关闭public final static ClosingState closeingState new ClosingState();//关闭状态这时候电梯可以运行、停止和开门public final static RunningState runningState new RunningState();//运行状态这时候电梯只能停止public final static StoppingState stoppingState new StoppingState();//停止状态这时候电梯可以开门、运行//定义一个当前电梯状态private LiftState liftState;public LiftState getLiftState() {return this.liftState;}public void setLiftState(LiftState liftState) {//当前环境改变this.liftState liftState;//把当前的环境通知到各个实现类中this.liftState.setContext(this);}public void open() {this.liftState.open();}public void close() {this.liftState.close();}public void run() {this.liftState.run();}public void stop() {this.liftState.stop();} }//测试类 public class Client {public static void main(String[] args) {Context context new Context();context.setLiftState(new ClosingState());context.open();context.close();context.run();context.stop();} }5.1 优缺点及使用场景 1优点 将所有与某个状态有关的行为放到一个类中并且可以方便地增加新的状态只需要改变对象状态即可改变对象的行为。允许状态转换逻辑与状态对象合成一体而不是某一个巨大的条件语句块。 2缺点 状态模式的使用必然会增加系统类和对象的个数。状态模式的结构与实现都较为复杂如果使用不当将导致程序结构和代码的混乱。状态模式对开闭原则的支持并不太好。 3、使用场景 当一个对象的行为取决于它的状态并且它必须在运行时根据状态改变它的行为时就可以考虑使用状态模式。一个操作中含有庞大的分支结构并且这些分支决定于对象的状态时。 6、观察者模式 定义 又被称为发布-订阅Publish/Subscribe模式它定义了一种一对多的依赖关系让多个观察者对象同时监听某一个主题对象。这个主题对象在状态变化时会通知所有的观察者对象使他们能够自动更新自己。 在观察者模式中有如下角色 Subject抽象主题抽象被观察者抽象主题角色把所有观察者对象保存在一个集合里每个主题都可以有任意数量的观察者抽象主题提供一个接口可以增加和删除观察者对象。ConcreteSubject具体主题具体被观察者该角色将有关状态存入具体观察者对象在具体主题的内部状态发生改变时给所有注册过的观察者发送通知。Observer抽象观察者是观察者的抽象类它定义了一个更新接口使得在得到主题更改通知时更新自己。ConcrereObserver具体观察者实现抽象观察者定义的更新接口以便在得到主题更改通知时更新自身的状态。 例子 【例】微信公众号 在使用微信公众号时大家都会有这样的体验当你关注的公众号中有新内容更新的话它就会推送给关注公众号的微信用户端。我们使用观察者模式来模拟这样的场景微信用户就是观察者微信公众号是被观察者【具体主题】 类图如下 定义抽象主题类【抽象被观察者】 // 抽象被观察者抽象主题 public interface Subject {// 增加观察者void attach(Observer observer);// 删除观察者void detach(Observer observer);// 通知被观察者void notify(String message); }抽象观察者类 // 抽象观察者 public interface Observer {// 收到被观察者的内容void update(String message); }具体被观察者类【具体主题类】核心就是在更新被观察者内容时同时通知观察者 // 具体被观察者具体主题类 public class SubscriptionSubject implements Subject {// 被观察者集合private ListObserver observers new ArrayList();// 增加观察者Overridepublic void attach(Observer observer) {observers.add(observer);}// 删除观察者Overridepublic void detach(Observer observer) {observers.remove(observer);}// 通知观察者Overridepublic void notify(String message) {for (Observer observer : observers) {observer.update(message);}} } 具体观察者类 // 具体观察者 public class WxUser implements Observer{// 观察者姓名private String name;public WxUser(String name) {this.name name;}Overridepublic void update(String message) {System.out.println(name 收到了消息: message);} } 测试 // 测试 public class Client {public static void main(String[] args) {// 1、创建一个公众号SubscriptionSubject subject new SubscriptionSubject();// 2、增加公众号订阅用户subject.attach(new WxUser(熏悟空));subject.attach(new WxUser(虾悟净));subject.attach(new WxUser(别龙马));// 3、公众号更新内容subject.notify(不好了师傅不见了~);} }6.1 优缺点及使用场景 1优点 降低了目标与观察者之间的耦合关系两者之间是抽象耦合关系。被观察者发送通知所有注册的观察者都会收到信息【可以实现广播机制】 2缺点 如果观察者非常多的话那么所有的观察者收到被观察者发送的通知会耗时如果被观察者有循环依赖的话那么被观察者发送通知会使观察者循环调用会导致系统崩溃 3、使用场景 对象间存在一对多关系一个对象的状态发生改变会影响其他对象。当一个抽象模型有两个方面其中一个方面依赖于另一方面时。 6.2 JDK 源码解析 JDK 中的 Observable 和 Observer 就使用到了观察者模式。只要实现他们就能编写观察者模式实例 1、Observable Observable类是抽象的主题类也就是被观察者类它有一个 Vector 集合用于保存所有要通知的观察者对象下面来介绍它最重要的 3 个方法 void addObserver(Observer o) 方法用于将新的观察者对象添加到集合中。 void notifyObservers(Object arg) 方法调用集合中的所有观察者对象的 update方法通知它们数据发生改变。通常越晚加入集合的观察者越先得到通知【采用逆序遍历】。 void setChange() 方法用来设置一个 boolean 类型的内部标志注明目标对象发生了变化。当它为true时notifyObservers() 才会通知观察者。 public class Observable {private boolean changed false;private VectorObserver obs;// 增加观察者 public synchronized void addObserver(Observer o) {if (o null)throw new NullPointerException();if (!obs.contains(o)) {obs.addElement(o);}}// 通知所有的观察者 public void notifyObservers(Object arg) {Object[] arrLocal;synchronized (this) {// 通知标志只有当 changed true 时才会通知观察者if (!changed)return;arrLocal obs.toArray();clearChanged();}// 采用逆序遍历越晚加入集合的观察者越早被通知for (int i arrLocal.length-1; i0; i--)((Observer)arrLocal[i]).update(this, arg);} // 设置标志 protected synchronized void setChanged() {changed true;} }2、Observer Observer 接口是抽象观察者它监视目标对象的变化当目标对象发生变化时观察者得到通知并调用 update 方法进行相应的工作。 public interface Observer {void update(Observable o, Object arg); }6.3 小偷与警察案例 使用 JDK 提供的 Observer、Observable实现 【例】警察抓小偷也可以使用观察者模式来实现警察是观察者小偷是被观察者。代码如下 小偷是被观察者当小偷偷东西时警察接收到通知 // 小偷是被观察者: 小偷偷东西警察收到通知去抓捕 public class Thief extends Observable {private String name; // 小偷名字public Thief(String name) {this.name name;}public String getName() {return name;}public void setName(String name) {this.name name;}public void steal() {System.out.println( name 偷了东西...);// 设置通知标志setChanged();super.notifyObservers();} } 创建观察者警察 // 警察是观察者观察小偷 public class Policemen implements Observer {// 警察收到通知去抓捕小偷Overridepublic void update(Observable o, Object arg) {System.out.println(警察: ((Thief)o).getName() 放下武器);} } ceshi public class Client {public static void main(String[] args) {// 创建小偷对象Thief thief new Thief(法外狂徒张三);// 增加观察者让警察观察小偷thief.addObserver(new Policemen());// 小偷偷东西了thief.steal();} }7、中介者模式 一般来说同事类之间的关系是比较复杂的多个同事类之间互相关联时他们之间的关系会呈现为复杂的网状结构这是一种过度耦合的架构即不利于类的复用也不稳定。例如在下左图中有六个同事类对象假如对象1发生变化那么将会有4个对象受到影响。如果对象2发生变化那么将会有5个对象受到影响。也就是说同事类之间直接关联的设计是不好的。 如果引入中介者模式那么同事类之间的关系将变为星型结构从下右图中可以看到任何一个类的变动只会影响的类本身以及中介者这样就减小了系统的耦合。一个好的设计必定不会把所有的对象关系处理逻辑封装在本类中而是使用一个专门的类来管理那些不属于自己的行为。 定义 又叫调停模式定义一个中介角色来封装一系列对象之间的交互使原有对象之间的耦合松散且可以独立地改变它们之间的交互。 中介者模式包含以下主要角色 抽象中介者Mediator角色它是中介者的接口提供了同事对象注册与转发同事对象信息的抽象方法。 具体中介者ConcreteMediator角色实现中介者接口定义一个 List 来管理同事对象协调各个同事角色之间的交互关系因此它依赖于同事角色。 抽象同事类Colleague角色定义同事类的接口保存中介者对象提供同事对象交互的抽象方法实现所有相互影响的同事类的公共功能。 具体同事类Concrete Colleague角色是抽象同事类的实现者当需要与其他同事对象交互时由中介者对象负责后续的交互。 例子 【例】租房 现在租房基本都是通过房屋中介房主将房屋托管给房屋中介而租房者从房屋中介获取房屋信息。房屋中介充当租房者与房屋所有者之间的中介者。 类图如下 代码如下 //抽象中介者 public abstract class Mediator {//申明一个联络方法public abstract void constact(String message,Person person); }//抽象同事类 public abstract class Person {protected String name;protected Mediator mediator;public Person(String name,Mediator mediator){this.name name;this.mediator mediator;} }//具体同事类 房屋拥有者 public class HouseOwner extends Person {public HouseOwner(String name, Mediator mediator) {super(name, mediator);}//与中介者联系public void constact(String message){mediator.constact(message, this);}//获取信息public void getMessage(String message){System.out.println(房主 name 获取到的信息 message);} }//具体同事类 承租人 public class Tenant extends Person {public Tenant(String name, Mediator mediator) {super(name, mediator);}//与中介者联系public void constact(String message){mediator.constact(message, this);}//获取信息public void getMessage(String message){System.out.println(租房者 name 获取到的信息 message);} }//中介机构 public class MediatorStructure extends Mediator {//首先中介结构必须知道所有房主和租房者的信息private HouseOwner houseOwner;private Tenant tenant;public HouseOwner getHouseOwner() {return houseOwner;}public void setHouseOwner(HouseOwner houseOwner) {this.houseOwner houseOwner;}public Tenant getTenant() {return tenant;}public void setTenant(Tenant tenant) {this.tenant tenant;}public void constact(String message, Person person) {if (person houseOwner) { //如果是房主则租房者获得信息tenant.getMessage(message);} else { //反正则是房主获得信息houseOwner.getMessage(message);}} }//测试类 public class Client {public static void main(String[] args) {//一个房主、一个租房者、一个中介机构MediatorStructure mediator new MediatorStructure();//房主和租房者只需要知道中介机构即可HouseOwner houseOwner new HouseOwner(张三, mediator);Tenant tenant new Tenant(李四, mediator);//中介结构要知道房主和租房者mediator.setHouseOwner(houseOwner);mediator.setTenant(tenant);tenant.constact(需要租三室的房子);houseOwner.constact(我这有三室的房子你需要租吗);} }7.1 优缺点及使用场景 1优点 松散耦合 中介者模式通过把多个同事对象之间的交互封装到中介者对象里面从而使得同事对象之间松散耦合基本上可以做到互补依赖。这样一来同事对象就可以独立地变化和复用而不再像以前那样“牵一处而动全身”了。 集中控制交互 多个同事对象的交互被封装在中介者对象里面集中管理使得这些交互行为发生变化的时候只需要修改中介者对象就可以了当然如果是已经做好的系统那么就扩展中介者对象而各个同事类不需要做修改。 一对多关联转变为一对一的关联 没有使用中介者模式的时候同事对象之间的关系通常是一对多的引入中介者对象以后中介者对象和同事对象的关系通常变成双向的一对一这会让对象的关系更容易理解和实现。 2缺点 当同事类太多时中介者的职责将很大它会变得复杂而庞大以至于系统难以维护。 3、使用场景 系统中对象之间存在复杂的引用关系系统结构混乱且难以理解。当想创建一个运行于多个类之间的对象又不想生成新的子类时。 8、迭代器模式 定义 提供一个对象来顺序访问聚合对象中的一系列数据而不暴露聚合对象的内部表示。 迭代器模式主要包含以下角色 抽象聚合Aggregate角色定义存储、添加、删除聚合元素以及创建迭代器对象的接口。 具体聚合ConcreteAggregate角色实现抽象聚合类返回一个具体迭代器的实例。 抽象迭代器Iterator角色定义访问和遍历聚合元素的接口通常包含 hasNext()、next() 等方法。 具体迭代器Concretelterator角色实现抽象迭代器接口中所定义的方法完成对聚合对象的遍历记录遍历的当前位置。 例子 【例】定义一个可以存储学生对象的容器对象将遍历该容器的功能交由迭代器实现涉及到的类如下 代码如下、 // 存储的元素 public class Student {private String name;private int num ;public Student(String name, int num) {this.name name;this.num num;}Overridepublic String toString() {return Student{ name name \ , num num };}}// 抽象迭代器接口定义访问和遍历元素的方法 public interface StudentIterator {// 是否有下一个元素boolean hasNext();// 获取下一个元素Student next(); }// 具体迭代器类实现迭代器接口实现方法 public class StudentIteratorImpl implements StudentIterator {private ListStudent list;// 指针用来遍历集合中的元素private int pointer 0;// 传入指定的 list 集合public StudentIteratorImpl(ListStudent list) {this.list list;}// 判断是否有下一个元素Overridepublic boolean hasNext() {return pointer list.size();}// 遍历元素Overridepublic Student next() {// 从集合中取出元素并让指针1return list.get(pointer);} }// 抽象聚合类定义存储、删除元素获取迭代器的方法 public interface StudentAggregate {// 增加元素void add(Student stu);// 删除元素void remove(Student stu);// 获取迭代器StudentIterator getIterator(); }// 具体聚合类实现抽象聚合接口实现方法 public class StudentAggregateImpl implements StudentAggregate {private ListStudent list new ArrayList();Overridepublic void add(Student stu) {list.add(stu);}Overridepublic void remove(Student stu) {list.remove(stu);}// 获取迭代器Overridepublic StudentIterator getIterator() {return new StudentIteratorImpl(list);} }// 测试 public class Client {public static void main(String[] args) {// 创建集合StudentAggregateImpl studentAggregate new StudentAggregateImpl();studentAggregate.add(new Student(张三,1));studentAggregate.add(new Student(李四,2));studentAggregate.add(new Student(王五,3));// 获取迭代器StudentIterator iterator studentAggregate.getIterator();// 遍历while(iterator.hasNext()){System.out.println(iterator.next());}} } 8.1 优缺点及使用场景 1优点 它支持以不同的方式遍历一个聚合对象在同一个聚合对象上可以定义多种遍历方式。在迭代器模式中只需要用一个不同的迭代器来替换原有迭代器即可改变遍历算法我们也可以自己定义迭代器的子类以支持新的遍历方式。迭代器简化了聚合类。由于引入了迭代器在原有的聚合对象中不需要再自行提供数据遍历等方法这样可以简化聚合类的设计。在迭代器模式中由于引入了抽象层增加新的聚合类和迭代器类都很方便无须修改原有代码满足 “开闭原则” 的要求。 2缺点 增加了类的个数这在一定程度上增加了系统的复杂性。 3、 使用场景 当需要为聚合对象提供多种遍历方式时。当需要为遍历不同的聚合结构提供一个统一的接口时。当访问一个聚合对象的内容而无须暴露其内部细节的表示时。 8.2 JDK 源码解析 迭代器模式在JAVA的很多集合类中被广泛应用接下来看看JAVA源码中是如何使用迭代器模式的。 ListString list new ArrayList(); IteratorString iterator list.iterator(); //list.iterator()方法返回的肯定是Iterator接口的子实现类对象 while (iterator.hasNext()) {System.out.println(iterator.next()); }看完这段代码是不是很熟悉与我们上面代码基本类似。单列集合都使用到了迭代器我们以ArrayList举例来说明 List抽象聚合类ArrayList具体的聚合类实现了 List 接口重写了 iterator() 方法Iterator抽象迭代器Itr 具体迭代器类实现了 Iterator 接口重写 hasNext、Next 方法。list.iterator()返回的是实现了 Iterator 接口的具体迭代器对象 ArrayList源码 public class ArrayListE extends AbstractListEimplements ListE, RandomAccess, Cloneable, java.io.Serializable {// 实现了List中的 iterator 方法。获取迭代器// public IteratorE iterator() {return new Itr();}private class Itr implements IteratorE {int cursor; // 下一个要返回元素的索引int lastRet -1; // 上一个返回元素的索引int expectedModCount modCount;Itr() {}//判断是否还有元素public boolean hasNext() {return cursor ! size;}//获取下一个元素public E next() {checkForComodification();int i cursor;if (i size)throw new NoSuchElementException();Object[] elementData ArrayList.this.elementData;if (i elementData.length)throw new ConcurrentModificationException();cursor i 1;return (E) elementData[lastRet i];}... }ArrayList 实现了 List 接口并重写了 iterator() 方法通过 iterator() 方法获取了实现了 Iterator 接口的具体迭代器对象也就是 Itr 对象。 9、访问者模式 定义 封装一些作用于某种数据结构中的各元素的操作它可以在不改变这个数据结构的前提下定义作用于这些元素的新的操作。 访问者模式包含以下主要角色: 抽象访问者Visitor角色定义了对每一个元素Element访问的行为它的参数就是可以访问的元素它的方法个数理论上来讲与元素类个数Element的实现类个数是一样的从这点不难看出访问者模式要求元素类的个数不能改变。具体访问者ConcreteVisitor角色给出对每一个元素类访问时所产生的具体行为。抽象元素Element角色定义了一个接受访问者的方法accept其意义是指每一个元素都要可以被访问者访问。具体元素ConcreteElement角色 提供接受访问方法的具体实现而这个具体的实现通常情况下是使用访问者提供的访问该元素类的方法。对象结构Object Structure角色定义当中所提到的对象结构对象结构是一个抽象表述具体点可以理解为一个具有容器性质或者复合对象特性的类它会含有一组元素Element并且可以迭代这些元素供访问者访问。 例子 【例】给宠物喂食 现在养宠物的人特别多我们就以这个为例当然宠物还分为狗猫等要给宠物喂食的话主人可以喂其他人也可以喂食。 访问者角色给宠物喂食的人具体访问者角色主人、其他人抽象元素角色动物抽象类具体元素角色宠物狗、宠物猫结构对象角色主人家 类图如下 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Kj729Tu2-1677939060187)(https://images-1313160403.cos.ap-beijing.myqcloud.com/MarkDown/%E8%AE%BF%E9%97%AE%E8%80%85%E6%A8%A1%E5%BC%8F.png)] 代码如下 // 抽象元素角色 public interface Animals {// 接受访问者访问void accept(Person person); }// 具体元素角色 public class Cat implements Animals{Overridepublic void accept(Person person) {person.feed(this);System.out.println(猫好心人喂了我~);} }// 具体元素角色 public class Dog implements Animals{Overridepublic void accept(Person person) {person.feed(this);System.out.println(狗好心人喂了我~);} }// 抽象访问者角色 public interface Person {// 这里违反了依赖倒置原则不应该依赖于具体的类而应该依赖于抽象类或接口// 这也是访问者模式的一个缺点// 喂猫void feed(Cat cat);// 喂狗void feed(Dog dog); }// 具体访问者角色 public class Owner implements Person{Overridepublic void feed(Cat cat) {System.out.println(主人喂猫~);}Overridepublic void feed(Dog dog) {System.out.println(主人喂狗~);} }// 具体访问者角色 public class SomeOne implements Person{Overridepublic void feed(Cat cat) {System.out.println(其他人喂猫~);}Overridepublic void feed(Dog dog) {System.out.println(其他人喂狗~);} }// 对象结构角色 public class Home {// 保存宠物private ListAnimals nodeList new ArrayList();// 增加宠物public void add(Animals animals){nodeList.add(animals);}// 人对宠物的行为: 喂宠物public void action(Person person){for (Animals animal : nodeList) {animal.accept(person);}} }public class Client {public static void main(String[] args) {Home home new Home();home.add(new Dog());home.add(new Cat());Owner owner new Owner();home.action(owner);SomeOne someone new SomeOne();home.action(someone);} }9.1 优缺点及使用场景 1优点 扩展性好 在不修改对象结构中的元素的情况下为对象结构中的元素添加新的功能。 复用性好 通过访问者来定义整个对象结构通用的功能从而提高复用程度。 分离无关行为 通过访问者来分离无关的行为把相关的行为封装在一起构成一个访问者这样每一个访问者的功能都比较单一。 2缺点 对象结构变化很困难 在访问者模式中每增加一个新的元素类都要在每一个具体访问者类中增加相应的具体操作这违背了“开闭原则”。 违反了依赖倒置原则 访问者模式依赖了具体类而没有依赖抽象类。 3、使用场景 对象结构相对稳定但其操作算法经常变化的程序。 对象结构中的对象需要提供多种不同且不相关的操作而且要避免让这些操作的变化影响对象的结构。 10、备忘录模式 备忘录模式提供了一种状态恢复的实现机制使得用户可以方便地回到一个特定的历史步骤当新的状态无效或者存在问题时可以使用暂时存储起来的备忘录将状态复原很多软件都提供了撤销Undo操作如 Word、记事本、Photoshop、IDEA等软件在编辑时按 CtrlZ 组合键时能撤销当前操作使文档恢复到之前的状态还有在 浏览器 中的后退键、数据库事务管理中的回滚操作、玩游戏时的中间结果存档功能、数据库与操作系统的备份操作、棋类游戏中的悔棋功能等都属于这类。 定义 又叫快照模式在不破坏封装性的前提下捕获一个对象的内部状态并在该对象之外保存这个状态以便以后当需要时能将该对象恢复到原先保存的状态。 备忘录模式的主要角色如下 发起人Originator角色记录当前时刻的内部状态信息提供创建备忘录和恢复备忘录数据的功能实现其他业务功能它可以访问备忘录里的所有信息。备忘录Memento角色负责存储发起人的内部状态在需要的时候提供这些内部状态给发起人。管理者Caretaker角色对备忘录进行管理提供保存与获取备忘录的功能但其不能对备忘录的内容进行访问与修改。 备忘录有两个等效的接口 窄接口管理者(Caretaker)对象和其他发起人对象之外的任何对象看到的是备忘录的窄接口(narror Interface)这个窄接口只允许他把备忘录对象传给其他的对象。宽接口与管理者看到的窄接口相反发起人对象可以看到一个宽接口(wide Interface)这个宽接口允许它读取所有的数据以便根据这些数据恢复这个发起人对象的内部状态。 例子 【例】游戏挑战BOSS 游戏中的某个场景一游戏角色有生命力、攻击力、防御力等数据在打Boss前和后一定会不一样的我们允许玩家如果感觉与Boss决斗的效果不理想可以让游戏恢复到决斗之前的状态。 要实现上述案例有两种方式 “白箱”备忘录模式“黑箱”备忘录模式 10.1 “白箱”备忘录模式 备忘录角色对任何对象都提供一个接口即宽接口备忘录角色的内部所存储的状态就对所有对象公开。类图如下 代码如下 //游戏角色类 public class GameRole {private int vit; //生命力private int atk; //攻击力private int def; //防御力//初始化状态public void initState() {this.vit 100;this.atk 100;this.def 100;}//战斗public void fight() {this.vit 0;this.atk 0;this.def 0;}//保存角色状态public RoleStateMemento saveState() {return new RoleStateMemento(vit, atk, def);}//回复角色状态public void recoverState(RoleStateMemento roleStateMemento) {this.vit roleStateMemento.getVit();this.atk roleStateMemento.getAtk();this.def roleStateMemento.getDef();}public void stateDisplay() {System.out.println(角色生命力 vit);System.out.println(角色攻击力 atk);System.out.println(角色防御力 def);}public int getVit() {return vit;}public void setVit(int vit) {this.vit vit;}public int getAtk() {return atk;}public void setAtk(int atk) {this.atk atk;}public int getDef() {return def;}public void setDef(int def) {this.def def;} }//游戏状态存储类(备忘录类) public class RoleStateMemento {private int vit;private int atk;private int def;public RoleStateMemento(int vit, int atk, int def) {this.vit vit;this.atk atk;this.def def;}public int getVit() {return vit;}public void setVit(int vit) {this.vit vit;}public int getAtk() {return atk;}public void setAtk(int atk) {this.atk atk;}public int getDef() {return def;}public void setDef(int def) {this.def def;} }//角色状态管理者类 public class RoleStateCaretaker {private RoleStateMemento roleStateMemento;public RoleStateMemento getRoleStateMemento() {return roleStateMemento;}public void setRoleStateMemento(RoleStateMemento roleStateMemento) {this.roleStateMemento roleStateMemento;} }//测试类 public class Client {public static void main(String[] args) {System.out.println(------------大战Boss前------------);//大战Boss前GameRole gameRole new GameRole();gameRole.initState();gameRole.stateDisplay();//保存进度RoleStateCaretaker roleStateCaretaker new RoleStateCaretaker();roleStateCaretaker.setRoleStateMemento(gameRole.saveState());System.out.println(------------大战Boss后------------);//大战Boss时损耗严重gameRole.fight();gameRole.stateDisplay();System.out.println(------------恢复之前状态------------);//恢复之前状态gameRole.recoverState(roleStateCaretaker.getRoleStateMemento());gameRole.stateDisplay();} }分析白箱备忘录模式是破坏封装性的。但是通过程序员自律同样可以在一定程度上实现模式的大部分用意。 10.2 “黑箱”备忘录模式 备忘录角色对发起人对象提供一个宽接口而为其他对象提供一个窄接口。在Java语言中实现双重接口的办法就是将备忘录类设计成发起人类的内部成员类。 将 RoleStateMemento 设为 GameRole 的内部类从而将 RoleStateMemento 对象封装在 GameRole 里面在外面提供一个标识接口 Memento 给 RoleStateCaretaker 及其他对象使用。这样 GameRole 类看到的是 RoleStateMemento 所有的接口而RoleStateCaretaker 及其他对象看到的仅仅是标识接口 Memento 所暴露出来的接口从而维护了封装型。类图如下 代码如下 窄接口Memento这是一个标识接口因此没有定义出任何的方法 public interface Memento { }定义发起人类 GameRole并在内部定义备忘录内部类 RoleStateMemento该内部类设置为私有的 /游戏角色类 public class GameRole {private int vit; //生命力private int atk; //攻击力private int def; //防御力//初始化状态public void initState() {this.vit 100;this.atk 100;this.def 100;}//战斗public void fight() {this.vit 0;this.atk 0;this.def 0;}//保存角色状态public Memento saveState() {return new RoleStateMemento(vit, atk, def);}//回复角色状态public void recoverState(Memento memento) {RoleStateMemento roleStateMemento (RoleStateMemento) memento;this.vit roleStateMemento.getVit();this.atk roleStateMemento.getAtk();this.def roleStateMemento.getDef();}public void stateDisplay() {System.out.println(角色生命力 vit);System.out.println(角色攻击力 atk);System.out.println(角色防御力 def);}public int getVit() {return vit;}public void setVit(int vit) {this.vit vit;}public int getAtk() {return atk;}public void setAtk(int atk) {this.atk atk;}public int getDef() {return def;}public void setDef(int def) {this.def def;}private class RoleStateMemento implements Memento {private int vit;private int atk;private int def;public RoleStateMemento(int vit, int atk, int def) {this.vit vit;this.atk atk;this.def def;}public int getVit() {return vit;}public void setVit(int vit) {this.vit vit;}public int getAtk() {return atk;}public void setAtk(int atk) {this.atk atk;}public int getDef() {return def;}public void setDef(int def) {this.def def;}} }负责人角色类 RoleStateCaretaker 能够得到的备忘录对象是以 Memento 为接口的由于这个接口仅仅是一个标识接口因此负责人角色不可能改变这个备忘录对象的内容 //角色状态管理者类 public class RoleStateCaretaker {private Memento memento;public Memento getMemento() {return memento;}public void setMemento(Memento memento) {this.memento memento;} }客户端测试类 public class Client {public static void main(String[] args) {System.out.println(------------大战Boss前------------);//大战Boss前GameRole gameRole new GameRole();gameRole.initState();gameRole.stateDisplay();//保存进度RoleStateCaretaker roleStateCaretaker new RoleStateCaretaker();roleStateCaretaker.setMemento(gameRole.saveState());System.out.println(------------大战Boss后------------);//大战Boss时损耗严重gameRole.fight();gameRole.stateDisplay();System.out.println(------------恢复之前状态------------);//恢复之前状态gameRole.recoverState(roleStateCaretaker.getMemento());gameRole.stateDisplay();} } 10.3 优缺点及使用场景 1优点 提供了一种可以恢复状态的机制。当用户需要时能够比较方便地将数据恢复到某个历史的状态。实现了内部状态的封装。除了创建它的发起人之外其他对象都不能够访问这些状态信息。简化了发起人类。发起人不需要管理和保存其内部状态的各个备份所有状态信息都保存在备忘录中并由管理者进行管理这符合单一职责原则。 2缺点 资源消耗大。如果要保存的内部状态信息过多或者特别频繁将会占用比较大的内存资源。 3、使用场景 需要保存与恢复数据的场景如玩游戏时的中间结果的存档功能。 需要提供一个可回滚操作的场景如 Word、记事本、Photoshopidea等软件在编辑时按 CtrlZ 组合键还有数据库中事务操作。 11、解释器模式 如上图设计一个软件用来进行加减计算。我们第一想法就是使用工具类提供对应的加法和减法的工具方法。 //用于两个整数相加 public static int add(int a,int b){return a b; }//用于两个整数相加 public static int add(int a,int b,int c){return a b c; }//用于n个整数相加 public static int add(Integer ... arr) {int sum 0;for (Integer i : arr) {sum i;}return sum; }上面的形式比较单一、有限如果形式变化非常多这就不符合要求因为加法和减法运算两个运算符与数值可以有无限种组合方式。比如 12345、123-4等等。 显然现在需要一种翻译识别机器能够解析由数字以及 - 符号构成的合法的运算序列。如果把运算符和数字都看作节点的话能够逐个节点的进行读取解析运算这就是解释器模式的思维。 定义 给定一个语言定义它的文法表示并定义一个解释器这个解释器使用该标识来解释语言中的句子。 在解释器模式中我们需要将待解决的问题提取出规则抽象为一种“语言”。比如加减法运算规则为由数值和±符号组成的合法序列“13-2” 就是这种语言的句子。 解释器就是要解析出来语句的含义。但是如何描述规则呢 文法语法规则 文法是用于描述语言的语法结构的形式规则。 expression :: value | plus | minus plus :: expression ‘’ expression minus :: expression ‘-’ expression value :: integer注意 这里的符号“::”表示“定义为”的意思竖线 | 表示或左右的其中一个引号内为字符本身引号外为语法。 上面规则描述为 表达式可以是一个值也可以是plus或者minus运算而plus和minus又是由表达式结合运算符构成值的类型为整型数。 抽象语法树 在计算机科学中抽象语法树AbstractSyntaxTreeAST或简称语法树Syntax tree是源代码语法结构的一种抽象表示。它以树状的形式表现编程语言的语法结构树上的每个节点都表示源代码中的一种结构。 用树形来表示符合文法规则的句子。 解释器模式包含以下主要角色。 抽象表达式Abstract Expression角色定义解释器的接口约定解释器的解释操作主要包含解释方法 interpret()。 终结符表达式Terminal Expression角色是抽象表达式的子类用来实现文法中与终结符相关的操作文法中的每一个终结符都有一个具体终结表达式与之相对应。 非终结符表达式Nonterminal Expression角色也是抽象表达式的子类用来实现文法中与非终结符相关的操作文法中的每条规则都对应于一个非终结符表达式。 环境Context角色通常包含各个解释器需要的数据或是公共的功能一般用来传递被所有解释器共享的数据后面的解释器可以从这里获取这些值。 客户端Client主要任务是将需要分析的句子或表达式转换成使用解释器对象描述的抽象语法树然后调用解释器的解释方法当然也可以通过环境角色间接访问解释器的解释方法。 例子 【例】设计实现加减法的软件 代码如下 //抽象角色AbstractExpression public abstract class AbstractExpression {public abstract int interpret(Context context); }//终结符表达式角色 public class Value extends AbstractExpression {private int value;public Value(int value) {this.value value;}Overridepublic int interpret(Context context) {return value;}Overridepublic String toString() {return new Integer(value).toString();} }//非终结符表达式角色 加法表达式 public class Plus extends AbstractExpression {private AbstractExpression left;private AbstractExpression right;public Plus(AbstractExpression left, AbstractExpression right) {this.left left;this.right right;}Overridepublic int interpret(Context context) {return left.interpret(context) right.interpret(context);}Overridepublic String toString() {return ( left.toString() right.toString() );} }///非终结符表达式角色 减法表达式 public class Minus extends AbstractExpression {private AbstractExpression left;private AbstractExpression right;public Minus(AbstractExpression left, AbstractExpression right) {this.left left;this.right right;}Overridepublic int interpret(Context context) {return left.interpret(context) - right.interpret(context);}Overridepublic String toString() {return ( left.toString() - right.toString() );} }//终结符表达式角色 变量表达式 public class Variable extends AbstractExpression {private String name;public Variable(String name) {this.name name;}Overridepublic int interpret(Context ctx) {return ctx.getValue(this);}Overridepublic String toString() {return name;} }//环境类 public class Context {private MapVariable, Integer map new HashMapVariable, Integer();public void assign(Variable var, Integer value) {map.put(var, value);}public int getValue(Variable var) {Integer value map.get(var);return value;} }//测试类 public class Client {public static void main(String[] args) {Context context new Context();Variable a new Variable(a);Variable b new Variable(b);Variable c new Variable(c);Variable d new Variable(d);Variable e new Variable(e);//Value v new Value(1);context.assign(a, 1);context.assign(b, 2);context.assign(c, 3);context.assign(d, 4);context.assign(e, 5);AbstractExpression expression new Minus(new Plus(new Plus(new Plus(a, b), c), d), e);System.out.println(expression expression.interpret(context));} }11.1 优缺点及使用场景 1优点 易于改变和扩展文法。 由于在解释器模式中使用类来表示语言的文法规则因此可以通过继承等机制来改变或扩展文法。每一条文法规则都可以表示为一个类因此可以方便地实现一个简单的语言。 实现文法较为容易。 在抽象语法树中每一个表达式节点类的实现方式都是相似的这些类的代码编写都不会特别复杂。 增加新的解释表达式较为方便。 如果用户需要增加新的解释表达式只需要对应增加一个新的终结符表达式或非终结符表达式类原有表达式类代码无须修改符合 “开闭原则”。 2缺点 对于复杂文法难以维护。 在解释器模式中每一条规则至少需要定义一个类因此如果一个语言包含太多文法规则类的个数将会急剧增加导致系统难以管理和维护。 执行效率较低。 由于在解释器模式中使用了大量的循环和递归调用因此在解释较为复杂的句子时其速度很慢而且代码的调试过程也比较麻烦。 3、 使用场景 当语言的文法较为简单且执行效率不是关键问题时。 当问题重复出现且可以用一种简单的语言来进行表达时。
http://www.dnsts.com.cn/news/78795.html

相关文章:

  • 如何做网站 百度经验网站横幅怎么更换
  • 就业服务网站建设方案qq群怎么推广
  • php网站编程邮箱域名
  • 网站设计公司请示在招聘网站做销售怎么样
  • vs网站模板网上怎么做宣传啊
  • 全国网站建设大赛网站建设费走什么费用
  • 如何建网站老鱼网seo优化软件
  • 有哪几个网站可以做贸易html5自适应网站模版
  • 厦门市建设工程在哪备案网站湛江网站建设服务
  • 哈尔滨自主建站模板东营企业自助建站
  • 网优 是什么网站网站代码的重点内容是什么
  • 临沂品牌网站建设公司网站多个页面要加引导
  • 天津电子商务网站怎样学做网站
  • 网站信息备案变更 哪里做如何申请建设网站首页
  • 高端网站设计报价表wanwang
  • 大学学校网站建设方案简单小网站
  • 黑帽seo怎么做网站排名制作网页的图片
  • 网站简单设计网站两侧对联广告图片
  • 河南平顶山网站建设公司南昌百度推广公司
  • 南雄市建设局网站博物馆建设网站
  • 尤溪建设局网站建设银行北海分行网站
  • 三网合一 营销型网站有什么好的网站可以接单子做
  • 天王手表官方网站免费域名注册2023
  • 广州品牌网站china东莞seo
  • 南山建设网站现在最新技术有哪些
  • 如何利用源码做网站wordpress 画图插件
  • 建筑认证四川网站seo
  • 怎么做点播网站wordpress页面设置方法
  • 天津免费做网站山东seo优化
  • 做网站参考文献男女做暧昧视频网站