一、背景

  1. 概念
    • 设计模式 Design Pattern 是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结,它描述了在软件设计过程中的一些不断重复发生的问题,以及该问题的解决方案;
  2. 本质
    • 这 23 种设计模式的本质是面向对象设计原则的实际运用,是对类的封装性、继承性和多态性,以及类的关联关系和组合关系的充分理解;
  3. 意义
    • 使用设计模式是为了可重用代码、让代码更容易被他人理解并且保证代码可靠性;
  4. 优势

    • 可以提高程序员的思维能力、编程能力和设计能力;
    • 使程序设计更加标准化、代码编制更加工程化,使软件开发效率大大提高,从而缩短软件的开发周期;
    • 使设计的代码可重用性高、可读性强、可靠性高、灵活性好、可维护性强;

      二、七大原则

  5. 开闭原则:一个软件实体应当对扩展开放,对修改关闭,即软件实体应尽量在不修改原有代码的情况下进行扩展,抽象化是开闭原则的关键;

  6. 里氏替换原则:所有引用基类对象的地方都能够透明地使用其子类的对象;
    • 里氏代换原则是实现开闭原则的重要方式之一,在程序中尽量使用基类类型来对对象进行定义,而在运行时再确定其子类类型,用子类对象来替换父类对象;
  7. 依赖倒置原则:抽象不应依赖具体类,具体类应依赖于抽象,即针对接口编程;
    • 程序中尽量使用抽象层进行编程,而将具体类写在配置文件中,且一个具体类应当只实现接口或抽象类中声明过的方法;
  8. 单一职责原则:一个类只负责一个功能领域中的相应职责,类职责越多,复用的可能性就越小;
  9. 迪米特法则(最少知道原则):一个软件实体应尽可能少地与其他实体发生相互作用,即降低类与类之间的耦合度;
  10. 接口分离原则:使用多个专门的接口,而不使用单一的总接口,即客户端不应依赖那些它不需要的接口;
  11. 合成复用原则:软件复用时,要尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现;

    三、设计模式

    根据模式是用来完成什么工作来划分,分为创建型模式、结构型模式和行为型模式 3 种。

    1. 创建型模式

    用于描述“怎样创建对象”,它的主要特点是“将对象的创建与使用分离”。GoF 中提供了单例、原型、工厂方法、抽象工厂、建造者等 5 种创建型模式。

    1.1 单例(Singleton)模式

  • 理论
    • 概念:某个类只能生成一个实例,且由该类自行创建,该类将提供一个全局访问点供外部获取该实例,其拓展是有限多例模式;
    • 需求:在有些系统中,为了节省内存资源、保证数据内容的一致性,对某些类要求只能创建一个实例;
    • 使用场景
      • 某类只要求生成一个对象,如每个人的身份证号码;
      • 对象需要被共享的场合,可节省内存,并加快对象访问速度;
      • 某类需要频繁实例化,且创建的对象又频繁被销毁,如多线程的线程池、网络连接池等;
      • 实际应用: Windows 的回收站、操作系统中的文件系统、多线程中的线程池、显卡的驱动程序对象、打印机的后台处理服务、应用程序的日志对象、数据库的连接池、网站的计数器、Web 应用的配置对象、应用程序中的对话框、系统中的缓存等;
  • 实现

    • 懒汉模式:必须调用 getInstance 才能创建实例;

      1. //线程安全写法,注意 static、volatile、synchronized 等关键字;
      2. public class LazySingleton{
      3. private static volatile LazySingleton instance=null;
      4. private LazySingleton(){}
      5. public static synchronized LazySingleton getInstance()
      6. {
      7. if(instance==null) instance = new LazySingleton();
      8. return instance;
      9. }}
    • 饿汉模式:类一加载就会生成实例,在调用 getInstance 之前就已生成实例;

      1. //线程安全,可直接用于多线程;
      2. public class HungrySingleton{
      3. private static final HungrySingleton instance = new HungrySingleton();
      4. private HungrySingleton(){}
      5. public static HungrySingleton getInstance()
      6. {
      7. return instance;
      8. }}

      1.2 原型(Prototype)模式

  • 理论

    • 概念:用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象;
    • 需求:在有些系统中存在大量相同或相似对象的创建问题,采用构造函数创建对象比较复杂且耗时耗资源;
    • 使用场景
      • 对象之间相同或相似,即只存在个别属性不同;
      • 对象的创建过程比较麻烦,但复制比较简单;
  • 实现

    • 浅克隆:只适合 copy 基本类型,对引用类型仅 copy 地址,若修改克隆地址则会修改原地址所指向对象;

      1. // 实现 Cloneable 接口
      2. public static void shadowClone(){
      3. class Shadow extends CloneClass implements Cloneable{
      4. @Override
      5. public Shadow clone(){
      6. Shadow s = null;
      7. try {
      8. s = (Shadow) super.clone();
      9. } catch (CloneNotSupportedException e) {
      10. e.printStackTrace();
      11. }
      12. return s;
      13. }
      14. }
      15. Shadow s1 = new Shadow();
      16. s1.setA(10);
      17. s1.setB("original");
      18. s1.setC(new int[]{1000});
      19. System.out.println("s1 克隆前:" + s1);
      20. Shadow s2 = s1.clone();
      21. s2.setA(20);
      22. s2.setB("shadowClone");
      23. int[] c1 = s2.getC();
      24. c1[0] = 500;
      25. s2.setC(c1);
      26. System.out.println("克隆的 s2:" + s2);
      27. System.out.println("s1 克隆后:" + s1);
      28. }
      29. class CloneClass{
      30. int a;
      31. String b;
      32. int[] c;
      33. CloneClass(){}
      34. public int getA() {
      35. return a;
      36. }
      37. public void setA(int a) {
      38. this.a = a;
      39. }
      40. public String getB() {
      41. return b;
      42. }
      43. public void setB(String b) {
      44. this.b = b;
      45. }
      46. public int[] getC() {
      47. return c;
      48. }
      49. public void setC(int[] c) {
      50. this.c = c;
      51. }
      52. @Override
      53. public String toString() {
      54. return "CloneClass{" +
      55. "a=" + a +
      56. ", b='" + b + '\'' +
      57. ", c=" + Arrays.toString(c) +
      58. '}';
      59. }
      60. }
      61. // 输出
      62. s1 克隆前:CloneClass{a=10, b='original', c=[1000]}
      63. 克隆的 s2CloneClass{a=20, b='shadowClone', c=[500]}
      64. s1 克隆后:CloneClass{a=10, b='original', c=[500]}
    • 深克隆:对基本类型和引用类型都实现对象的克隆,如实现 serializable 接口;

      1.3 工厂方法(Factory Method)模式

  • 理论

    • 概念:定义一个创建对象的工厂接口,通过具体子工厂完成创建,以满足“创建与使用相分离”的特点;
    • 需求:1)软件开发中实现软件对象的生产和使用相分离;2)在满足“开闭原则”下客户可随意增删或改变对软件相关对象的使用;
    • 使用场景
      • 客户只知道创建产品的工厂名,而不知道具体的产品名。如 TCL 电视工厂、海信电视工厂等;
      • 创建对象的任务由多个具体子工厂中的某一个完成,而抽象工厂只提供创建产品的接口;
      • 客户不关心创建产品的细节,只关心产品的品牌;
  • 实现

    1. public class creatorFactory {
    2. interface RoleFactory{
    3. Role creator(String name);
    4. }
    5. interface Role{ void print(); }
    6. static class StuWork implements Role{ public void print() { System.out.println("student work"); }}
    7. static class StuFun implements Role{ public void print() { System.out.println("student fun"); } }
    8. static class TeaWork implements Role{ public void print() { System.out.println("teacher work"); } }
    9. static class TeaFun implements Role{ public void print() { System.out.println("teacher fun"); } }
    10. static class StuFactory implements RoleFactory{
    11. public Role creator(String name) {
    12. if (name.equals("sw")) { return new StuWork(); }
    13. else if (name.equals("sf")) { return new StuFun(); }
    14. return null;
    15. }
    16. }
    17. static class TeaFactory implements RoleFactory{
    18. public Role creator(String name) {
    19. if (name.equals("tw")) { return new TeaWork(); }
    20. else if (name.equals("tf")) { return new TeaFun(); }
    21. return null;
    22. }
    23. }
    24. public static void main(String[] args) {
    25. RoleFactory student = new StuFactory();
    26. student.creator("sf").print();
    27. RoleFactory teacher = new TeaFactory();
    28. teacher.creator("tw").print();
    29. }
    30. }

    1.4 抽象工厂(AbstractFactory)模式

  • 理论

    • 概念:为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品;
    • 需求:工厂方法模式只考虑生产同等级(单一类)的产品,但在现实生活中许多工厂是综合型的工厂,能生产多等级(多种类) 的产品,将同一个具体工厂所生产的位于不同等级的一组产品称为一个产品族;
    • 使用场景
      • 创建对象是一系列相互关联或相互依赖的产品族时,如电器工厂中的电视机、洗衣机、空调等;
      • 系统中有多个产品族,但每次只使用其中的某一族产品,如有人只喜欢穿某一个品牌的衣服和鞋;
      • 系统中提供了产品的类库,且所有产品的接口相同,客户端不依赖产品实例的创建细节和内部结构;
  • 实现

    public class creatorAbstractFactory {
     static class Role{
          private Work work; private Fun fun;
          Role(Work work, Fun fun){
             this.work = work;
             this.fun = fun;
          }
          void printInfo(){
              System.out.println("show composed role:");
              work.print();
              fun.print();
          }
     }
      interface RoleFactory{
          Work makerWork();
          Fun makerFun();
      }
      static class stuFactory implements RoleFactory{
          public Work makerWork() { return new StuWork(); }
          public Fun makerFun() { return new StuFun(); }
      }
      static class teaFactory implements RoleFactory{
          public Work makerWork() {
              return new TeaWork();
          }
          public Fun makerFun() {
              return new TeaFun();
          }
      }
      interface Work{ void print();}
      interface Fun{ void print();}
      static class StuWork implements Work{
          public void print() {
              System.out.println("创建 student work");
          }
      }
      static class TeaWork implements Work{
          public void print() {
              System.out.println("创建 teacher work");
          }
      }
      static class StuFun implements Fun{
          public void print() {
              System.out.println("创建 student fun");
          }
      }
      static class TeaFun implements Fun{
          public void print() {
              System.out.println("创建 teacher fun");
          }
      }
      public static void main(String[] args) {
          RoleFactory student = new stuFactory();
          Work stuWork = student.makerWork();
          RoleFactory teacher = new teaFactory();
          Fun teaFun = teacher.makerFun();
          Role role = new Role(stuWork, teaFun);
          role.printInfo();
      }
    }
    输出:show composed role:
       创建 student work
       创建 teacher fun
    

    1.5 建造者(Builder)模式

  • 理论

    • 概念:指将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示,产品的组成部分是不变,但每一部分可以灵活选择;
    • 需求:在软件开发过程中有时需要创建一个复杂的对象,这个复杂对象通常由多个子部件按一定的步骤组合而成;
    • 使用场景
      • 创建的对象较复杂,由多个部件构成,各部件面临着复杂的变化,但构件间的建造顺序是稳定的;
      • 创建复杂对象的算法独立于该对象的组成部分以及它们的装配方式,即产品的构建过程和最终的表示是独立的;
  • 实现

      // 产品类
      static class Computer{
          private String cpu ; // cpu
          private String hardDisk ; //硬盘
          private String mainBoard ; // 主板
          private String memory ; // 内存
    
          public String getCpu() {
              return cpu;
          }
    
          public void setCpu(String cpu) {
              this.cpu = cpu;
          }
    
          public String getHardDisk() {
              return hardDisk;
          }
    
          public void setHardDisk(String hardDisk) {
              this.hardDisk = hardDisk;
          }
    
          public String getMainBoard() {
              return mainBoard;
          }
    
          public void setMainBoard(String mainBoard) {
              this.mainBoard = mainBoard;
          }
    
          public String getMemory() {
              return memory;
          }
    
          public void setMemory(String memory) {
              this.memory = memory;
          }
    
          @Override
          public String toString() {
              return "Computer{" +
                      "cpu='" + cpu + '\'' +
                      ", hardDisk='" + hardDisk + '\'' +
                      ", mainBoard='" + mainBoard + '\'' +
                      ", memory='" + memory + '\'' +
                      '}';
          }
      }
      // 抽象构造类
      interface Builder{
          void createCpu(String cpu);
          void createHardDisk(String hardDisk);
          void createMainBoard(String mainBoard);
          void createMemory(String memory);
          Computer createComputer();
      }
      // 具体构造类
      static class AssemblerBuilder implements Builder{
          private Computer computer = new Computer();
          public void createCpu(String cpu) {
              computer.setCpu(cpu);
          }
          public void createHardDisk(String hardDisk) {
              computer.setHardDisk(hardDisk);
          }
          public void createMainBoard(String mainBoard) {
              computer.setMainBoard(mainBoard);
          }
          public void createMemory(String memory) {
              computer.setMemory(memory);
          }
          public Computer createComputer() {
              return computer;
          }
      }
      // 指挥类
      static class Director{
          private Builder builder;
          Director(Builder builder){
              this.builder = builder;
          }
          Computer getComputer(String cpu, String hardDisk, String mainBoard, String memory){
              this.builder.createCpu(cpu);
              builder.createHardDisk(hardDisk);
              builder.createMainBoard(mainBoard);
              builder.createMemory(memory);
              return builder.createComputer();
          }
      }
      public static void main(String[] args) {
          Builder builder = new AssemblerBuilder();
          Director director = new Director(builder);
          Computer computer = director.getComputer("Intel 酷睿i9 7900X","三星M9T 2TB (HN-M201RAD)",
                  "技嘉AORUS Z270X-Gaming 7","科赋Cras II 红灯 16GB DDR4 3000");
          System.out.println(computer);
      }
    

    2. 结构型模式

    结构型模式描述如何将类或对象按某种布局组成更大的结构,可分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者釆用组合或聚合来组合对象,由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象结构型模式比类结构型模式更为灵活。

    2.1 代理(Proxy)模式

  • 理论

    • 概念:访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介,实现访问控制,具有隔离保护、目标扩展的功能,同时造成请求速度降低和系统复杂;
    • 需求:出于性能或安全等情况考虑,一个客户不能或不想直接访问另一个对象,需通过中介控制访问;
    • 使用场景
      • 远程代理:这种方式通常是为了隐藏目标对象存在于不同地址空间的事实,方便客户端访问,如用户申请某些网盘空间时,会在用户的文件系统中建立一个虚拟的硬盘,用户访问虚拟硬盘时实际访问的是网盘空间;
      • 虚拟代理:这种方式通常用于要创建的目标对象开销很大时,如下载一幅很大的图像需要很长时间,因某种计算比较复杂而短时间无法完成,这时可以先用小比例的虚拟代理替换真实的对象,消除用户对服务器慢的感觉;
      • 安全代理:这种方式通常用于控制不同种类客户对真实对象的访问权限;
      • 智能指引:主要用于调用目标对象时,代理附加一些额外的处理功能,如增加计算真实对象的引用次数的功能,这样当该对象没有被引用时,就可以自动释放它;
      • 延迟加载:指为了提高系统的性能,延迟对目标的加载,如 Hibernate 中就存在属性的延迟加载和关联表的延时加载;
  • 实现

    public class ConstructorProxy {
      interface Subject{
          void request();
      }
      static class RealSubject implements Subject{
          public void request() {
              System.out.println("show real information of realSubject");
          }
      }
      static class Proxy implements Subject{
          private RealSubject realSubject;
          public void request() {
              if (realSubject == null) {
                  realSubject = new RealSubject();
              }
              preRequest();
              realSubject.request();
              postRequest();
          }
          public void preRequest(){
              System.out.println("show the pre_request");
          }
          public void postRequest(){
              System.out.println("show the post_request");
          }
      }
      public static void main(String[] args) {
          Proxy proxy = new Proxy();
          proxy.request();
      }
    }
    

    2.2 适配器(Adapter)模式

  • 理论

    • 概念:将一个类的接口转换成客户要求的另外一个接口,解决接口不兼容的问题,间接降低耦合度,适配器模式分为类结构型模式和对象结构型模式,一般用后者,更换适配器较为复杂;
    • 需求:某些类具有两个或多个维度的变化,如图形既可按形状分,又可按颜色分,若采用继承则子类过多且无法实现良好的扩展;
    • 使用场景
      • 以前开发的系统存在满足新系统功能需求的类,但其接口同新系统的接口不一致;
      • 使用第三方提供的组件,但组件接口定义和自己要求的接口定义不同;
  • 实现

    public class ConstructAdapter {
      // 目标接口
      interface Target{
          void request();
      }
      // 适配者接口
      static class Adaptee{
          void specificRequest(){
              System.out.println("适配者中的业务代码被调用");
          }
      }
      // 类适配器接口
      static class ClassAdapter extends Adaptee implements Target{
          public void request() {
              specificRequest();
          }
      }
      // 对象适配器接口
      static class ObjectAdapter implements Target{
          private Adaptee adaptee;
          ObjectAdapter(Adaptee adaptee){
              this.adaptee = adaptee;
          }
          public void request() {
              adaptee.specificRequest();
          }
      }
      public static void main(String[] args) {
          Target target1 = new ClassAdapter();
          target1.request();
    
          Target target2 = new ObjectAdapter(new Adaptee());
          target2.request();
      }
    }
    

    2.3 桥接(Bridge)模式(多维度)

  • 理论

    • 概念:将抽象与实现分离,用组合关系代替继承关系,降低了抽象和实现这两个可变维度的耦合度,扩展能力强且实现细节对客户透明,但抽象编程存在理解和设计难度;
    • 需求:在有些系统中,为了节省内存资源、保证数据内容的一致性,对某些类要求只能创建一个实例;
    • 使用场景
      • 一个类存在两个独立变化的维度,且这两个维度都需要进行扩展;
      • 一个系统不希望使用继承或因为多层次继承导致系统类的个数急剧增加;
      • 当一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性时;
  • 实现

    public class ConstructorBridge {
      // 女士皮包有很多种,可以按用途分、按皮质分、按品牌分、按颜色分、按大小分等,存在多个维度的变化;
      // 实现化角色1 Color
      interface Color{
          String getColor();
      }
      // 实现化角色2 Size
      interface Size{
          String getSize();
      }
      // 具体实现化角色1
      static class Red implements Color{
          public String getColor() { return "red"; }
      }
      static class Yellow implements Color{
          public String getColor() { return "yellow"; }
      }
      // 具体实现化角色2
      static class Little implements Size{
          public String getSize() { return "little"; }
      }
      static class Big implements Size{
          public String getSize() { return "big"; }
      }
      // 抽象化角色类
      abstract class Bag{
          protected Color color;
          protected Size size;
          Bag(Color color, Size size){
              this.color = color;
              this.size = size;
          }
          abstract String getName();
      }
      // 扩展抽象化角色类
      class HandBag extends Bag{
          HandBag(Color color, Size size) {
              super(color, size);
          }
          String getName() {
              return "手提包, 对应颜色:" + color.getColor() + ",对应尺寸:" + size.getSize();
          }
      }
      class Wallet extends Bag{
          Wallet(Color color, Size size) {
              super(color, size);
          }
          String getName() {
              return "钱包, 对应颜色:" + color.getColor() + ",对应尺寸:" + size.getSize();
          }
      }
      public static void main(String[] args) {
          ConstructorBridge bridge = new ConstructorBridge();
          bridge.testBridge();
      }
      public void testBridge(){
          Bag bag1 = new HandBag(new Red(),new Big());
          Bag bag2 = new Wallet(new Yellow(),new Little());
          System.out.println(bag1.getName());
          System.out.println(bag2.getName());
      }
    }
    

    2.4 装饰(Decorator)模式(功能扩展)

  • 理论

    • 概念:在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式,属于对象结构型模式,比继承方式相对灵活;
    • 需求:软件开发过程中,基于现有组件,在不改变其结构的情况下,可以动态地扩展其功能;
    • 使用场景
      • 给一个现有类添加附加职责,而又不能采用生成子类的方法进行扩充,例如该类被隐藏或者该类是终极类或者采用继承方式会产生大量的子类;
      • 通过对现有的一组基本功能进行排列组合而产生非常多的功能,采用继承关系很难实现;
      • 对象的功能要求可动态地添加,也可再动态地撤销;
  • 实现

    public class ConstructorDecorator {
      // 不同咖啡豆和不同流质可生成不同口味的组合的咖啡饮料
      // 抽象构件
      interface Beverage{
          String getDescription();
          Double getPrice();
      }
      // 具体构件1
      class CoffeeBean1 implements Beverage{
          private String description = "第一种咖啡豆";
          public String getDescription() {
              return description;
          }
          public Double getPrice() {
              return 50.0;
          }
      }
      // 具体构件2
      class CoffeeBean2 implements Beverage{
          private String description = "第二种咖啡豆";
          public String getDescription() {
              return description;
          }
          public Double getPrice() {
              return 100.0;
          }
      }
      // 抽象装饰类
      abstract class Decorator implements Beverage{
          protected Beverage beverage;
    //        Decorator(Beverage beverage){
    //            this.beverage = beverage;
    //        }
          public String getDescription() {
              return beverage.getDescription();
          }
          public Double getPrice() {
              return beverage.getPrice();
          }
      }
      // 装饰角色1
      class Milk extends Decorator{
          private Beverage beverage = null;
          Milk(Beverage beverage) {
    //            super(beverage); // 会报空指针异常
              this.beverage = beverage;
          }
          public String getDescription() {
              return beverage.getDescription() + addFunc();
          }
          public Double getPrice() {
              return beverage.getPrice() + 10;// 10 为牛奶的价格
          }
          public String addFunc(){
              return  ",加了牛奶";
          }
      }
      // 装饰角色2
      class Mocha extends Decorator{
          private Beverage beverage = null;
          Mocha(Beverage beverage) {
              this.beverage = beverage;
          }
          public String getDescription() {
              return beverage.getDescription() + addFunc();
          }
          public Double getPrice() {
              return beverage.getPrice() + 20;
          }
          public String addFunc(){
              return ",加了摩卡";
          }
      }
      // 装饰角色3
      class Soy extends Decorator{
          private Beverage beverage = null;
          Soy(Beverage beverage) {
              this.beverage = beverage;
          }
          public String getDescription() {
              return beverage.getDescription() + addFunc();
          }
          public Double getPrice() {
              return beverage.getPrice() + 30;
          }
          public String addFunc(){
              return ",加了豆浆";
          }
      }
      public static void main(String[] args) {
          ConstructorDecorator decorator = new ConstructorDecorator();
          decorator.testDecorator();
      }
      public  void testDecorator(){
           Beverage beverage = new CoffeeBean1();
           beverage = new Milk(beverage);
           beverage = new Soy(beverage);
           System.out.println(beverage.getDescription() + ",总价格为:" + beverage.getPrice());
      }
    }
    输出:
      第一种咖啡豆,加了牛奶,加了豆浆,总价格为:90.0
    

    2.5 外观(Facade)模式(总开关)

  • 理论

    • 概念:通过为多个复杂的子系统提供一个统一接口,使得子系统更加容易被访问,不仅对客户屏蔽了系统组件,也降低两者耦合度,便于维护;
    • 需求:随着一个系统的功能越来越强,子系统越来越多,客户对系统的访问也越来越复杂,有必要为多个子系统提供一个统一的接口,从而降低系统的耦合度;
    • 使用场景
      • 对分层结构系统构建时,使用外观模式定义子系统中每层的入口点可以简化子系统之间的依赖关系;
      • 当一个复杂系统的子系统很多时,外观模式可以为系统设计一个简单的接口供外界访问;
      • 当客户端与多个子系统之间存在很大的联系时,引入外观模式可将它们分离,从而提高子系统的独立性和可移植性;
  • 实现

    public class ConstructorFacade {
      class Light{
          void makeOn(){
              System.out.println("开灯");
          }
      }
      class TV{
          void makeOpen(){
              System.out.println("打开电视");
          }
      }
      class Aircondition{
          void makeWork(){
              System.out.println("打开空调");
          }
      }
      class Facade{
          private Light light;
          private TV tv;
          private Aircondition aircondition;
          Facade(Light light, TV tv, Aircondition aircondition){
              this.light = light;
              this.tv = tv;
              this.aircondition = aircondition;
          }
          void on(){
              light.makeOn();
              tv.makeOpen();
              aircondition.makeWork();
          }
      }
      public static void main(String[] args) {
          ConstructorFacade facade = new ConstructorFacade();
          facade.testFacade();
      }
      private void testFacade(){
          Facade facade = new Facade(new Light(),new TV(),new Aircondition());
          facade.on();
      }
    }
    

    2.6 享元(Flyweight)模式(相似)

  • 理论

    • 概念:运用共享技术来有効地支持大量细粒度对象的复用,大幅度减少需要创建的对象数量、避免大量相似类的开销,提高系统资源的利用率,但需要将一些不能共享的状态外部化;
    • 需求:面向对象程序设计过程中,有时会面临要创建大量相同或相似对象实例的问题。创建那么多的对象将会耗费很多的系统资源,是系统性能提高的一个瓶颈;
    • 使用场景
      • 系统中存在大量相同或相似的对象,这些对象耗费大量的内存资源;
      • 大部分的对象可以按照内部状态进行分组,且可将不同部分外部化,每组只需保存一个内部状态;
      • 享元模式需要额外维护一个保存享元的数据结构,应当在有足够多的享元实例时才值得使用享元模式;
  • 实现 ```java import java.util.HashMap;

public class ConstructorFlyWeight { // 非享元角色 class UnsharedFlyWeight{ private String info; UnsharedFlyWeight(String info){ this.info = info; } public String getInfo() { return info; } public void setInfo(String info) { this.info = info; } } // 抽象享元角色 interface FlyWeight{ void operation(UnsharedFlyWeight unsharedFlyWeight); } // 具体享元角色 class ConcreteFlyWeight implements FlyWeight{ private String key; ConcreteFlyWeight(String key){ this.key = key; System.out.println(“享元” + key + “被创建”); } public void operation(UnsharedFlyWeight unsharedFlyWeight) { System.out.println(“具体享元” + key + “被调用”); unsharedFlyWeight.getInfo(); } } // 享元工厂 class FlyWeightFactory{ HashMap map = new HashMap(); FlyWeight createFlyWeight(String key){ FlyWeight flyWeight = map.get(key); if (flyWeight != null) { System.out.println(“具体享元” + key + “已被创建”); } else { flyWeight = new ConcreteFlyWeight(key); map.put(key, flyWeight); } return flyWeight; } } public static void main(String[] args) { ConstructorFlyWeight flyWeight = new ConstructorFlyWeight(); flyWeight.testFlyWeight(); } public void testFlyWeight(){ FlyWeightFactory factory = new FlyWeightFactory(); FlyWeight fw1 = factory.createFlyWeight(“A”); fw1.operation(new UnsharedFlyWeight(“A_INFO”)); FlyWeight fw2 = factory.createFlyWeight(“B”); fw2.operation(new UnsharedFlyWeight(“B_INFO”)); FlyWeight fw3 = factory.createFlyWeight(“A”); } } 输出: 享元A被创建 具体享元A被调用 享元B被创建 具体享元B被调用 具体享元A已被创建

<a name="jjhdV"></a>
### 2.7 组合(Composite)模式(文件树)

- 理论
   - 概念:将对象组合成树状的层次结构的模式,用来表示“部分-整体”的关系,使用户对单个对象和组合对象具有一致的访问性;
   - 需求:软件开发过程中,如文件系统中的文件与文件夹、窗体程序中的简单控件与容器控件等,对这些简单对象与复合对象的处理,如果用组合模式来实现会很方便;
   - 使用场景
      - 在需要表示一个对象整体与部分的层次结构的场合;
      - 要求对用户隐藏组合对象与单个对象的不同,用户可用统一的接口使用组合结构中的所有对象的场合;
- 实现
```java
import java.util.ArrayList;
import java.util.List;

