行为型模式

行为型模式,第1张

模板模式

简介:定义一个 *** 作中算法的框架,而将一些步骤延迟到子类中。模板方法模式使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

结构:

模板方法模式包含如下两个角色 :

AbstractClass(抽象类):在抽象类中定义了一系列基本 *** 作(PrimitiveOperations),这些基本 *** 作可以是具体的,也可以是抽象的,每一个基本 *** 作对应算法的一个步骤,在其子类中可以重定义或实现这些步骤。同时,在抽象类中实现了一个模板方法(Template Method),用于定义一个算法的框架,模板方法不仅可以调用在抽象类中实现的基本方法,也可以调用在抽象类的子类中实现的基本方法,还可以调用其他对象中的方法。
ConcreteClass(具体子类):它是抽象类的子类,用于实现在父类中声明的抽象基本 *** 作以完成子类特定算法的步骤,也可以覆盖在父类中已经实现的具体基本 *** 作。

案例

需求:统计某一代码运行时间

使用前:

​

package com.javaxl.design.template.before;
​
/**
* @author 小李飞刀
* @site www.javaxl.com
* @company
* @create  2020-02-24 15:42
*/
public class CodeTotalTime {
​
  public static void template(){
      long start = System.currentTimeMillis();
      //       检测Operation_1方法运行的时长======33
      Operation_1();
      //       检测Operation_2方法运行的时长======616
      //       Operation_2();
      long end = System.currentTimeMillis();
      System.out.println(end-start);
  }
​
  public static void Operation_1(){
​
      for (int i = 0; i<1000 ;i++){
          System.out.println("模拟耗时操作...");
      }
      System.out.print("检测Operation_1方法运行的时长======");
  }
​
  public static void Operation_2(){
​
      for (int i = 0; i<20000 ;i++){
          System.out.println("模拟耗时操作...");
      }
​
      System.out.print("检测Operation_2方法运行的时长======");
  }
​
}
​
​
public class Client {
  public static void main(String[] args) {
      CodeTotalTime.template();
  }
}

 使用后

