设计模式4-韩

1、备忘录模式(Memento Pattern)

1、介绍

1、概述

(1)备忘录模式(Memento Pattern)在不破坏封装性的前提下,捕获一个对象的内 部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保 存的状态。

(2)可以这里理解备忘录模式:现实生活中的备忘录是用来记录某些要去做的事情, 或者是记录已经达成的共同意见的事情,以防忘记了。而在软件层面,备忘录 模式有着相同的含义,备忘录对象主要用来记录一个对象的某种状态,或者某 些数据,当要做回退时,可以从备忘录对象里获取原来的数据进行恢复操作。

传统的方法就是是简单地做备份,new出另外一个对象出来,再把需要备份的数据放到 这个新对象,但这就暴露了对象内部的细节。

2、原理类图

image-20200924171653120

(1)originator : 对象(需要保存状态的对象) 。

(2)Memento : 备忘录对象,负责保存好记录,即Originator内部 状态。

(3)Caretaker: 守护者对象,负责保存多个备忘录对象, 使用集合管理,提高效率。

说明:如果希望保存多个originator对象的不同时间的状态,也可以,只需要HashMap 。

2、案例

1、需求

游戏角色有攻击力和防御力,在大战Boss前保存自身的状态(攻击力和防御力),当大 战Boss后攻击力和防御力下降,从备忘录对象恢复到大战前的状态。

2、代码

(1)创建原生对象

package com.atguigu.memento.theory;

public class Originator {
    
    
   private String state;//状态信息

   public String getState() {
    
    
      return state;
   }

   public void setState(String state) {
    
    
      this.state = state;
   }
   
   //编写一个方法,可以保存一个状态对象 Memento
   //因此编写一个方法,返回 Memento
   public Memento saveStateMemento() {
    
    
      return new Memento(state);
   }
   
   //通过备忘录对象,恢复状态
   public void getStateFromMemento(Memento memento) {
    
    
      state = memento.getState();
   }
}

(2)创建备忘录对象

public class Memento {
    
    
   private String state;

   //构造器 将state放到属性中去
   public Memento(String state) {
    
    
      super();
      this.state = state;
   }

   public String getState() {
    
    
      return state;
   }
}

(3)创建备忘录对象执行者

import java.util.ArrayList;
import java.util.List;

//备忘录对象统一管理
public class Caretaker {
    
    
   //在List 集合中会有很多的备忘录对象
   private List<Memento> mementoList = new ArrayList<Memento>();

   public void add(Memento memento) {
    
    
      mementoList.add(memento);
   }

   //获取到第index个Originator 的 备忘录对象(即保存状态)
   public Memento get(int index) {
    
    
      return mementoList.get(index);
   }
}

(4)客户端

public class Client {
    
    
   public static void main(String[] args) {
    
    
      Originator originator = new Originator(); //创建一个原生对象
      Caretaker caretaker = new Caretaker();
      
      originator.setState(" 状态#1 攻击力 100 "); //设置状态
      
      //保存了当前的状态
      caretaker.add(originator.saveStateMemento());

      //保存状态2
      originator.setState(" 状态#2 攻击力 80 ");
      caretaker.add(originator.saveStateMemento());

      System.out.println("当前的状态是 =" + originator.getState());
      
      //希望得到状态 1, 将 originator 恢复到状态1
   originator.getStateFromMemento(caretaker.get(0));
      System.out.println("恢复到状态1 , 当前的状态是");
      System.out.println("当前的状态是 =" + originator.getState());
   }
}

3、细节

1、实现了信息的封装,使得用户不需要关心状态的保存细节。

2、如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定 的内存, 这个需要注意。

3、为了节约内存,备忘录模式可以和原型模式配合使用。

2、解释器模式(Interpreter Pattern)

1、介绍

1、概述

解释器模式(Interpreter Pattern):是指给定一个语言(表达式),定义它的文法 的一种表示,并定义一个解释器,使用该解释器来解释语言中的句子(表达式)。

2、应用场景