public class ConstructComposite {
    // 构建一棵文件树
    // 抽象构件
    abstract class File{
        protected String name;
        File(String name){
            this.name = name;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public abstract void display();
    }
    // 树枝构件
    class Folder extends File{
        private List<File> files = new ArrayList<File>();
        Folder(String name) {
            super(name);
        }
        public void display() {
            System.out.println("该文件夹包括:");
            for (File f:files) {
                System.out.println(f.getClass().getName() + "_" + f.getName());
            }
        }
        public void addFile(File file){
            files.add(file);
        }
        public void deleteFile(File file){
            files.remove(file);
        }
    }
    // 树叶构件
    class TextFile extends File{
        TextFile(String name) {
            super(name);
        }
        public void display() {
            System.out.println("文本类文件" + this.name);
        }
    }
    // 树叶构件
    class ImageFile extends File{
        ImageFile(String name) {
            super(name);
        }
        public void display() {
            System.out.println("图片类文件" + this.name);
        }
    }
    public static void main(String[] args) {
        ConstructComposite composite = new ConstructComposite();
        composite.testComposite();
    }
    public void testComposite(){
        Folder folder_1 = new Folder("总文件夹");
        folder_1.addFile(new TextFile("文本"));
        folder_1.addFile(new ImageFile("照片"));
        Folder folder_2 = new Folder("子文件夹");
        folder_1.addFile(folder_2);
        folder_1.display();
    }
}

3. 行为型模式

3.1 模板方法(Template Method)模式(炒菜)