package com.javaxl.design.template.after;
​
/**
* @author 小李飞刀
* @site www.javaxl.com
* @company
* @create  2020-02-24 15:51
* 

* 统计代码执行时长的抽象类 */ public abstract class CodeAbstractClass { ​ public void template() { long start = System.currentTimeMillis(); method(); long end = System.currentTimeMillis(); System.out.println("当前方法执行时长:"+(end - start)); } ​ public abstract void method(); ​ } ​ class ConcreteClassA extends CodeAbstractClass{ ​ @Override public void method() { for (int i = 0; i<1000 ;i++){ System.out.println("模拟耗时操作..."); } System.out.print("检测ConcreteClassA.method方法运行的时长======"); } } ​ class ConcreteClassB extends CodeAbstractClass{ ​ @Override public void method() { for (int i = 0; i<20000 ;i++){ System.out.println("模拟耗时操作..."); } System.out.print("ConcreteClassB.method方法运行的时长======"); } } ​ ​ public class Client { public static void main(String[] args) { //检测ConcreteClassA.method方法运行的时长======当前方法执行时长:40 // new ConcreteClassA().template(); //ConcreteClassB.method方法运行的时长======当前方法执行时长:272 new ConcreteClassB().template(); } }

钩子函数应用场景: 

public abstract class CodeAbstractClass {
​
  public void template() {
      long start = System.currentTimeMillis();
      if (callback()) method();
      long end = System.currentTimeMillis();
      System.out.println("当前方法执行时长:" + (end - start));
  }
​
  public abstract void method();
​
  public boolean callback() {
      return true;
  }
​
}

从上面可以看出:template方法默认是用作统计method方法的执行时长,但是有的时候我们无需统计代码时长,template函数中有一些其它逻辑要执行,在这里我们可以考虑采用钩子函数;钩子函数被子类覆写,覆写成false,那么method方法就不会被调用,不再统计代码时长了;前端框架Vue的生命周期就有多处用到钩子函数;

  • 注意事项和细节

    • 钩子函数 在模板方法模式的父类中,我们可以定义一个方法,它默认不做任何事,子类可以视情况要不要覆盖它,该方法称为“钩子”

    • 算法只存在于一个地方,也就是在父类中,容易修改。需要修改算法时,只要修改父类的模板方法或者已经实现的某些步骤,子类就会继承这些修改

    • 一般模板方法都加上 final 关键字, 防止子类重写模板方法

  • 应用

    Spring IOC容器加载

命令模式(Command Pattern)

概述

    命令模式(Command Pattern):将一个请求封装为一个对象,从而让我们可用不同的请求对客户进行参数化;对请求排队或者记录请求日志,以及支持可撤销的 *** 作。命令模式是一种对象行为型模式,其别名为动作(Action)模式或事务(Transaction)模式。

    术语

        Command:命令

        ConcreteCommand:具体的命令

        Invoker:调用者

        Receiver:接受者

模式结构

        Command(抽象命令类):抽象命令类一般是一个抽象类或接口,在其中声明了用于执行请求的execute()等方法,通过这些方法可以调用请求接收者的相关 *** 作。
        ConcreteCommand(具体命令类):具体命令类是抽象命令类的子类,实现了在抽象命令类中声明的方法,它对应具体的接收者对象,将接收者对象的动作绑定其中。在实现execute()方法时,将调用接收者对象的相关 *** 作(Action)。
        Invoker(调用者):调用者即请求发送者,它通过命令对象来执行请求。一个调用者并不需要在设计时确定其接收者,因此它只与抽象命令类之间存在关联关系。在程序运行时可以将一个具体命令对象注入其中,再调用具体命令对象的execute()方法,从而实现间接调用请求接收者的相关 *** 作。
        Receiver(接收者):接收者执行与请求相关的 *** 作,它具体实现对请求的业务处理。

案例

需求:万能遥控器的制作

    角色

        Command 抽象命令 执行命令 撤销命令

        ConcreteCommand LightOnCommand 开灯
        LightOffCommand 关灯
        NonCommand   空命令

        Invoker 调用者 遥控器聚合所有命令 Command[] ons Command[] offs Command undo

        Receiver 接受者 电灯、空调、电视

案例UML类图

使用前

   

 class AirConditioner {
            public void on() {
                System.out.println("空调打开...");
            }
     
            public void off() {
                System.out.println("空调关闭...");
            }
     
        }
     
        class Television {
            public void on() {
                System.out.println("电视打开...");
            }
     
            public void off() {
                System.out.println("电视关闭...");
            }
     
        }
     
     
     
     
    public class Invoker {
        private Light light = new Light();
        private AirConditioner airConditioner = new AirConditioner();
        private Television television = new Television();
     
        public void lightOn() {
            light.on();
        }
     
        public void lightOff() {
            light.off();
        }
     
        public void airOn() {
            airConditioner.on();
        }
     
        public void airOff() {
            airConditioner.off();
        }
     
        public void tvOn() {
            television.on();
        }
     
        public void tvOff() {
            television.off();
        }
    }
     
     
     
    public class Client {
        public static void main(String[] args) {
            Invoker invoker = new Invoker();
            invoker.lightOn();
            invoker.lightOff();
            System.out.println("=============");
            invoker.airOn();
            invoker.airOff();
            System.out.println("=============");
            invoker.tvOn();
            invoker.tvOff();
        }
    }

使用后
   

 public class Light {
        public void on() {
            System.out.println("电灯打开...");
        }
     
        public void off() {
            System.out.println("电灯关闭...");
        }
    }
     
    class AirConditioner {
        public void on() {
            System.out.println("空调打开...");
        }
     
        public void off() {
            System.out.println("空调关闭...");
        }
    }
     
    class Television {
        public void on() {
            System.out.println("电视打开...");
        }
     
        public void off() {
            System.out.println("电视关闭...");
        }
    }

    interface Command {
        void execute();
     
        void undo();
    }// 空命令
     
    class NonCommand implements Command {
        @Override
        public void execute() {
        }
     
        @Override
        public void undo() {
        }
    }
     
    class LightOnCommand implements Command {
        private Light light = new Light();
     
        @Override
        public void execute() {
            light.on();
        }
     
        @Override
        public void undo() {
            light.off();
        }
    }
     
    class LightOffCommand implements Command {
        private Light light = new Light();
     
        @Override
        public void execute() {
            light.off();
        }
     
        @Override
        public void undo() {
            light.on();
        }
    }
     
    class TvOnCommand implements Command {
        private Television tv = new Television();
     
        @Override
        public void execute() {
            tv.on();
        }
     
        @Override
        public void undo() {
            tv.off();
        }
    }
     
    class TvOffCommand implements Command {
        private Television tv = new Television();
     
        @Override
        public void execute() {
            tv.off();
        }
     
        @Override
        public void undo() {
            tv.on();
        }
    }
     
    public class Invoker {
        Command[] ons;
        Command[] offs;// 记录上一个命令
        Command command;
     
        public Invoker(int n) {
            ons = new Command[n];
            offs = new Command[n];
            command = new NonCommand();
            for (int i = 0; i < n; i++) {
                setCommand(i, new NonCommand(), new NonCommand());
            }
        }
     
        public void setCommand(int no, Command on, Command off) {
            ons[no] = on;
            offs[no] = off;
        }
     
        public Command getOnCommand(int no) {
            return ons[no];
        }
     
        public Command getOffCommand(int no) {
            return offs[no];
        }
     
        // 执行命令
        public void invoke(Command command) {
            // 执行当前命令
            command.execute();// 保存当前执行命令
            this.command = command;
        }// 撤销命令(上个操作的反操作)
     
        public void undo() {// 这里就能体现定义一个空命令的好处了,如果第一次按撤销命令,那么应该什么都不做;// 如果没有定义空命令的话,此时就需要判断空处理了
            command.undo();
        }
    }

    public class Client {
        public static void main(String[] args) {
            Invoker invoker = new Invoker(2);
            invoker.setCommand(0, new LightOnCommand(), new LightOffCommand());
            invoker.setCommand(1, new TvOnCommand(), new TvOffCommand());
            System.out.println("电灯打开关闭操作===========");
            invoker.invoke(invoker.getOnCommand(0));
            invoker.invoke(invoker.getOffCommand(0));
            // invoker.undo();
            System.out.println("电视打开关闭操作===========");
            invoker.invoke(invoker.getOnCommand(1));
            invoker.undo();
        }
    }

    注意事项和细节:

    将发起请求的对象与执行请求的对象解耦 容易实现对请求的撤销和重做 空命令也是一种设计模式,它为我们省去了判空的 *** 作
     

    命令模式不足: ​ 可能导致某些系统有过多的具体命令类,增加了系统的复杂度

    与外观模式相似:都是将多个功能聚合在一起 ​ 外观模式更多适用于维护;命令模式更多应用于设计

总结

    命令模式是一种使用频率非常高的设计模式,它可以将请求发送者与接收者解耦,请求发送者通过命令对象来间接引用请求接收者,使得系统具有更好的灵活性和可扩展性。
     

    主要优点

               (1) 降低系统的耦合度。由于请求者与接收者之间不存在直接引用,因此请求者与接收者之间实现完全解耦,相同的请求者可以对应不同的接收者,同样,相同的接收者也可以供不同的请求者使用,两者之间具有良好的独立性。
               (2) 新的命令可以很容易地加入到系统中。由于增加新的具体命令类不会影响到其他类,因此增加新的具体命令类很容易,无须修改原有系统源代码,甚至客户类代码,满足“开闭原则”的要求。
               (3) 可以比较容易地设计一个命令队列或宏命令(组合命令)。
               (4) 为请求的撤销(Undo)和恢复(Redo) *** 作提供了一种设计和实现方案。

    主要缺点

        使用命令模式可能会导致某些系统有过多的具体命令类。因为针对每一个对请求接收者的调用 *** 作都需要设计一个具体命令类,因此在某些系统中可能需要提供大量的具体命令类,这将影响命令模式的使用。

    适用场景

        系统需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互。请求调用者无须知道接收者的存在,也无须知道接收者是谁,接收者也无须关心何时被调用。
        系统需要在不同的时间指定请求、将请求排队和执行请求。一个命令对象和请求的初始调用者可以有不同的生命期,换言之,最初的请求发出者可能已经不在了,而命令对象本身仍然是活动的,可以通过该命令对象去调用请求接收者,而无须关心请求调用者的存在性,可以通过请求日志文件等机制来具体实现。
        系统需要支持命令的撤销(Undo) *** 作和恢复(Redo) *** 作。
        系统需要将一组 *** 作组合在一起形成宏命令。

备忘录模式

概述

    备忘录模式(Memento Pattern):在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态。它是一种对象行为型模式,其别名为Token。

     

    术语

        Memento:备忘录
        originator:发起者
        Caretaker:守护者

模式结构

    在备忘录模式结构图中包含如下几个角色

        Originator(原发器):它是一个普通类,可以创建一个备忘录,并存储它的当前内部状态,也可以使用备忘录来恢复其内部状态,一般将需要保存内部状态的类设计为原发器。
        Memento(备忘录):存储原发器的内部状态,根据原发器来决定保存哪些内部状态。备忘录的设计一般可以参考原发器的设计,根据实际需要确定备忘录类中的属性。需要注意的是,除了原发器本身与负责人类之外,备忘录对象不能直接供其他类使用,原发器的设计在不同的编程语言中实现机制会有所不同。
        Caretaker(负责人):负责人又称为管理者,它负责保存备忘录,但是不能对备忘录的内容进行 *** 作或检查。在负责人类中可以存储一个或多个备忘录对象,它只负责存储对象,而不能修改对象,也无须知道对象的实现细节

案例

需求:游戏人物大战后状态恢复

情况一:为一个对象保留一个状态

    public class Hero {
        //    需要存档的属性:这里用一个state属性来表示,实际需要存档的属性可能会有很多
        private String state;
     
        public Hero(String state) {
            this.state = state;
        }
     
        public String getState() {
            return state;
        }
     
        public void setState(String state) {
            this.state = state;
        }    //    将当前Hero对象实例进行备份
     
        public HeroMemento saveHero() {
            return new HeroMemento(this.state);
        }
     
        //    恢复上一个英雄状态
        public void getMemento(HeroMemento heroMemento) {
            this.state = heroMemento.getState();
        }
    }
     
     
     
    public class Caretaker {
        private HeroMemento heroMemento;
     
        public HeroMemento getHeroMemento() {
            return heroMemento;
        }
     
        public void setHeroMemento(HeroMemento heroMemento) {
            this.heroMemento = heroMemento;
        }
    }
     
     
     
    public class HeroMemento {
        private String state;
     
        public HeroMemento(String state) {
            this.state = state;
        }
     
        public String getState() {
            return state;
        }
     
        public void setState(String state) {
            this.state = state;
        }
    }
     
     
     
    public class Client {
        public static void main(String[] args) {
            Hero hero = new Hero("状态1,满血状态");
            Caretaker caretaker = new Caretaker();
            caretaker.setHeroMemento(hero.saveHero());
            hero.setState("状态2:状态下滑");
            System.out.println("当前的状态===============" + hero.getState());
            hero.getMemento(caretaker.getHeroMemento());
            System.out.println("当前的状态===============" + hero.getState());
            caretaker.setHeroMemento(hero.saveHero());
            hero.setState("状态3:残血状态");
            hero.getMemento(caretaker.getHeroMemento());
            System.out.println("当前的状态===============" + hero.getState());
            caretaker.setHeroMemento(hero.saveHero());
            hero.setState("状态4:临死状态");
            caretaker.setHeroMemento(hero.saveHero());
        }
    }

情况二:为一个对象保留多个状态

   

 public class Hero {
        //    需要存档的属性:这里用一个state属性来表示,实际需要存档的属性可能会有很多
        private String state;
     
        public Hero(String state) {
            this.state = state;
        }
     
        public String getState() {
            return state;
        }
     
        public void setState(String state) {
            this.state = state;
        }    //    将当前Hero对象实例进行备份
     
        public HeroMemento saveHero() {
            return new HeroMemento(this.state);
        }    //    恢复某一个英雄状态
     
        public void getMemento(Caretaker caretaker, int no) {
            this.state = caretaker.getMemento(no).getState();
        }
    }
     
     
     
    public class HeroMemento {
        private String state;
     
        public HeroMemento(String state) {
            this.state = state;
        }
     
        public String getState() {
            return state;
        }
     
        public void setState(String state) {
            this.state = state;
        }
    }
     
     
     
    public class Caretaker {
        private List heroMementos = new ArrayList<>();
     
        public void addMemento(HeroMemento memento) {
            heroMementos.add(memento);
        }
     
        public HeroMemento getMemento(int no) {
            return heroMementos.get(no);
        }
    }
     
     
     
    public class Client {
        public static void main(String[] args) {
            Hero hero = new Hero("状态1,满血状态");
            Caretaker caretaker = new Caretaker();
            caretaker.addMemento(hero.saveHero());
            hero.setState("状态2:状态下滑");
            hero.setState("状态3:残血状态");
            caretaker.addMemento(hero.saveHero());
            hero.setState("状态4:临死状态");
            caretaker.addMemento(hero.saveHero());
            hero.setState("状态5:死亡状态");
            //        上面备份了1、3、4状态,我来恢复看看         
            System.out.println("当前的状态===============" + hero.getState());
            hero.getMemento(caretaker, 0);
            System.out.println("回复到状态1===============" + hero.getState());
            hero.getMemento(caretaker, 1);
            System.out.println("回复到状态3===============" + hero.getState());
            hero.getMemento(caretaker, 2);
            System.out.println("回复到状态4===============" + hero.getState());
        }
    }

情况三:为多个对象保留一个状态

   

 public class Hero {
        //    需要存档的属性:这里用一个state属性来表示,实际需要存档的属性可能会有很多
        private String state;
     
        public Hero(String state) {
            this.state = state;
        }
     
        public String getState() {
            return state;
        }
     
        public void setState(String state) {
            this.state = state;
        }    //    将当前Hero对象实例进行备份
     
        public HeroMemento saveHero() {
            return new HeroMemento(this.state);
        }    //    恢复某一个英雄状态
     
      
    }
     
     
    public class Caretaker {
        private HashMap mementos = new HashMap();
     
        public void addMemento(Caretaker caretaker,HeroMemento memento) {
            mementos.put(caretaker,memento);
        }
     
        public HashMap getMemento() {
            return this.mementos;
        }
     
    }
     
     
    public class HeroMemento {
        private String state;
     
        public HeroMemento(String state) {
            this.state = state;
        }
     
        public String getState() {
            return state;
        }
     
        public void setState(String state) {
            this.state = state;
        }
     
    }
     
     
    public class Client {
        public static void main(String[] args) {
            Hero hero = new Hero("状态1,满血状态");
            Caretaker caretaker = new Caretaker();
            Caretaker caretaker2 = new Caretaker();
     
            HeroMemento heroMemento = new HeroMemento(hero.getState());
     
            caretaker.addMemento(caretaker,heroMemento);
            caretaker.addMemento(caretaker2,heroMemento);
     
     
            HashMap memento = caretaker.getMemento();
            Set> entrySet = memento.entrySet();
            for (Map.Entry entry : entrySet) {
                String key = entry.getKey().toString();
                String value = entry.getValue().toString();
                System.out.println("key=" + key + " value=" + value);
            }
     
    }

情况四:为多个对象保留多个对象

   public class Hero {
        //    需要存档的属性:这里用一个state属性来表示,实际需要存档的属性可能会有很多
        private String state;
     
        public Hero(String state) {
            this.state = state;
        }
     
        public String getState() {
            return state;
        }
     
        public void setState(String state) {
            this.state = state;
        }    //    将当前Hero对象实例进行备份
     
        public HeroMemento saveHero() {
            return new HeroMemento(this.state);
        }    //    恢复某一个英雄状态
     
    }
     
     
    public class HeroMemento {
        private String state;
     
        public HeroMemento(String state) {
            this.state = state;
        }
     
        public String getState() {
            return state;
        }
     
        public void setState(String state) {
            this.state = state;
        }
     
    }
     
     
    public class Caretaker {
        private HashMap> mementos  = new HashMap();
        public void addMemento(Caretaker caretaker,List memento) {
            mementos.put(caretaker,memento);
        }
     
        public HashMap> getMemento() {
            return this.mementos;
        }
     
    }
     
    public class Client {
        public static void main(String[] args) {
            Hero hero1 = new Hero("状态1,满血状态");
            Hero hero2 = new Hero("状态2,满血状态");
            Hero hero3 = new Hero("状态3,满血状态");
            Hero hero4 = new Hero("状态4,满血状态");
     
            Caretaker caretaker = new Caretaker();
            Caretaker caretaker2 = new Caretaker();
     
            HeroMemento heroMemento1 = new HeroMemento(hero1.getState());
            HeroMemento heroMemento2 = new HeroMemento(hero2.getState());
            HeroMemento heroMemento3 = new HeroMemento(hero3.getState());
            HeroMemento heroMemento4 = new HeroMemento(hero4.getState());
     
            List lsit1=new ArrayList<>();
            lsit1.add(heroMemento1);
            lsit1.add(heroMemento2);
     
            List lsit2=new ArrayList<>();
            lsit2.add(heroMemento3);
            lsit2.add(heroMemento4);
     
            caretaker.addMemento(caretaker,lsit1);
            caretaker.addMemento(caretaker2,lsit2);
     
     
            HashMap> memento = caretaker.getMemento();
     
     
            Set>> entries = memento.entrySet();
     
            for (Map.Entry> entry : entries) {
                String key = entry.getKey().toString();
                String value = entry.getValue().toString();
                System.out.println("key=" + key + " value=" + value);
            }
    }

    注意事项和细节:

        给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态

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

        如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存

总结

            备忘录模式在很多软件的使用过程中普遍存在,但是在应用软件开发中,它的使用频率并不太高,因为现在很多基于窗体和浏览器的应用软件并没有提供撤销 *** 作。如果需要为软件提供撤销功能,备忘录模式无疑是一种很好的解决方案。在一些字处理软件、图像编辑软件、数据库管理系统等软件中备忘录模式都得到了很好的应用。

    主要优点

              它提供了一种状态恢复的实现机制,使得用户可以方便地回到一个特定的历史步骤,当新的状态无效或者存在问题时,可以使用暂时存储起来的备忘录将状态复原。
             备忘录实现了对信息的封装,一个备忘录对象是一种原发器对象状态的表示,不会被其他代码所改动。备忘录保存了原发器的状态,采用列表、堆栈等集合来存储备忘录对象可以实现多次撤销 *** 作。

    主要缺点

        资源消耗过大,如果需要保存的原发器类的成员变量太多,就不可避免需要占用大量的存储空间,每保存一次对象的状态都需要消耗一定的系统资源。

    适用场景

        保存一个对象在某一个时刻的全部状态或部分状态,这样以后需要时它能够恢复到先前的状态,实现撤销 *** 作。
        防止外界对象破坏一个对象历史状态的封装性,避免将对象历史状态的实现细节暴露给外界对象。

状态模式(State)

概念:

        当控制一个对象状态的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同状态的一系列类中,可以把复杂的判断逻辑简化

角色:

Context:环境角色:用于维护 State 实例,这个实例定义当前状态 State:抽象状态角色:聚合到Context环境角色中 ConcreteState :具体的状态角色 ConcreteStateA ConcreteStateB ConcreteStateC

UML类图

案例 :抽奖活动项目设计

 使用状态模式之前:

package com.javaxl.design.state.before;

import java.util.Random;

/**
 * @author 小李飞刀
 * @site www.javaxl.com
 * @company
 * @create  2020-02-25 12:57
 */
public class State {
    //    当前的状态
    private int state;
    //    供抽奖的积分
    private int score;
    //    奖品的数量
    private int count;

    public State(int score, int count) {
        this.score = score;
        this.count = count;
    }

    public int getCount() {
        return count;
    }

    public int getState() {
        return state;
    }

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

    public int getScore() {
        return score;
    }

    public void setScore(int score) {
        this.score = score;
    }

    //    扣除积分
    public void minus() {
//        只有一阶段可以扣积分
        this.state = 1;
        if (this.state == 1) {
            if (this.score >= 50) {
                if (this.count == 0) {
                    System.out.println("奖品领完....");
                    return;
                }
                this.score = this.score - 50;
                System.out.println("========扣除50积分,当前积分还剩" + this.score + "========");
                this.state = 2;
                if (luckHit()) {
                    this.state = 3;
                    getPrize();
                }
            } else {
                System.out.println("========积分不够,当前积分为" + this.score + "========");
            }
        }

    }

    //    十分之一抽中奖品的概率
    public boolean luckHit() {
//        只有二阶段可以抽奖
        return this.state == 2 ? (new Random().nextInt(10) == 6) : false;
    }

    public void getPrize() {
        if (this.state == 3) {
            if (this.count > 0) {
                System.out.println("领取奖品....");
                this.count = this.count - 1;
            } else {
                System.out.println("奖品领完....");
            }
        }
    }

}


public class Client {
    public static void main(String[] args) {
        State state = new State(500,1);
//        state.minus();
        for (int i = 0; i < 300; i++) {
            state.minus();
        }
    }
}

从上面的编码中,我们可以看出,完成该需求有很多的条件判断,非常不利于后续的维护;上面状态只有4个,代码已经比较复杂了;状态越多,代码嵌套就越复杂,维护成本就越高;

当我们使用状态模式之后:

package com.javaxl.design.state.after;

import java.util.Random;

/**
 * @author 小李飞刀
 * @site www.javaxl.com
 * @company
 * @create  2020-02-25 15:59
 */
public abstract class State {

//    扣积分
    abstract void minus();
//    抽奖
    abstract boolean luckHit();
//    获取奖品
    abstract void getPrize();

}

class ConcreteStateA extends State{
    Context context;

    public ConcreteStateA(Context context) {
        this.context = context;
    }

    @Override
    void minus() {
        if(context.getScore()>=50){
            context.setScore(context.getScore()-50);
            System.out.println("========扣除50积分,当前积分还剩"+context.getScore()+"========");
            context.setState(context.getStateB());
        }else{
            System.out.println("========积分不够,当前积分为"+context.getScore()+"========");
        }
    }

    @Override
    boolean luckHit() {
        System.out.println("还在扣费环节,不能抽奖...");
        return false;
    }

    @Override
    void getPrize() {
        System.out.println("还在扣费环节,不能领取奖品...");
    }
}

class ConcreteStateB extends State{
    Context context;

    public ConcreteStateB(Context context) {
        this.context = context;
    }

    @Override
    void minus() {
        System.out.println("已经在抽奖环节...");
    }

    @Override
    boolean luckHit() {
        boolean flag = new Random().nextInt(10) == 6;
        if(flag){
            context.setState(context.getStateC());
        }else{
            context.setState(context.getStateA());
        }
        return flag;
    }

    @Override
    void getPrize() {
        System.out.println("还在抽奖环节,不能领取奖品...");
    }
}

class ConcreteStateC extends State{
    Context context;

    public ConcreteStateC(Context context) {
        this.context = context;
    }

    @Override
    void minus() {
        System.out.println("已经在领取奖品环节...");
    }

    @Override
    boolean luckHit() {
        System.out.println("已经在领取奖品环节...");
        return false;
    }

    @Override
    void getPrize() {
        if(context.getCount()>0){
            System.out.println("领取奖品成功...");
            context.setState(context.getStateA());
        }else {
            System.out.println("活动结束,领取奖品失败...");
            context.setState(context.getStateD());
//            不继续抽奖
//            System.exit(0);
        }
    }
}

class ConcreteStateD extends State{
    Context context;

    public ConcreteStateD(Context context) {
        this.context = context;
    }

    @Override
    void minus() {
        System.out.println("已经在活动结束,奖品送完环节...");
    }

    @Override
    boolean luckHit() {
        System.out.println("已经在活动结束,奖品送完环节...");
        return false;
    }

    @Override
    void getPrize() {
        System.out.println("已经在活动结束,奖品送完环节...");
    }
}


public class Context {
    //    当前的状态
    private State state;
    //    奖品数量
    public int count;
    //    用户积分
    private int score;

    //    表示同一个对象的四种状态
    private ConcreteStateA stateA = new ConcreteStateA(this);
    private ConcreteStateB stateB = new ConcreteStateB(this);
    private ConcreteStateC stateC = new ConcreteStateC(this);
    private ConcreteStateD stateD = new ConcreteStateD(this);


    public Context(int score, int count) {
        this.score = score;
        this.count = count;
        this.state = stateA;
    }

    //    扣积分
    public void minus() {
        state.minus();
    }

    //    抽奖
    public void luckHit() {
        if (state.luckHit()) {
            state.getPrize();
        }
    }

    public State getState() {
        return state;
    }

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

    public int getCount() {
        return count--;
    }

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

    public ConcreteStateA getStateA() {
        return stateA;
    }

    public void setStateA(ConcreteStateA stateA) {
        this.stateA = stateA;
    }

    public ConcreteStateB getStateB() {
        return stateB;
    }

    public void setStateB(ConcreteStateB stateB) {
        this.stateB = stateB;
    }

    public ConcreteStateC getStateC() {
        return stateC;
    }

    public void setStateC(ConcreteStateC stateC) {
        this.stateC = stateC;
    }

    public ConcreteStateD getStateD() {
        return stateD;
    }

    public void setStateD(ConcreteStateD stateD) {
        this.stateD = stateD;
    }

    public int getScore() {
        return score;
    }

    public void setScore(int score) {
        this.score = score;
    }
}


public class Client {
    public static void main(String[] args) {
//        这次游戏积分500个,用完为止,总奖品数2
        Context context = new Context(500,1);
//        context.lunkHit();//还在扣费环节,不能抽奖...

        for (int i = 0; i < 300; i++) {
            context.minus();
            context.luckHit();
        }
        System.out.println("------------------");

    }
}

一次没抽中 :

抽中一次:

抽中两次:

 

  • 注意事项和细节

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

    • 方便维护。将容易产生问题的 if-else 语句删除了

    • 符合“开闭原则”。容易增删状态

缺点:会产生很多类。每个状态都要一个对应的类,当状态过多时会产生很多类,加大维护难度

应用场景:当一个事件或者对象有很多种状态,状态之间会相互转换,对不同的状态要求有不同的行为的时候, 可以考虑使用状态模式

应用

借贷平台状态管理

职责链模式(Chain of Responsibility)

角色

Handler 抽象的处理者, 定义了一个处理请求的接口 ConcreteHandlerA , B 具体的处理者, 处理它自己负责的请求, 可以访问它的后继者(即下一个处理者) Request 含义很多属性,表示一个请求

案例

需求:OA系统请假审批案例

学生请假1天:教员审批

学生请假2天:教学主管审批

学生请假3天:教学经理审批

学生请假5天:副校长审批

学生请假超过5天:校长审批

使用前

package com.javaxl.design.chain.before;

/**
 * @author 小李飞刀
 * @site www.javaxl.com
 * @company
 * @create  2020-02-25 19:30
 */
public class Request {
    private String content;
    private int day;

    public Request(String content, int day) {
        this.content = content;
        this.day = day;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public int getDay() {
        return day;
    }

    public void setDay(int day) {
        this.day = day;
    }
}


package com.javaxl.design.chain.before;

/**
 * @author 小李飞刀
 * @site www.javaxl.com
 * @company
 * @create  2020-02-25 19:31
 */
public class Handler {

    public void handle(Request request){
        int day = request.getDay();
        if(day <= 1){
            System.out.println("教员处理了:因 " + request.getContent() + " 请假" + day + "天的请求");
        }else if(day <= 2){
            System.out.println("教学主管处理了:因 " + request.getContent() + " 请假" + day + "天的请求");
        }else if(day <= 3){
            System.out.println("教学经理处理了:因 " + request.getContent() + " 请假" + day + "天的请求");
        }else if(day <= 5){
            System.out.println("副校长处理了:因 " + request.getContent() + " 请假" + day + "天的请求");
        }else {
            System.out.println("校长处理了:因 " + request.getContent() + " 请假" + day + "天的请求");
        }
    }
}


public class Client {
    public static void main(String[] args) {
        Handler handler = new Handler();
        Request request1 = new Request("小感冒",1);
        handler.handle(request1);

        Request request2 = new Request("做检查",2);
        handler.handle(request2);

        Request request3 = new Request("打点滴",3);
        handler.handle(request3);


        Request request4 = new Request("住院",4);
        handler.handle(request4);

        Request request5 = new Request("在家调养",30);
        handler.handle(request5);
    }
}

 

违背了迪米特法则,调用方清楚的知道整个处理链的存在;

使用后:

public class Request {
    private String content;
    private int day;

    public Request(String content, int day) {
        this.content = content;
        this.day = day;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public int getDay() {
        return day;
    }

    public void setDay(int day) {
        this.day = day;
    }
}

public abstract class Handler {
    Handler next;
    String name;

    public Handler(String name) {
        this.name = name;
    }

    public Handler getNext() {
        return next;
    }

    public void setNext(Handler next) {
        this.next = next;
    }

    public abstract void handle(Request request);
}

class HandlerA extends Handler {
    public HandlerA(String name) {
        super(name);
    }

    public void handle(Request request) {
        int day = request.getDay();
        if (day <= 1) {
            System.out.println(this.name + "处理了:因 " + request.getContent() + " 请假" + day + "天的请求");
        } else {
            next.handle(request);
        }
    }
}

class HandlerB extends Handler {
    public HandlerB(String name) {
        super(name);
    }

    public void handle(Request request) {
        int day = request.getDay();
        if (day <= 2) {
            System.out.println(this.name + "处理了:因 " + request.getContent() + " 请假" + day + "天的请求");
        } else {
            next.handle(request);
        }
    }
}

class HandlerC extends Handler {
    public HandlerC(String name) {
        super(name);
    }

    public void handle(Request request) {
        int day = request.getDay();
        if (day <= 3) {
            System.out.println(this.name + "处理了:因 " + request.getContent() + " 请假" + day + "天的请求");
        } else {
            next.handle(request);
        }
    }
}

class HandlerD extends Handler {
    public HandlerD(String name) {
        super(name);
    }

    public void handle(Request request) {
        int day = request.getDay();
        if (day <= 5) {
            System.out.println(this.name + "处理了:因 " + request.getContent() + " 请假" + day + "天的请求");
        } else {
            next.handle(request);
        }
    }
}

class HandlerE extends Handler {
    public HandlerE(String name) {
        super(name);
    }

    public void handle(Request request) {
        int day = request.getDay();
        System.out.println(this.name + "处理了:因 " + request.getContent() + " 请假" + day + "天的请求");
    }
}


public class Client {
    public static void main(String[] args) {
        HandlerA handlerA = new HandlerA("教员");
        HandlerB handlerB = new HandlerB("教学主管");
        HandlerC handlerC = new HandlerC("教学经理");
        HandlerD handlerD = new HandlerD("副校长");
        HandlerE handlerE = new HandlerE("校长");
        handlerA.setNext(handlerB);
        handlerB.setNext(handlerC);
        handlerC.setNext(handlerD);
        handlerD.setNext(handlerE);

        Request request1 = new Request("小感冒",1);
        handlerA.handle(request1);

        Request request2 = new Request("做检查",2);
        handlerA.handle(request2);

        Request request3 = new Request("打点滴",3);
        handlerA.handle(request3);


        Request request4 = new Request("住院",4);
        handlerA.handle(request4);

        Request request5 = new Request("在家调养",30);
        handlerA.handle(request5);
    }
}
  • 注意事项和细节

    将请求和处理分开,实现解耦,提高系统的灵活性 简化了对象,使对象不需要知道链的结构

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

  • 应用

    springmvc框架~HandlerExcutionChain类 工作流框架绘图生成xml反射实例化,整个流程归档过程 js中的原型链

观察者模式(Observer)
  • 角色

  • Observer (观察者) 百度 新浪 谷歌 (Subject) 气象局( WeatherData)

 UML类图

 

案例

需求:气象站数据更新推送问题

使用前:

package com.javaxl.design.observer.before;

/**
 * @author 小李飞刀
 * @site www.javaxl.com
 * @company
 * @create  2020-02-26 18:08
 */
public class WeatherData {
    double temperature;
    double humidity;

    public WeatherData(double temperature, double humidity) {
        this.temperature = temperature;
        this.humidity = humidity;
    }

    public void getWeatherInfo() {
        System.out.println("当前温度:" + temperature + ",当前湿度:" + humidity);
    }

    public void change(double temperature, double humidity) {
        this.temperature = temperature;
        this.humidity = humidity;
    }
}



public class Baidu {
    private WeatherData weatherData;

    public Baidu(WeatherData weatherData) {
        this.weatherData = weatherData;
    }

    public void getWeatherInfo() {
        System.out.print("百度网站温馨提示===>");
        weatherData.getWeatherInfo();
    }
}

class Sina {
    private WeatherData weatherData;

    public Sina(WeatherData weatherData) {
        this.weatherData = weatherData;
    }

    public void getWeatherInfo() {
        System.out.print("新浪网站温馨提示===>");
        weatherData.getWeatherInfo();
    }
}


public class Client {
    public static void main(String[] args) {
        WeatherData weatherData = new WeatherData(30,20);
        Baidu baidu = new Baidu(weatherData);
        Sina sina = new Sina(weatherData);
        baidu.getWeatherInfo();
        sina.getWeatherInfo();
        weatherData.change(10,10);
        baidu.getWeatherInfo();
        sina.getWeatherInfo();
    }
}

由第三方(百度、新浪)主动获取最新天气信息,这种方案需要每个第三方主动定时获取最新天气数据,涉及多个第三方;

使用后:
 

package com.javaxl.design.observer.after;

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

/**
 * @author 小李飞刀
 * @site www.javaxl.com
 * @company
 * @create  2020-02-26 19:14
 */
public interface Subject {
    void addObserver(Observer observer);
    void removeObserver(Observer observer);
    void notifyObservers();
}

class WeatherData implements Subject{
    double temperature;
    double humidity;
    List Observers = new ArrayList<>();

    public WeatherData(double temperature, double humidity) {
        this.temperature = temperature;
        this.humidity = humidity;
    }

    public void update(double temperature, double humidity) {
        this.temperature = temperature;
        this.humidity = humidity;
//        气象局数据一改变,马上通知接入的第三方/观察者
        notifyObservers();
    }

    @Override
    public void addObserver(Observer observer) {
        Observers.add(observer);
        observer.update(this.temperature,this.humidity);
    }

    @Override
    public void removeObserver(Observer observer) {
        Observers.remove(observer);
    }

    @Override
    public void notifyObservers() {
        for (Observer observer : Observers) {
            observer.update(this.temperature,this.humidity);
        }
    }
}


public interface Observer {
    void display();
    void update(double temperature, double humidity);
}

class Baidu implements Observer{
    double temperature;
    double humidity;

    @Override
    public void display() {
        System.out.println("百度温馨提示:当前温度:" + temperature + ",当前湿度:" + humidity);
    }

    @Override
    public void update(double temperature, double humidity) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.display();
    }
}

class Sina implements Observer{
    double temperature;
    double humidity;

    @Override
    public void display() {
        System.out.println("新浪温馨提示:当前温度:" + temperature + ",当前湿度:" + humidity);
    }

    @Override
    public void update(double temperature, double humidity) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.display();
    }
}

public class Client {
    public static void main(String[] args) {
        WeatherData weatherData = new WeatherData(30, 20);
        Baidu baidu = new Baidu();
        Sina sina = new Sina();
        weatherData.addObserver(baidu);
        weatherData.addObserver(sina);
        weatherData.update(10, 10);
        weatherData.removeObserver(baidu);
        weatherData.update(12, 12);
    }
}

由气象局主动通知第三方,天气数据发生了改变;并且,第三方的接入可以控制(增加、删除、通知);

  • 注意事项和细节

    集合的方式来管理用户(Observer),包括注册,移除和通知

  • 应用

    JDK源码中Observable类

策略模式(Strategy)

概念:

其用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化

  图例:

角色

Context 环境

Strategy 策略接口 ConcreteStrategyA ConcreteStrategyB

Strategy2 策略接口 ConcreteStrategyC ConcreteStrategyD

 案例:

需求:学院共有专业需求

使用前:

package com.javaxl.design.strategy.before;

/**
 * @author 小李飞刀
 * @site www.javaxl.com
 * @company
 * @create  2020-02-26 20:51
 */
public class Major {
    private String name;

    public Major(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public boolean equals(Object obj) {
        Major major = (Major) obj;
        return this.name.equals(major.name);
    }
}


public class College {
    String name;

    public College(String name) {
        this.name = name;
    }
}

class CollegeA extends College{
    List list = new ArrayList<>();
    CollegeA(String name){
        super(name);
        this.list.add(new Major("JAVA"));
        this.list.add(new Major("PHP"));
        this.list.add(new Major("JavaScript"));
        this.list.add(new Major("C语言"));
        this.list.add(new Major("android"));
    }
}

class CollegeB extends College{
    List list = new ArrayList<>();
    CollegeB(String name){
        super(name);
        this.list.add(new Major("iOS"));
        this.list.add(new Major("PHP"));
        this.list.add(new Major("JavaScript"));
        this.list.add(new Major("C语言"));
        this.list.add(new Major("嵌入式"));
    }
}


public class StrategyA {
    public List intersect(List a,List b){
        List list = new ArrayList();
        for (Major major : a) {
            if(b.contains(major)){
                list.add(major);
            }
        }
        return list;
    }
}

class StrategyB {
    public List intersect(List a,List b){
//        a.retainAll(b);
        b.retainAll(a);
        return b;
    }
}

public class Client {
    public static void main(String[] args) {
        StrategyA strategyA = new StrategyA();
        CollegeA a = new CollegeA("华东交通大学");
        CollegeB b = new CollegeB("东华理工大学");
        List intersect = strategyA.intersect(a.list, b.list);
        System.out.println(a.name + "与" + b.name + "都有的专业");
        for (Major major : intersect) {
            System.out.println(major.getName());
        }

        StrategyB strategyB = new StrategyB();
        List intersect2 = strategyB.intersect(a.list, b.list);
        System.out.println(a.name + "与" + b.name + "都有的专业");
        for (Major major : intersect2) {
            System.out.println(major.getName());
        }
    }
}

使用后:

package com.javaxl.design.strategy.after;

/**
 * @author 小李飞刀
 * @site www.javaxl.com
 * @company
 * @create  2020-02-26 20:51
 */
public class Major {
    private String name;

    public Major(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public boolean equals(Object obj) {
        Major major = (Major) obj;
        return this.name.equals(major.name);
    }
}


public class College {
    String name;

    public College(String name) {
        this.name = name;
    }
}

class CollegeA extends College {
    List list = new ArrayList<>();
    CollegeA(String name){
        super(name);
        this.list.add(new Major("JAVA"));
        this.list.add(new Major("PHP"));
        this.list.add(new Major("JavaScript"));
        this.list.add(new Major("C语言"));
        this.list.add(new Major("android"));
    }
}

class CollegeB extends College {
    List list = new ArrayList<>();
    CollegeB(String name){
        super(name);
        this.list.add(new Major("iOS"));
        this.list.add(new Major("PHP"));
        this.list.add(new Major("JavaScript"));
        this.list.add(new Major("C语言"));
        this.list.add(new Major("嵌入式"));
    }
}

public interface Strategy {
    List intersect(List a, List b);
}

public class StrategyA implements Strategy{
    public List intersect(List a,List b){
        List list = new ArrayList();
        for (Major major : a) {
            if(b.contains(major)){
                list.add(major);
            }
        }
        return list;
    }
}

public class Context {
    public List intersect(List a, List b,Strategy strategy){
        return strategy.intersect(a,b);
    }
}

public class Client {
    public static void main(String[] args) {
        CollegeA a = new CollegeA("华东交通大学");
        CollegeB b = new CollegeB("东华理工大学");
        Context context = new Context();

        List intersect = context.intersect(a.list, b.list, new StrategyA());
        System.out.println(a.name + "与" + b.name + "都有的专业");
        for (Major major : intersect) {
            System.out.println(major.getName());
        }

//        可以随意定制策略
        List intersect2 = context.intersect(a.list, b.list, new Strategy() {
            @Override
            public List intersect(List a, List b) {
                a.retainAll(b);
                return a;
            }
        });
        System.out.println(a.name + "与" + b.name + "都有的专业========");
        for (Major major : intersect2) {
            System.out.println(major.getName());
        }
    }
}
  • 注意事项和细节

    • 分析项目中变化部分与不变部分

    • 体现了“对修改关闭,对扩展开放”原则,客户端增加行为不用修改原有代码,只要添加一种策略(或者行为) 即可

    • 策略模式将算法封装在独立的 Strategy 类中使得你可以独立于其 Context 改变它,使它易于切换、易于理解、易于扩展

注意:在很多场景中,策略接口会作为内部接口体现

应用

  • Arrays工具类的排序方法Comparator策略接口的使用

  • JDBC对Result结果集的处理

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/726900.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-26
下一篇 2022-04-26

发表评论

登录后才能评论

评论列表(0条)

保存