可以将一个需要解释执行的语言中的句子表示为一个抽象语法树。

3、原理类图

image-20200924193403967

image-20200924193411775

2、案例

1、需求

通过该模式,来实现四则运算,比如计算a+b的值。

3、状态模式(State Pattern)

1、介绍

1、概述

状态模式(State Pattern):它主要用来解决对象在多种状态转换时,需要对外 输出不同的行为的问题。状态和行为是一一对应的,状态之间可以相互转换。

当一个对象的内在状态改变时,允许改变其行为,这个对象看起来像是改变了 其类。

2、APP抽奖问题

image-20200924200604132

(1)假如每参加一次这个活动要 扣除用户50积分,中奖概率 是10%。

(2)奖品数量固定,抽完就不能抽奖

(3)活动有四个状态: 可以抽奖、 不能抽奖、发放奖品和奖品 领完

3、原理类图

image-20200924200729453

2、案例

image-20200924200756388

1、代码

(1)状态抽象类

public abstract class State {
    
    
	// 扣除积分 - 50
	public abstract void deductMoney();

	// 是否抽中奖品
	public abstract boolean raffle();

	// 发放奖品
	public abstract void dispensePrize();
}

(2)四种状态

package com.atguigu.state;

/**
 * 不能抽奖状态
 */
public class NoRaffleState extends State {
    
    

   // 初始化时传入活动引用,扣除积分后改变其状态
   RaffleActivity activity;

   public NoRaffleState(RaffleActivity activity) {
    
    
      this.activity = activity;
   }

   // 当前状态可以扣积分 , 扣除后,将状态设置成可以抽奖状态
   @Override
   public void deductMoney() {
    
    
      System.out.println("扣除50积分成功,您可以抽奖了");
      activity.setState(activity.getCanRaffleState());
   }

   // 当前状态不能抽奖
   @Override
   public boolean raffle() {
    
    
      System.out.println("扣了积分才能抽奖喔!");
      return false;
   }

   // 当前状态不能发奖品
   @Override
   public void dispensePrize() {
    
    
      System.out.println("不能发放奖品");
   }
}
package com.atguigu.state;

import java.util.Random;

/**
 * 可以抽奖的状态
 */
public class CanRaffleState extends State {
    
    

   RaffleActivity activity;

   public CanRaffleState(RaffleActivity activity) {
    
    
      this.activity = activity;
   }

   //已经扣除了积分,不能再扣
   @Override
   public void deductMoney() {
    
    
      System.out.println("已经扣取过了积分");
   }

   //可以抽奖, 抽完奖后,根据实际情况,改成新的状态
   @Override
   public boolean raffle() {
    
    
      System.out.println("正在抽奖,请稍等!");
      Random r = new Random();
      int num = r.nextInt(10);
      // 10%中奖机会
      if (num > 0 ) {
    
    
         // 改变活动状态为发放奖品 context
         activity.setState(activity.getDispenseState());
         return true;
      } else {
    
    
         System.out.println("很遗憾没有抽中奖品!");
         // 改变状态为不能抽奖
         activity.setState(activity.getNoRafflleState());
         return false;
      }
   }

   // 不能发放奖品
   @Override
   public void dispensePrize() {
    
    
      System.out.println("没中奖,不能发放奖品");
   }
}
package com.atguigu.state;

/**
 * 发放奖品的状态
 */
public class DispenseState extends State {
    
    
   // 初始化时传入活动引用,发放奖品后改变其状态
   RaffleActivity activity;

   public DispenseState(RaffleActivity activity) {
    
    
      this.activity = activity;
   }
   @Override
   public void deductMoney() {
    
    
      System.out.println("不能扣除积分");
   }

   @Override
   public boolean raffle() {
    
    
      System.out.println("不能抽奖");
      return false;
   }