  • 理论
    • 概念:定义一个操作中的算法骨架,将算法的一些步骤延迟到子类中,使得子类在可以不改变该算法结构的情况下重定义该算法的某些特定步骤,虽然符合开闭原则,但子类数量过多会造成设计复杂和理解难度;
    • 需求:在面向对象程序设计中,了解算法所需的关键步骤及执行顺序,但某些步骤的具体实现还未知,或者说某些步骤的实现与具体的环境相关;
    • 使用场景
      • 算法的整体步骤很固定,但其中个别部分易变,可将易变部分抽象出来供子类实现;
      • 多个子类存在公共行为,可将其提取出来并集中到一个公共父类中以避免代码重复;
      • 需要控制子类的扩展,模板方法只在特定点调用钩子操作,这样就只允许在这些点进行扩展;
  • 实现

    public class BehaviourTemplateMethod {
      abstract class FryVegetable{
          // 模板方法
          public final void process(){
              this.pourOil();
              this.heatOil();
              this.pourVegetable();
              this.pourSource();
              this.fry();
          }
          // 具体方法
          void pourOil(){
              System.out.println("倒油");
          }
          void heatOil(){
              System.out.println("热油");
          }
          // 抽象方法
          abstract void pourVegetable();
          abstract void pourSource();
          void fry(){
              System.out.println("炒菜");
          }
          // 钩子方法:在抽象类中已经实现,包括用于判断的逻辑方法和需要子类重写的空方法
      }
      class FryCabbage extends FryVegetable{
          void pourVegetable() {
              System.out.println("倒入包菜");
          }
          void pourSource() {
              System.out.println("加入蚝油");
          }
      }
      class FryCarrot extends FryVegetable{
          void pourVegetable() {
              System.out.println("倒入胡萝卜");
          }
          void pourSource() {
              System.out.println("加入料酒");
          }
      }
      public static void main(String[] args) {
          BehaviourTemplateMethod method = new BehaviourTemplateMethod();
          method.testCook();
      }
      public void testCook(){
          FryVegetable cabbage = new FryCabbage();
          cabbage.process();
          FryVegetable carrot = new FryCarrot();
          carrot.process();
      }
    }
    

    3.2 策略(Strategy)模式(算法替换)

  • 理论

    • 概念:定义并独立封装一系列算法,使它们可以相互替换,且算法的改变不会影响使用算法的客户;
    • 需求:在软件开发中当实现某一个功能存在多种算法或者策略,可以根据环境或者条件的不同选择不同的算法或者策略来完成该功能,若使用多重条件转移语句实现,会使得实现复杂且违背开闭原则;
    • 使用场景
      • 算法的整体步骤很固定,但其中个别部分易变,可将易变部分抽象出来供子类实现;
      • 多个子类存在公共行为,可将其提取出来并集中到一个公共父类中以避免代码重复;
      • 需要控制子类的扩展,模板方法只在特定点调用钩子操作,这样就只允许在这些点进行扩展;
  • 实现

    public class BehaviourStrategy {
      // 抽象策略类
      interface Strategy{
          Integer calculate(int a, int b);
      }
      // 具体策略类
      class AddStrategy implements Strategy{
          public Integer calculate(int a, int b) {
              return a + b;
          }
      }
      class SubtractStrategy implements Strategy{
          public Integer calculate(int a, int b) {
              return a - b;
          }
      }
      // 环境类:持有一个策略类的引用,最终给客户端调用
      class Context{
          Strategy strategy;
          Context(Strategy strategy){
              this.strategy = strategy;
          }
          void dynamicReplace(Strategy applyStrategy){
              this.strategy = applyStrategy;
          }
          Integer calculate(int a, int b){
              return strategy.calculate(a,b);
          }
      }
      public static void main(String[] args) {
          BehaviourStrategy strategy = new BehaviourStrategy();
          strategy.testStrategy();
      }
      public void testStrategy(){
          Strategy add = new AddStrategy();
          Context context = new Context(add);
          context.dynamicReplace((Strategy) new SubtractStrategy());
          System.out.println(context.calculate(10,2));
      }
    }
    