   //发放奖品
   @Override
   public void dispensePrize() {
    
    
      if (activity.getCount() > 0) {
    
    
         System.out.println("恭喜中奖了");
         // 改变状态为不能抽奖
         activity.setState(activity.getNoRafflleState());
      } else {
    
    
         System.out.println("很遗憾,奖品发送完了");
         // 改变状态为奖品发送完毕, 后面我们就不可以抽奖
         activity.setState(activity.getDispensOutState());
      }
   }
}
package com.atguigu.state;

/**
 * 奖品发放完毕状态
 * 说明,当我们activity 改变成 DispenseOutState, 抽奖活动结束

 */
public class DispenseOutState extends State {
    
    

   // 初始化时传入活动引用
   RaffleActivity activity;

   public DispenseOutState(RaffleActivity activity) {
    
    
      this.activity = activity;
   }

   @Override
   public void deductMoney() {
    
    
      System.out.println("奖品发送完了,请下次再参加");
   }

   @Override
   public boolean raffle() {
    
    
      System.out.println("奖品发送完了,请下次再参加");
      return false;
   }

   @Override
   public void dispensePrize() {
    
    
      System.out.println("奖品发送完了,请下次再参加");
   }
}

(3)抽奖活动

package com.atguigu.state;

/**
 * 抽奖活动
 */
public class RaffleActivity {
    
    
   // state 表示活动当前的状态,是变化
    State state = null;
    // 奖品数量
    int count = 0;
    
    // 四个属性,表示四种状态
    State noRafflleState = new NoRaffleState(this);
    State canRaffleState = new CanRaffleState(this);
    State dispenseState =   new DispenseState(this);
    State dispensOutState = new DispenseOutState(this);

    //构造器
    //1. 初始化当前的状态为 noRafflleState(即不能抽奖的状态)
    //2. 初始化奖品的数量
    public RaffleActivity( int count) {
    
    
        this.state = getNoRafflleState();
        this.count = count;
    }

    //扣分, 调用当前状态的 deductMoney
    public void debuctMoney(){
    
    
        state.deductMoney();
    }

    //抽奖 
    public void raffle(){
    
    
       // 如果当前的状态是抽奖成功
        if(state.raffle()){
    
    
           //领取奖品
            state.dispensePrize();
        }

    }

    public State getState() {
    
    
        return state;
    }

    public void setState(State state) {
    
    
        this.state = state;
    }

    //这里请大家注意,每领取一次奖品,count--
    public int getCount() {
    
    
       int curCount = count; 
       count--;
        return curCount;
    }

    public void setCount(int count) {
    
    
        this.count = count;
    }

    public State getNoRafflleState() {
    
    
        return noRafflleState;
    }

    public void setNoRafflleState(State noRafflleState) {
    
    
        this.noRafflleState = noRafflleState;
    }

    public State getCanRaffleState() {
    
    
        return canRaffleState;
    }

    public void setCanRaffleState(State canRaffleState) {
    
    
        this.canRaffleState = canRaffleState;
    }

    public State getDispenseState() {
    
    
        return dispenseState;
    }

    public void setDispenseState(State dispenseState) {
    
    
        this.dispenseState = dispenseState;
    }

    public State getDispensOutState() {
    
    
        return dispensOutState;
    }

    public void setDispensOutState(State dispensOutState) {
    
    
        this.dispensOutState = dispensOutState;
    }
}

(4)客户端

public class ClientTest {
    
    
   public static void main(String[] args) {
    
    
      // 创建活动对象,奖品有1个奖品
        RaffleActivity activity = new RaffleActivity(1);

        // 我们连续抽300次奖
        for (int i = 0; i < 10; i++) {
    
    
            System.out.println("--------第" + (i + 1) + "次抽奖----------");
            // 参加抽奖,第一步点击扣除积分
            activity.debuctMoney();

            // 第二步抽奖
            activity.raffle();
        }
   }
}

3、细节

1、代码有很强的可读性。状态模式将每个状态的行为封装到对应的一个类中。

2、方便维护。将容易产生问题的if-else语句删除了,如果把每个状态的行为都放到一 个类中,每次调用方法时都要判断当前是什么状态,不但会产出很多if-else语句, 而且容易出错。