    3.3 命令(Command)模式(点餐)

  • 理论

    • 概念:将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开,这样两者之间通过命令对象进行沟通,方便对命令对象进行储存、传递、调用、增加与管理;
    • 需求:在软件开发系统中,常常出现“方法的请求者”与“方法的实现者”之间存在紧密的耦合关系,不利于软件功能的扩展与维护;
    • 使用场景
      • 系统需要随机请求命令或经常增加或删除命令;
      • 系统需要执行一组操作,命令模式可以定义宏命令来实现该功能;
      • 系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作时,可以将命令对象存储起来,采用备忘录模式来实现;
  • 实现

    public class BehaviourCommand {
      // 用命令模式实现客户去餐厅吃早餐,点餐是命令,服务员是调用者,厨师是接收者
    
      // 调用者
      class Waiter{
          OrderBreakfast eatHunDun, eatJuanBing;
          public void chooseEatHunDun() {
              eatHunDun.orderInfo();
          }
          public void setEatHunDun(OrderBreakfast eatHunDun) {
              this.eatHunDun = eatHunDun;
          }
          public void chooseEatJuanBing() {
              eatJuanBing.orderInfo();
          }
          public void setEatJuanBing(OrderBreakfast eatJuanBing) {
              this.eatJuanBing = eatJuanBing;
          }
      }
      // 抽象命令类
      interface OrderBreakfast{
          void orderInfo();
      }
      // 具体命令类
      class HunDun implements OrderBreakfast{
          Chief_HunDun chief_hunDun;
          HunDun(){
              chief_hunDun = new Chief_HunDun();
          }
          public void orderInfo() {
              chief_hunDun.print();
          }
      }
      class JuanBing implements OrderBreakfast{
          Chief_JuanBing chief_juanBing;
          JuanBing(){
              chief_juanBing = new Chief_JuanBing();
          }
          public void orderInfo() {
              chief_juanBing.print();
          }
      }
      // 接收者:做混沌的厨师
      class Chief_HunDun{
          void print(){
              System.out.println("收到一单做混沌的命令");
          }
      }
      // 接收者:做卷饼的厨师
      class Chief_JuanBing{
          void print(){
              System.out.println("收到一单做卷饼的命令");
          }
      }
      public static void main(String[] args) {
          BehaviourCommand command = new BehaviourCommand();
          command.testCommand();
      }
      public void testCommand(){
          Waiter waiter = new Waiter();
          OrderBreakfast order = new JuanBing();
          waiter.setEatJuanBing(order);
          waiter.chooseEatJuanBing();
      }
    }
    

    3.4 职责链(Chain of Responsibility)模式(请假链)

  • 理论