4、策略模式(Strategy Pattern)

1、介绍

1、概述

该模式定义算法族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。

该算法体现了几个设计原则,第一、把变化的代码从不变的代码中分离出来; 第二、针对接口编程而不是具体类(定义了策略接口);第三、多用组合/聚合, 少用继承(客户通过组合方式使用策略)。

2、原理类图

image-20200924201717142

从上图可以看到,客户context 有成员变量strategy或者其他的策略接口 ,至于需要使用到哪个策略,我们可以在构造器中指定。

2、案例

1、需求

有各种鸭子(比如 野鸭、北京鸭、水鸭等, 鸭子有各种行为,比如 叫、飞行等) ,此外,显示鸭子的信息。

使用传统方式时,存在一下问题:对于其它鸭子,都继承了Duck类,所以fly让所有子类都会飞了,这是不正确的。

image-20200924203536263

image-20200924203604398

2、代码

(1)Duck类

package com.atguigu.strategy.improve;

public abstract class Duck {
    
    
   //属性, 策略接口
   FlyBehavior flyBehavior;
   //其它属性<->策略接口
   QuackBehavior quackBehavior;

   public Duck() {
    
    
   }

   public abstract void display();//显示鸭子信息

   public void quack() {
    
    
      System.out.println("鸭子嘎嘎叫~~");
   }

   public void swim() {
    
    
      System.out.println("鸭子会游泳~~");
   }

   public void fly() {
    
    
      if (flyBehavior != null) {
    
     //说明该接口有子类赋值
         flyBehavior.fly();
      }
   }

   public void setFlyBehavior(FlyBehavior flyBehavior) {
    
    
      this.flyBehavior = flyBehavior;
   }

   public void setQuackBehavior(QuackBehavior quackBehavior) {
    
    
      this.quackBehavior = quackBehavior;
   }
}

(2)飞行为接口

public interface FlyBehavior {
    
    
   void fly(); // 子类具体实现
}

(3)叫行为接口

public interface QuackBehavior {
    
    
	void quack();//子类实现
}

(4)针对飞行的策略

//针对飞行有不同的策略
public class GoodFlyBehavior implements FlyBehavior {
    
    
   @Override
   public void fly() {
    
    
      System.out.println(" 飞翔技术高超 ~~~");
   }
}
public class NoFlyBehavior implements FlyBehavior {
    
    
   @Override
   public void fly() {
    
    
      System.out.println(" 不会飞翔 ");
   }
}

(5)野鸭

public class WildDuck extends Duck {
    
    
   //构造器,传入FlyBehavor 的对象
   public WildDuck() {
    
    
      //按照下面的格式书写
      flyBehavior = new GoodFlyBehavior();
   }

   @Override
   public void display() {
    
    
      System.out.println(" 这是野鸭 ");
   }
}

(6)客户端测试

public class Client {
    
    
   public static void main(String[] args) {
    
    
      WildDuck wildDuck = new WildDuck();
      wildDuck.fly();//

      //动态改变某个对象的行为, 野鸭 不能飞
      wildDuck.setFlyBehavior(new NoFlyBehavior());
      System.out.println("野鸭的实际飞翔能力");
      wildDuck.fly();
   }
}

3、细节

(1)策略模式的关键是:分析项目中变化部分与不变部分。

(2)策略模式的核心思想是:多用组合/聚合 少用继承;用行为类组合,而不是行为的 继承。更有弹性。

(3)体现了“对修改关闭,对扩展开放”原则,客户端增加行为不用修改原有代码,只 要添加一种策略(或者行为)即可,避免了使用多重转移语句(if…else if…else)。

(4)提供了可以替换继承关系的办法: 策略模式将算法封装在独立的Strategy类中使得 你可以独立于其Context改变它,使它易于切换、易于理解、易于扩展。

(5)需要注意的是:每添加一个策略就要增加一个类,当策略过多是会导致类数目庞大