    • 概念:为了避免请求发送者与多个请求处理者耦合在一起,将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链,当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止,但可能存在请求最终没有被处理或“责任链”过长影响性能等情况;
    • 需求:一个请求有多个对象可以处理,但每个对象的处理条件或权限不同;
    • 使用场景
      • 有多个对象可以处理一个请求,哪个对象处理该请求由运行时刻自动确定;
      • 可动态指定一组对象处理请求,或添加新的处理者;
      • 在不明确指定请求处理者的情况下,向多个处理者中的一个提交请求;
  • 实现 ```java // 以下案例较特殊,处理者之间的处理逻辑类似,故在原基础上进行重构,否则一般情况下,handleRequest 应 用 abstract 修饰,各个处理者分别实现该方法;

public class BehaviourResponsibilityChain { //用责任链模式设计一个请假条审批模块。 //假如规定学生请假,班主任可以批准;小于或等于 7 天,系主任可以批准;小于或等于 10 天,院长可以批准;其他情况不予批准;

// 抽象处理者
abstract class Leader{
    Leader next = null;
    String name;
    Integer critic;
    Leader(String name){
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public Integer getCritic() {
        return critic;
    }
    public void setCritic(Integer critic) {
        this.critic = critic;
    }
    void setNext(Leader leader){
        next = leader;
    }
    Leader getNext(){
        return next;
    }
    void handleRequest(int leave){
        if (leave <= getCritic()) {
            System.out.println(this.getName() + "批准假期: " + leave + "天");
        } else {
            if (getNext() != null) {
                getNext().handleRequest(leave);
            } else {
                System.out.println("请假天数太多,没人批准该假条");
            }
        }
    }
}
// 具体处理者
class Teacher extends Leader{
    Teacher(String name) {
        super(name);
    }
}
class ChairMan extends Leader{
    ChairMan(String name) {
        super(name);
    }
}
class Dean extends Leader{
    Dean(String name) {
        super(name);
    }
}
public static void main(String[] args) {
    BehaviourResponsibilityChain chain = new BehaviourResponsibilityChain();
    chain.testResponsibility();
}
public void testResponsibility(){
    Leader dean = new Dean("院长");
    dean.setCritic(10);
    Leader chairMan = new ChairMan("系主任");
    chairMan.setCritic(7);
    Leader teacher = new Teacher("班主任");
    teacher.setCritic(2);
    dean.setNext(null);
    chairMan.setNext(dean);
    teacher.setNext(chairMan);
    teacher.handleRequest(5);
}

}

<a name="bZ6w1"></a>
### 3.5 状态(State)模式(成绩状态转换)

- 理论
   - 概念:对有状态的对象,把复杂的“判断逻辑”提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为;
   - 需求:在软件开发过程中,应用程序中的有些对象可能会根据不同的情况做出不同的行为,当有状态的对象与外部事件产生互动时,其内部状态会发生改变,从而使得其行为也随之发生改变。;
   - 使用场景
      - 一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为;
      - 一个操作中含有庞大的分支结构,并且这些分支决定于对象的状态时;
- 实现
```java
public class BehaviourState {
    //用“状态模式”设计一个学生成绩的状态转换程序。
    //包含 3 种状态,当学生的分数小于 60 分时为“不及格”状态,当分数大于等于 60 分且小于 90 分时为“中等”状态,当分数大于等于 90 分时为“优秀”状态

    // 环境类
    class ScoreContext{
        AbstractState abstractState;
        ScoreContext(){
            abstractState = new LowState(this);
        }
        public AbstractState getAbstractState() {
            return abstractState;
        }
        public void setAbstractState(AbstractState abstractState) {
            this.abstractState = abstractState;
        }
        void add(int score){
            abstractState.addScore(score);
        }
    }
    // 抽象状态类
    abstract class AbstractState{
        ScoreContext context;
        String state_name;
        int score;
        public String getState_name() {
            return state_name;
        }
        public void setState_name(String state_name) {
            this.state_name = state_name;
        }
        void addScore(int num){
            score += num;
            System.out.print("加分:" + num + ",现分数为:" + score);
            checkState();
            System.out.println(",加分后状态重新调整为:" + context.getAbstractState().getState_name());
        }
        void updateState(AbstractState state){
            context = state.context;
            score = state.score;
        }
        abstract void checkState();
    }
    // 具体状态类
    class LowState extends AbstractState{
        LowState(ScoreContext context){
            this.context = context;
            score = 0;
            state_name = StateName.low;
        }
        LowState(AbstractState state){
            updateState(state);
            state_name = StateName.low;
        }
        void checkState() {
            if (score > Critic.up) {
                context.setAbstractState(new HighState(this));
            }
            else if (score > Critic.low) {
                context.setAbstractState(new MiddleState(this));
            }
            else {
                context.setAbstractState(new LowState(this));
            }
        }
    }
    class MiddleState extends AbstractState{
        MiddleState(AbstractState state){
            updateState(state);
            state_name = StateName.middle;
        }
        void checkState() {
            if (score < Critic.low) {
                context.setAbstractState(new LowState(this));
            }
            else if (score > Critic.up) {
                context.setAbstractState(new HighState(this));
            }
        }
    }
    class HighState extends AbstractState{
        HighState(AbstractState state){
            updateState(state);
            state_name = StateName.high;
        }
        void checkState() {
            if (score < Critic.low) {
                context.setAbstractState(new LowState(this));
            }
            else if (score < Critic.up) {
                context.setAbstractState(new MiddleState(this));
            }
        }
    }
    static class Critic{
        static private int low;
        static private int up;
        public static void setLow(int low) {
            Critic.low = low;
        }
        public static void setUp(int up) {
            Critic.up = up;
        }
    }
    static class StateName{
        static private String low;
        static private String middle;
        static private String high;
        public static void setLow(String low) {
            StateName.low = low;
        }
        public static void setMiddle(String middle) {
            StateName.middle = middle;
        }
        public static void setHigh(String high) {
            StateName.high = high;
        }
    }
    public static void main(String[] args) {
        BehaviourState state = new BehaviourState();
        state.testState();
    }
    public void testState(){
        ScoreContext context = new ScoreContext();
        Critic.setLow(60); Critic.setUp(90);
        StateName.setLow("不及格"); StateName.setMiddle("中等"); StateName.setHigh("优秀");
        context.add(40);
        context.add(30);
        context.add(25);
        context.add(-40);
    }
}
输出:
    加分:40,现分数为:40,加分后状态重新调整为:不及格
    加分:30,现分数为:70,加分后状态重新调整为:中等
    加分:25,现分数为:95,加分后状态重新调整为:优秀
    加分:-40,现分数为:55,加分后状态重新调整为:不及格

3.6 观察者(Observer)模式(响铃)