5、职责链模式(Chain of Responsibility Pattern)

1、介绍

1、概述

职责链模式, 又叫责任链模式,为请求创建了一个接收者对象的链(简单示意图)。这种模式对请求的发送者和接收者进行解耦。

职责链模式通常每个接收者都包含对另一个接 收者的引用。如果一个对象不能处理该请求, 那么它会把相同的请求传给下一个接收者,依此类推。

该模式 使多个对象都有机会处理请求,从而避 免请求的发送者和接收者之间的耦合关 系。将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理 它为止。

2、原理类图

image-20200924204318285

(1)Handler : 抽象的处理者, 定义了一个处理请求的接口, 同时含义另外Handler。

(2)ConcreteHandlerA , B:是具体的处理者, 处理它自己负责的请求, 可以访问它的后继者(即下一个处理者), 如果可以处理当前请求,则处理,否则就将该请求交个后继者去处理,从而形成一个职责链。

(3)Request:包含很多属性,表示一个请求。

2、案例

1、需求

编写程序完成学校OA系统的采购审批项目:

如果金额 小于等于5000, 由教学主任审批;

如果金额 小于等于10000, 由院长审批 ;

如果金额 小于等于30000, 由副校长审批;

如果金额 超过30000以上,有校长审批。

image-20200924210222612

2、传统方式

传统方式是:接收到一个采购请求后,根据采购金额来调用对应的Approver (审批 人)完成审批。

客户端这里会使用到分支判断(比如 switch) 来对不同的采 购请求处理, 这样就存在如下问题:

(1) 如果各个级别的人员审批金额发生变化,在客户端的也需要变化;

(2) 客户端必须明确的知道,有多少个审批级别和访问。

这样 对一个采购请求进行处理 和 Approver (审批人) 就存在强耦合关系,不利于代码的扩展和维护。

3、代码

(1)请求类

public class PurchaseRequest {
    
    
   private int type = 0; //请求类型
   private float price = 0.0f; //请求金额
   private int id = 0;

   //构造器
   public PurchaseRequest(int type, float price, int id) {
    
    
      this.type = type;
      this.price = price;
      this.id = id;
   }

   public int getType() {
    
    
      return type;
   }

   public float getPrice() {
    
    
      return price;
   }

   public int getId() {
    
    
      return id;
   }
}

(2)抽象处理者

public abstract class Approver {
    
    
   Approver approver;  //下一个处理者
   String name; // 名字
   
   public Approver(String name) {
    
    
      this.name = name;
   }

   //下一个处理者
   public void setApprover(Approver approver) {
    
    
      this.approver = approver;
   }
   
   //处理审批请求的方法,得到一个请求, 处理是子类完成,因此该方法做成抽象
   public abstract void processRequest(PurchaseRequest purchaseRequest);
}

(3)具体处理者

//1、系主任
public class DepartmentApprover extends Approver {
    
    
   public DepartmentApprover(String name) {
    
    
      super(name);
   }

   @Override
   public void processRequest(PurchaseRequest purchaseRequest) {
    
    
      if (purchaseRequest.getPrice() <= 5000) {
    
    
         System.out.println(" 请求编号 id= " + purchaseRequest.getId() + " 被 " + this.name + " 处理");
      } else {
    
    
         //处理不了,让后继者去处理
         approver.processRequest(purchaseRequest);
      }
   }
}
//2、院长级别
public class CollegeApprover extends Approver {
    
    
   public CollegeApprover(String name) {
    
    
      super(name);
   }

   @Override
   public void processRequest(PurchaseRequest purchaseRequest) {
    
    
      if (purchaseRequest.getPrice() < 5000 && purchaseRequest.getPrice() <= 10000) {
    
    
         System.out.println(" 请求编号 id= " + purchaseRequest.getId() + " 被 " + this.name + " 处理");
      } else {
    
    
         approver.processRequest(purchaseRequest);
      }
   }
}
//3、副校长级别
public class ViceSchoolMasterApprover extends Approver {
    
    
   public ViceSchoolMasterApprover(String name) {
    
    
      super(name);
   }