  • 理论
    • 概念:指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。这种模式有时又称作发布-订阅模式、模型-视图模式;
    • 需求:一个对象的行为发生改变可能会导致一个或者多个其他对象的行为也发生改变,如Excel 中的数据与折线图、饼状图、柱状图之间的关系;
    • 使用场景
      • 对象间存在一对多关系,一个对象的状态发生改变会影响其他对象;
      • 当一个抽象模型有两个方面,其中一个方面依赖于另一方面时,可将这二者封装在独立的对象中以使它们可以各自独立地改变和复用;
  • 实现 ```java import java.util.*;

public class BehaviourObserver { //利用观察者模式设计一个学校铃声的事件处理程序

//铃声事件类:用于封装事件源及一些与事件相关的参数
class RingEvent extends EventObject{
    private boolean sound;
    public RingEvent(Object source, boolean sound) {
        super(source);
        this.sound = sound;
    }
    public boolean isSound() {
        return sound;
    }
    public void setSound(boolean sound) {
        this.sound = sound;
    }
}
//目标类:事件源,铃
class BellEventSource {
    private List<BellEventListener> listeners;
    BellEventSource(){
        listeners = new ArrayList<BellEventListener>();
    }
    // 监听器的绑定与解除
    void addListener(BellEventListener listener){
        listeners.add(listener);
    }
    void removeListener(BellEventListener listener){
        listeners.remove(listener);
    }
    // 铃声事件触发器
    public void ring(Boolean sound){
        String ring_type = sound ? "上课铃" : "下课铃";
        System.out.println(ring_type + "响");
        RingEvent event = new RingEvent(this, sound);
        notifies(event);
    }
    public void notifies(RingEvent event){
        for (BellEventListener listener:listeners) {
            listener.hearRing(event);
        }
    }
}
//抽象观察者类:铃声事件监听器
interface BellEventListener extends EventListener {
    void hearRing(RingEvent event);
}
//具体观察者类:老师事件监听器
class TeacherListener implements BellEventListener{
    public void hearRing(RingEvent event) {
        if (event.isSound()) {
            System.out.println("老师上课了");
        } else {
            System.out.println("老师下课啦");
        }
    }
}
//具体观察者类:学生事件监听器
class StudentListener implements BellEventListener{
    public void hearRing(RingEvent event) {
        if (event.isSound()) {
            System.out.println("学生上课了");
        } else {
            System.out.println("学生下课啦");
        }
    }
}
public static void main(String[] args) {
    BehaviourObserver observer = new BehaviourObserver();
    observer.testObserver();
}
public void testObserver(){
    BellEventSource source = new BellEventSource();
    source.addListener(new TeacherListener());
    source.addListener(new StudentListener());
    source.ring(true);
    source.ring(false);
}

} 输出: 上课铃响 老师上课了 学生上课了 下课铃响 老师下课啦 学生下课啦

<a name="lcaOj"></a>
### 3.7 中介者(Mediator)模式(同事影响)

- 理论
   - 概念:定义一个中介对象来封装一系列对象之间的交互,使原有对象之间的耦合松散,且可以独立地改变它们之间的交互。中介者模式又叫调停模式,是迪米特法则的典型应用;
   - 需求:处理多对多的复杂关系,可用星状结构代替网状结构,具备结构性的中转作用和行为性的协调作用;
   - 使用场景
      - 对象之间存在复杂的网状结构关系而导致依赖关系混乱且难以复用;
      - 需创建一个运行于多个类之间的对象,又不想生成新的子类;
- 实现
```java
public class BehaviourMediator {
    // 有两个类A和B,类中各有一个数字,并且要保证类B中的数字永远是类A中数字的100倍,即类A类B互相影响(同事类);
    // 抽象同事类
    abstract class AbstractColleague{
        protected int number;
        public int getNumber() {
            return number;
        }
        public void setNumber(int number) {
            this.number = number;
        }
        public abstract void dealWork(int number, Mediator mediator);
    }
    // 具体同事类A
    class ColleagueA extends AbstractColleague{
        public void dealWork(int number, Mediator mediator) {
            this.number = number;
            mediator.affectB();
        }
    }
    // 具体同事类B
    class ColleagueB extends AbstractColleague{
        public void dealWork(int number, Mediator mediator) {
            this.number = number;
            mediator.affectA();
        }
    }
    // 抽象中介类
    abstract class Mediator {
        protected ColleagueA colleagueA;
        protected ColleagueB colleagueB;
        Mediator(ColleagueA colleagueA, ColleagueB colleagueB){
            this.colleagueA = colleagueA;
            this.colleagueB = colleagueB;
        }
        abstract void affectA();
        abstract void affectB();
    }
    // 具体中介类
    class ConcreteMediator extends Mediator{
        ConcreteMediator(ColleagueA colleagueA, ColleagueB colleagueB) {
            super(colleagueA, colleagueB);
        }
        void affectA() {
            int number = colleagueB.getNumber();
            colleagueA.setNumber(number/100);
        }
        void affectB() {
            int number = colleagueA.getNumber();
            colleagueB.setNumber(number*100);
        }
    }
    public static void main(String[] args) {
        BehaviourMediator mediator = new BehaviourMediator();
        mediator.testMediator();
    }
    public void testMediator(){
        ColleagueA colleagueA = new ColleagueA();
        ColleagueB colleagueB = new ColleagueB();
        Mediator mediator = new ConcreteMediator(colleagueA, colleagueB);
        System.out.println("通过设置 A 类 来影响 B 类");
        colleagueA.dealWork(30, mediator);
        System.out.println("A 类的值为:" + colleagueA.getNumber());
        System.out.println("B 类的值为:" + colleagueB.getNumber());
        System.out.println("通过设置 B 类 来影响 A 类");
        colleagueB.dealWork(100, mediator);
        System.out.println("A 类的值为:" + colleagueA.getNumber());
        System.out.println("B 类的值为:" + colleagueB.getNumber());
    }
}
输出:
    通过设置 A 类 来影响 B 类
    A 类的值为:30
    B 类的值为:3000
    通过设置 B 类 来影响 A 类
    A 类的值为:1
    B 类的值为:100

3.8 迭代器(Iterator)模式

  • 理论
    • 概念:提供一个对象来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示,支持以不同迭代器遍历一个聚合,可灵活扩展新的聚合类和迭代器类;
    • 需求:在现实生活以及程序设计中,客户经常要访问一个聚合对象中的各个元素,如“数据结构”中的链表遍历,通常的做法是将链表的创建和遍历都放在同一个类中,但这种方式不利于程序的扩展,如果要更换遍历方法就必须修改程序源代码,这违背了 “开闭原则”;
    • 使用场景
      • 为聚合对象提供多种遍历方式;
      • 为遍历不同的聚合结构提供一个统一的接口;
      • 访问一个聚合对象的内容而无须暴露其内部细节的表示;
  • 实现 ```java import java.util.ArrayList; import java.util.List;

public class BehaviourIterator { // 实现一个自定义迭代器

// 抽象聚合类
interface AbstractAggregate {
    void add(Object obj);
    void remove(Object obj);
    MyIterator getIterator();
}
// 具体聚合类
class ConcreteAggregate implements AbstractAggregate{
    List<Object> objects;
    ConcreteAggregate(){
        objects = new ArrayList<Object>();
    }
    public void add(Object obj) {
        objects.add(obj);
    }
    public void remove(Object obj) {
        objects.remove(obj);
    }
    public MyIterator getIterator() {
        return new concreteIterator(objects);
    }
}
// 抽象迭代器
interface MyIterator {
    Object first();
    Object next();
    Boolean hasNext();
}
// 具体迭代器
class concreteIterator implements MyIterator{
    List<Object> objects = null;
    int index = -1;
    concreteIterator(List<Object> objects){
        this.objects = objects;
    }
    public Object first() {
        return objects.get(0);
    }
    public Object next() {
        if (hasNext()) {
            index += 1;
            return objects.get(index);
        }
        return null;
    }
    public Boolean hasNext() {
        if (index < objects.size() - 1) {
            return true;
        } else {
            return false;
        }
    }
}
public static void main(String[] args) {
    BehaviourIterator iterator = new BehaviourIterator();
    iterator.testIterator();
}
public void testIterator(){
    AbstractAggregate aggregate = new ConcreteAggregate();
    aggregate.add("cyt");
    aggregate.add("cyb");
    aggregate.add("zgx");
    aggregate.add("cjf");
    aggregate.remove("cyt");
    MyIterator iterator = aggregate.getIterator();
    while (iterator.hasNext()){
        System.out.println(iterator.next());
    }
}

}

<a name="Tn3yw"></a>
### 3.9 访问者(Visitor)模式(多角度评选)

- 理论
   - 概念:将作用于某种数据结构中的各元素的操作分离出来封装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作,为数据结构中的每个元素提供多种访问方式;
   - 需求:集合对象中存在多种不同的元素,且每种元素也存在多种不同的访问者和处理方式;
   - 使用场景
      - 对象结构相对稳定,但其操作算法经常变化的程序;
      - 对象结构中的对象需要提供多种不同且不相关的操作,而且要避免让这些操作的变化影响对象的结构;
      - 对象结构包含很多类型的对象,希望对这些对象实施一些依赖于其具体类型的操作;
- 实现
```java
import java.util.ArrayList;
import java.util.List;

public class BehaviourVisitor {
    // 如果老师教学反馈得分大于等于85分、学生成绩大于等于90分,则可以入选成绩优秀奖;如果老师论文数目大于8、学生论文数目大于2,则可以入选科研优秀奖;
    // 抽象访问者
    interface Visitor {
        void visit(Student student);
        void visit(Teacher teacher);
    }
    // 具体访问者:以成绩进行评选
    class GradeVisitor implements Visitor {
        String awardWords = "[%s]的分数为:%d,荣获成绩优秀奖";
        public void visit(Student element) {
            if (element.getGrade() > 90) {
                System.out.println(String.format(awardWords, element.getName(), element.getGrade()));
            } 
        }
        public void visit(Teacher element) {
            if (element.getGrade() > 85) {
                System.out.println(String.format(awardWords, element.getName(), element.getGrade()));
            } 
        }
    }
    // 具体访问者:以论文数目进行评选
    class PaperVisitor implements Visitor {
        String awardWords = "[%s]的论文数为:%d,荣获科研优秀奖";
        public void visit(Student element) {
            if (element.getPaperNum() >= 2) {
                System.out.println(String.format(awardWords, element.getName(), element.getPaperNum()));
            } 
        }
        public void visit(Teacher element) {
            if (element.getPaperNum() > 8) {
                System.out.println(String.format(awardWords, element.getName(), element.getPaperNum()));
            } 
        }
    }
    // 抽象元素类
    abstract class Element {
        String name;
        Integer paperNum;
        Integer grade;
        public Element(String name, Integer paperNum, Integer grade) {
            this.name = name;
            this.paperNum = paperNum;
            this.grade = grade;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public Integer getPaperNum() {
            return paperNum;
        }
        public void setPaperNum(Integer paperNum) {
            this.paperNum = paperNum;
        }
        public Integer getGrade() {
            return grade;
        }
        public void setGrade(Integer grade) {
            this.grade = grade;
        }
        abstract void accept(Visitor visitor);
    }
    // 具体元素类:老师
    class Teacher extends Element {
        public Teacher(String name, Integer paperNum, Integer grade) {
            super(name, paperNum, grade);
        }
        void accept(Visitor visitor) {
            visitor.visit(this);
        }
    }
    // 具体元素类:学生
    class Student extends Element {
        public Student(String name, Integer paperNum, Integer grade) {
            super(name, paperNum, grade);
        }
        void accept(Visitor visitor) {
            visitor.visit(this);
        }
    }
    // 元素结构类
    class ObjectConstruct {
        private List<Element> elements = null;
        Visitor visitor;
        ObjectConstruct(Visitor visitor){
            elements = new ArrayList<Element>();
            this.visitor = visitor;
        }
        void add(Element element){
            elements.add(element);
        }
        void remove(Element element){
            elements.remove(element);
        }
        void traverse(){
            for (Element e:elements) {
                e.accept(visitor);
            }
        }
    }
    public static void main(String[] args) {
        BehaviourVisitor visitor = new BehaviourVisitor();
        visitor.testVisitor();
    }
    public void testVisitor(){
        Visitor paperVisitor = new PaperVisitor();
        ObjectConstruct construct = new ObjectConstruct(paperVisitor);
        Student student = new Student("cyt", 2, 85);
        Teacher teacher = new Teacher("Jin", 7, 89);
        construct.add(student);
        construct.add(teacher);
        construct.traverse();
    }
}

3.10 备忘录(Memento)模式

  • 理论
    • 概念:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态,又称快照模式;
    • 需求:当用户后悔时能撤销当前操作,使数据恢复到它原先的状态;
    • 使用场景
      • 需要保存与恢复数据的场景,如玩游戏时的中间结果的存档功能;
      • 需要提供一个可回滚操作的场景,如 Word、记事本、Photoshop,Eclipse 等软件在编辑时按 Ctrl+Z 组合键及数据库中事务操作;
  • 实现

    public class BehaviourMemento {
      // 备忘录
      class Memento {
          String state;
          Memento(String state){
              this.state = state;
          }
          public String getState() {
              return state;
          }
          public void setState(String state) {
              this.state = state;
          }
      }
      // 发起者
      class Originator {
          private String state;
          public String getState() {
              return state;
          }
          public void setState(String state) {
              this.state = state;
          }
          Memento createMemento(){
              return new Memento(state);
          }
          void storeMemento(Memento memento){
              this.setState(memento.state);
          }
      }
      // 管理者
      class CareTaker {
          private Memento memento;
          public Memento getMemento() {
              return memento;
          }
          public void setMemento(Memento memento) {
              this.memento = memento;
          }
      }
      public static void main(String[] args) {
          BehaviourMemento memento = new BehaviourMemento();
          memento.testMemento();
      }
      public void testMemento(){
          Originator originator = new Originator();
          originator.setState("初始状态");
          Memento memento = originator.createMemento();
          System.out.println(memento.getState());
          memento.setState("保存更新状态");
          originator.storeMemento(memento);
          System.out.println(memento.getState());
      }
    }
    

    3.11 解释器(Interpreter)模式

  • 理论

    • 概念:给分析对象定义一个语言,并定义该语言的文法表示,再设计一个解析器来解释语言中的句子,即用编译语言的方式来分析应用中的实例,实现了文法表达式处理的接口,该接口解释一个特定的上下文,该模式较少被使用;
    • 需求:在软件开发中,会遇到有些问题多次重复出现,而且有一定的相似性和规律性。如果将它们归纳成一种简单的语言,那么这些问题实例将是该语言的一些句子;
    • 使用场景
      • 语言的文法较为简单,且执行效率不是关键问题;
      • 问题重复出现,且可以用一种简单的语言来进行表达;
      • 一个语言需要解释执行,并且语言中的句子可以表示为一个抽象语法树的时候,如 XML 文档解释;
  • 实现 ```java import java.util.HashSet; import java.util.Set;

public class BehaviourInterpreter { // 设计一个“京津通”公交车卡的读卡器程序 // 抽象表达式类 interface AbstractExpression{ Boolean interpreter(String info); } // 终结符表达式类 class TerminalExpression implements AbstractExpression{ private Set expression = new HashSet(); TerminalExpression(String[] array){ for (String e : array) { expression.add(e); } } public Boolean interpreter(String info) { if (expression.contains(info)) { return true; } else { return false; } } } // 非终结符表达式类 class NoTerminalExpression implements AbstractExpression{ AbstractExpression city; AbstractExpression person; NoTerminalExpression(AbstractExpression city, AbstractExpression person){ this.city = city; this.person = person; } public Boolean interpreter(String info) { String[] s = info.split(“的”); return city.interpreter(s[0]) && person.interpreter(s[1]); } } // 环境类 class Context{ String[] array_city = {“北京”, “天津”}; String[] array_person = {“老人”, “妇女”, “儿童”}; AbstractExpression city_person; Context(){ AbstractExpression city = new TerminalExpression(array_city); AbstractExpression person = new TerminalExpression(array_person); city_person = new NoTerminalExpression(city, person); } Boolean freeRide(String info){ return city_person.interpreter(info); } void print(Context context, String info){ if (context.freeRide(info)) { System.out.println(info + “可以免费乘车”); } else { System.out.println(info + “不可以免费乘车”); } } } public static void main(String[] args) { BehaviourInterpreter interpreter = new BehaviourInterpreter(); interpreter.testInterpreter(); } public void testInterpreter(){ Context context = new Context(); String info1 = “北京的老人”; String info2 = “天津的青年”; context.print(context, info1); context.print(context, info2); } } 输出: 北京的老人可以免费乘车 天津的青年不可以免费乘车 ```

参考

Java设计模式:23种设计模式全面解析(超级详细)
Java 设计模式
学习并理解 23 种设计模式