   @Override
   public void processRequest(PurchaseRequest purchaseRequest) {
    
    
      if (purchaseRequest.getPrice() < 10000 && purchaseRequest.getPrice() <= 30000) {
    
    
         System.out.println(" 请求编号 id= " + purchaseRequest.getId() + " 被 " + this.name + " 处理");
      } else {
    
    
         approver.processRequest(purchaseRequest);
      }
   }
}
//4、校长级别
public class SchoolMasterApprover extends Approver {
    
    
   public SchoolMasterApprover(String name) {
    
    
      super(name);
   }

   @Override
   public void processRequest(PurchaseRequest purchaseRequest) {
    
    
      if (purchaseRequest.getPrice() > 30000) {
    
    
         System.out.println(" 请求编号 id= " + purchaseRequest.getId() + " 被 " + this.name + " 处理");
      } else {
    
    
         approver.processRequest(purchaseRequest);
      }
   }
}

(4)客户端

public class Client {
    
    
   public static void main(String[] args) {
    
    
      //创建一个请求
      PurchaseRequest purchaseRequest = new PurchaseRequest(1, 31000, 1);

      //创建相关的审批人
      DepartmentApprover departmentApprover = new DepartmentApprover("张主任");
      CollegeApprover collegeApprover = new CollegeApprover("李院长");
      ViceSchoolMasterApprover viceSchoolMasterApprover = new ViceSchoolMasterApprover("王副校");
      SchoolMasterApprover schoolMasterApprover = new SchoolMasterApprover("佟校长");


      //需要将各个审批级别的下一个设置好 (处理人构成环形: )
      departmentApprover.setApprover(collegeApprover);
      collegeApprover.setApprover(viceSchoolMasterApprover);
      viceSchoolMasterApprover.setApprover(schoolMasterApprover);
      schoolMasterApprover.setApprover(departmentApprover);

      departmentApprover.processRequest(purchaseRequest);
      viceSchoolMasterApprover.processRequest(purchaseRequest);
   }
}

3、细节

1、简化了对象,使对象不需要知道链的结构。

2、性能会受到影响,特别是在链比较长的时候,因此需控制链中最大节点数量,一般 通过在Handler中设置一个最大节点数量,在setNext()方法中判断是否已经超过阀值, 超过则不允许该链建立,避免出现超长链无意识地破坏系统性能。

3、调试不方便。采用了类似递归的方式,调试时逻辑可能比较复杂。

eApprover collegeApprover = new CollegeApprover(“李院长”);
ViceSchoolMasterApprover viceSchoolMasterApprover = new ViceSchoolMasterApprover(“王副校”);
SchoolMasterApprover schoolMasterApprover = new SchoolMasterApprover(“佟校长”);

  //需要将各个审批级别的下一个设置好 (处理人构成环形: )
  departmentApprover.setApprover(collegeApprover);
  collegeApprover.setApprover(viceSchoolMasterApprover);
  viceSchoolMasterApprover.setApprover(schoolMasterApprover);
  schoolMasterApprover.setApprover(departmentApprover);

  departmentApprover.processRequest(purchaseRequest);
  viceSchoolMasterApprover.processRequest(purchaseRequest);

}
}


### 3、细节

1、简化了对象,使对象不需要知道链的结构。

2、性能会受到影响,特别是在链比较长的时候,因此需控制链中最大节点数量,一般 通过在Handler中设置一个最大节点数量,在setNext()方法中判断是否已经超过阀值, 超过则不允许该链建立,避免出现超长链无意识地破坏系统性能。

3、调试不方便。采用了类似递归的方式,调试时逻辑可能比较复杂。

4、最佳应用场景:有多个对象可以处理同一个请求时,比如:多级请求、请假/加薪 等审批流程、Java Web中Tomcat对Encoding的处理、拦截器。

猜你喜欢

转载自blog.csdn.net/weixin_43334389/article/details/114113826