UML示例图

image.png

  • 类图
    • 第一行为类名
    • 第二行为特性(字段/属性),左边的“+”表示public,“-”表示private,“#”表示protected
    • 第三行:操作(方法/行为)
    • 如果类名是斜体字则为抽象类。
  • 接口图
    • 顶端有<>
    • 第一行为接口名,第二行为接口方法
    • 棒棒糖表示法:圆圈旁边为接口名,接口方法在方法中实现类中出现。
  • 关系
    • 继承:空心三角形+实线表示
    • 实现接口:空心三角+虚线表示
    • 关联:实线箭头表示
    • 拥有:空心菱形+实线箭头表示
    • 合成(整体和部分的关系):实心菱形+实线箭头
    • 依赖:虚线箭头

      简单工厂模式/静态工厂方法模式[创建型模式]

      抽象出产品的抽象类: ```java @Getter @Setter public abstract class Operation { private Double num_a; private Double num_b; /**
  • 抽象方法 获取运算结果
  • @return
  • @throws Exception */ public abstract Double getResult() throws Exception; } 具体的产品(继承抽象类)java ppublic class AddOperation extends Operation { @Override public Double getResult() throws Exception { return getNum_a()+getNum_b(); } }

public class DivOperation extends Operation { @Override public Double getResult() throws Exception { if(getNum_b() == 0){ throw new Exception(“除数不能为零!!”); } return getNum_a()/getNum_b(); }

}

public class MulOperation extends Operation { @Override public Double getResult() throws Exception { return getNum_a()*getNum_b(); }

}

public class SubOperation extends Operation { @Override public Double getResult() throws Exception { return getNum_a() - getNum_b(); } }

  1. 工厂类:保存一个抽象产品的引用,根据不同业务生产出不同产品
  2. ```java
  3. public class OperationFactory {
  4. public static Operation operation=null;
  5. public static Operation createOperation(String ope){
  6. switch (ope){
  7. case "+":
  8. operation= new AddOperation();
  9. break;
  10. case "-":
  11. operation=new SubOperation();
  12. break;
  13. case "*":
  14. operation=new MulOperation();
  15. break;
  16. case "/":
  17. operation=new DivOperation();
  18. break;
  19. default:
  20. break;
  21. }
  22. return operation;
  23. }

测试

public class SimpleFactoryPatternTest {
    @SneakyThrows
    public static void main(String[] args) {
        Double num_1=12.0;
        Double num_2=26.0;
        String ope="-";
        Operation operation = OperationFactory.createOperation(ope);
        operation.setNum_a(num_1);
        operation.setNum_b(num_2);
        Double result = operation.getResult();
        System.out.println("result:"+result);
    }
}

总结:

  • 根据外部给定的信息,创建工厂类进行对对象的创建,无需了解如何创建如何组织,明确各自的职责利于体系结构的优化;
  • 工厂类高内聚,产品增多,工厂类也需要相应的修改,扩展性不好;
  • 简单工厂模式使用了static工厂方法,造成工厂角色无法形成基于继承的等级结构;
  • 违背了开闭原则;
  • 适合创建对象个数为少数的情况下使用。

工厂模式

  • 定义一个用于创建对象的接口,让子类决定实例化哪一个类,工厂方法使一个类的实例化延迟到其子类;
  • 不同的工厂创建出不同的产品;
  • 客户端需要决定实例化哪一个工厂来实现创建某个产品;
  • 结构图:image.png
    • IFactory:抽象工厂,提供创建产品的接口,调用者通过它访问具体工厂的工厂方法来创建产品;
    • ConcreteFactory具体工厂:实现工厂接口的抽象方法,完成具体产品的创建;
    • IProduct抽象产品:定义产品的特性和功能;
    • ConcreteProduct具体的产品:实现产品接口,最终由对应的工厂创建出来;
  • 应用例子:计算器,根据不同的运算计算出对应的结果
    • 结构图:image.png
      • IOperationFactory:运算工厂接口,定义创建运算类方法;
      • MulOperationFactory:具体乘法运算类工厂,创建乘法运算类;
      • DivOperationFactory:具体除法运算类工厂,生产除法运算类;
      • AddOperationFactory:具体加法运算类工厂,生产加法运算类;
      • SubOperationFactory:具体减法运算类工厂,生产减法运算类;
      • IOperation:运算接口,定义运算方法;
      • MulOperation:乘法运算类,实现运算方法,计算积,由MulOperationFactory创建;
      • DivOperation:除法运算类,实现运算方法,计算商,由DivOperationFactory创建;
      • AddOperation:加法运算类,实现运算方法,计算和,由AddOperationFactory创建;
      • SubOperation:减法运算类,实现运算方法,计算差,由SubOperationFactory创建;
    • 代码实现
      • 抽象工厂: ```java /**
  • @author xulihua
  • @date 2021/1/15 17:17
  • 工厂接口 / public interface IOperationFactory { /*

    • 创建运算对象方法
    • @return */ IOperation newOperation(); } ```

      • 加法工厂 ```java /**
  • @author xulihua
  • @date 2021/1/15 17:36
  • 加法运算类工厂 / public class AddOperationFactory implements IOperationFactory { /*

    • 创建加法运算类
    • @return */ @Override public IOperation newOperation() { return new AddOperation(); } } ```

      • 减法工厂 ```java /**
  • @author xulihua
  • @date 2021/1/15 17:37
  • 减法运算类工厂 / public class SubOperationFactory implements IOperationFactory { /*

    • 创建减法运算类
    • @return */ @Override public IOperation newOperation() { return new SubOperation(); } } ```

      • 乘法工厂 ```java /**
  • @author xulihua
  • @date 2021/1/15 17:32
  • 乘法运算类工厂 / public class MulOperationFactory implements IOperationFactory { /*

    • 创建乘法运算类
    • @return */ @Override public IOperation newOperation() { return new MulOperation(); } } ```

      • 除法工厂 ```java /**
  • @author xulihua
  • @date 2021/1/15 17:35
  • 除法运算类工厂 / public class DivOperationFactory implements IOperationFactory { /*

    • 创建除法运算类
    • @return */ @Override public IOperation newOperation() { return new DivOperation(); } } ```

      • 抽象运算接口 ```java /**
  • @author xulihua
  • @date 2021/1/15 17:18
  • 抽象运算 / public interface IOperation { /*

    • 计算
    • @param num_1
    • @param num_2
    • @return */ public BigDecimal getResult(double num_1,double num_2); } ```

      • 加法运算类 ```java /**
  • @author xulihua
  • @date 2021/1/15 17:28
  • 加法运算类 / @Data public class AddOperation implements IOperation { /*

    • 加法运算
    • @return */ @Override public BigDecimal getResult(double num_1,double num_2) { return new BigDecimal(num_1+num_2); } } ```

      • 减法运算类 ```java /**
  • @author xulihua
  • @date 2021/1/15 17:30
  • 减法运算类 / @Data public class SubOperation implements IOperation { /*

    • 减法运算
    • @param num_1
    • @param num_2
    • @return */ @Override public BigDecimal getResult(double num_1,double num_2) { return new BigDecimal(num_1-num_2); } } ```

      • 乘法运算类 ```java /**
  • @author xulihua
  • @date 2021/1/15 17:22
  • 乘法运算类 / @Data public class MulOperation implements IOperation{ /*

    • 乘法计算
    • @param num_1
    • @param num_2
    • @return / @Override public BigDecimal getResult(double num_1, double num_2) { return new BigDecimal(num_1num_2).setScale(2,BigDecimal.ROUND_HALF_UP); } } ```

      • 除法运算类 ```java /**
  • @author xulihua
  • @date 2021/1/15 17:26
  • 除法运算类 / @Data public class DivOperation implements IOperation { /*

    • 除法运算
    • @return */ @SneakyThrows @Override public BigDecimal getResult(double num_1,double num_2) { if (num_2 == 0){

       throw new Exception("除数不能为0");
      

      } return new BigDecimal(num_1/num_2).setScale(2,BigDecimal.ROUND_HALF_UP); } }

      
      - 测试
      ```java
      public class Client {
      public static void main(String[] args) {
       // 创建乘法工厂
       IOperationFactory factory=new MulOperationFactory();
       // 生产乘法运算类
       MulOperation operation = (MulOperation) factory.newOperation();
       // 计算结果
       BigDecimal mulResult = operation.getResult(5.6,8.8);
       System.out.println("积:"+mulResult);
       // 除法
       IOperationFactory divFactory=new DivOperationFactory();
       DivOperation div = (DivOperation) divFactory.newOperation();
       BigDecimal divResult = div.getResult(96,3);
       System.out.println("商:"+divResult);
       // 减法
       IOperationFactory subOperationFactory = new SubOperationFactory();
       SubOperation sub = (SubOperation) subOperationFactory.newOperation();
       BigDecimal subResult = sub.getResult(5, 8);
       System.out.println("差:"+subResult);
       // 加法
       IOperationFactory addOperationFactory = new AddOperationFactory();
       AddOperation add = (AddOperation) addOperationFactory.newOperation();
       BigDecimal addResult = add.getResult(8, 99);
       System.out.println("和:"+addResult);
      }
      }
      
      • 总结
      • 优点:
        • 只需要知道具体工厂的名称就可以得到所要的产品,不需要知道具体创建过程;
        • 灵活性较强,对于新产品的创建,只需要加新的产品类和工厂类即可;
        • 典型的解耦框架,高层模块只需要知道产品的抽象类,无需关心其他实现类,满足迪米特法则/依赖倒置原则和里氏替换原则;
      • 缺点
        • 类的个数容易过多,增加复杂度;
        • 增加了系统的抽象性和理解难度;
        • 抽象产品只能生产一种产品,该不足可以使用抽象工厂模式去解决;

抽象工厂模式

  • 介绍
    • 为访问类提供一个创建一系列相关或者相互依赖对象的接口,且访问类无需指定所要的产品的具体类就能得到同族的不同等级的产品的模式结构;
    • 抽象工厂模式是工厂模式的升级版本,工厂模式只生产一个等级的产品,但是抽象工厂可生产出多个等级的产品;
    • 本质是选择产品族;
    • 结构图:image.png
      • AbsractFactory:抽象工厂,提供创建产品的接口,包含多个创建产品的方法,可以创建多个不同等级的产品;里面包含所有产品创建的抽象方法;
      • ConcreteFactory:具体工厂,主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建;
      • Product:抽象产品,定义了产品的规范,描述产品的主要特性和功能,完成具体产品的创建;它们可能都有两种不同的实现;
      • ConcreteProduct:具体产品,主要是实现抽象产品所定义的接口,由具体工厂来创建,同具体工厂之间是多对一的关系;
    • 应用例子:使用不同的数据库[mysql/SqlServer],对不同的数据表[user表/department表]进行数据的插入和获取
    • 结构图:image.png
      • IFactory:抽象工厂,定义出创建具体产品的抽象方法;
      • SqlServerFactory:具体工厂,定义创建使用SqlServer的user和department产品;
      • MysqlFactory:具体工厂,定义创建使用MySql的user和department产品;
      • IUser:抽象user产品,定义user相关的操作和特性;
      • SqlServerUser:SqlServer的user产品;MySqlUser:MySql的user产品;
      • IDepartment:抽象department产品,定义department相关特性以及方法;
      • SqlServerDepartment:SqlServer的department产品;MySqlDeppartment:MySql的department产品;
    • 代码实现:
      • 抽象工厂 ```java /**
  • @author xulihua
  • @date 2021/1/18 11:10
  • 抽象工厂 / public interface IFactory { /*

    • 创建user
    • @return / IUser createUser(); /*
    • 创建department
    • @return */ IDepartment createDepartment(); } ```

      • mysql工厂 ```java /**
  • @author xulihua
  • @date 2021/1/18 11:20
  • mysql工厂 */ public class MySqlFactory implements IFactory { @Override public IUser createUser() {

      return new MySqlUser();
    

    } @Override public IDepartment createDepartment() {

      return new MySqlDepartment();
    

    } } ```

    - SqlServer工厂
    

    ```java /**

  • @author xulihua
  • @date 2021/1/18 11:18
  • sqlserver工厂 */ public class SqlServerFactory implements IFactory { @Override public IUser createUser() {

      return new SqlServerUser();
    

    } @Override public IDepartment createDepartment() {

      return new SqlServerDepartment();
    

    } } ```

    - 抽象产品user
    

    ```java /**

  • @author xulihua
  • @date 2021/1/18 11:13
  • 抽象产品user / public interface IUser { /*

    • 插入数据
    • @param user / void insert(User user); /*
    • 获取数据
    • @param id
    • @return */ User getOne(Integer id); } ```

      • 抽象产品department ```java /**
  • @author xulihua
  • @date 2021/1/18 11:14
  • 抽象产品department / public interface IDepartment { /*

    • 插入数据
    • @param department / void insert(Department department); /*
    • 获取数据
    • @param id
    • @return */ Department getOne(Integer id); } ```

      • 具体产品mysql族的user ```java /**
  • @author xulihua
  • @date 2021/1/18 11:56
  • 具体user产品[mysql] */ public class MySqlUser implements IUser { @Override public void insert(User user) {

      Common.addUser(user);
      System.out.println("MySql添加user数据:"+user);
    

    } @Override public User getOne(Integer id) {

      User user = Common.getUser(id);
      System.out.println("MySql获取id为"+id+"user数据:"+user);
      return user;
    

    } } ```

    - 具体产品mysql族的department
    

    ```java /**

  • @author xulihua
  • @date 2021/1/18 11:58
  • 具体产品department[mysql] */ public class MySqlDepartment implements IDepartment { @Override public void insert(Department department) {

      Common.addDepartment(department);
      System.out.println("MySQL添加department数据"+department);
    

    } @Override public Department getOne(Integer id) {

      Department department = Common.getDepartment(id);
      System.out.println("MySQL获取id为"+id+"的department数据:"+department);
      return department;
    

    } } ```

    - 具体产品SqlServer的user
    

    ```java /**

  • @author xulihua
  • @date 2021/1/18 11:20
  • 具体产品user[SqlServer] */ public class SqlServerUser implements IUser{ @Override public void insert(User user) {

      Common.addUser(user);
      System.out.println("插入user数据:"+user);
    

    } @Override public User getOne(Integer id) {

      User user = Common.getUser(id);
      System.out.println("获取id为"+id+"的user记录:"+user);
      return user;
    

    } } ```

    - 具体产品SqlServer的department
    

    ```java /**

  • @author xulihua
  • @date 2021/1/18 11:40
  • 具体产品department[SqlServer] */ public class SqlServerDepartment implements IDepartment{ @Override public void insert(Department department) {

     Common.addDepartment(department);
      System.out.println("SqlServer添加department数据:"+department);
    

    } @Override public Department getOne(Integer id) {

      Department department = Common.getDepartment(id);
      System.out.println("SqlServer获取id为"+id+"department数据:"+department);
      return department;
    

    } } ```

    - 测试类
    

    ```java /**

  • @author xulihua
  • @date 2021/1/18 12:04 */ public class Client { public static void main(String[] args) {
      // mysql
      IFactory factory=new MySqlFactory();
      IUser user = factory.createUser();
      User u1= new User(44,"虎虎");
      user.insert(u1);
      user.getOne(44);
      // SqlServer
      // SqlServerFactory sqlServerFactory = new SqlServerFactory();
      // IDepartment department = sqlServerFactory.createDepartment();
      // Department dep = new Department(55, "技术部");
      // department.insert(dep);
      // department.getOne(55);
    
    } } ```
  • 总结
    • 优点
      • 易于交换产品的系列,只需要在具体的工厂中进行修改即可使用不同的产品配置;
      • 可以在类内部对产品族中相关联的多等级[品种]产品共同管理,不必专门引入多个新的类来管理;
      • 具体的实例创建过程和客户端分离,客户端是通过抽象接口操作实例,产品的具体类名也被具体工厂的实现分离;
      • 增加了程序的可扩展性,增加一个新的产品族的时候,不需要修改源代码,满足开闭原则;
    • 缺点
      • 当添加一个新的产品的时候,所以的工厂类都需要进行修改,增加了系统的抽象性和理解难度;
    • 应用场景
      • 当需要创建的对象是一个系列相关联或者是相互依赖的产品族时[电器工厂中的电视机/洗衣机/空调等];
      • 系统中有多个产品族,但是每次只使用其中一个族的产品[比如某一个人只喜欢某品牌的衣服鞋子包包];
      • 系统提供了产品的类库,且所有的接口相同,客户端不依赖产品实例的创建细节和内部结构;

建造者模式

  • 介绍
    • 将复杂对象的构造与它的表示分离,使得同样的构建过程可以创建不同的表示;
    • 将一个复杂的对象分解成为多个简单的对象,然后一步一步构建而成;
    • 将变与不变相分离,即产品的组成部分是不变的,但是每一个部分是可以灵活选择的;
  • 结构图:image.png
    • Product:产品角色,是一个包含多个组成部件的复杂对象,由具体的建造者来创建各个零部件;
    • Bulider:抽象建造者,是一个包含创建产品的各个子部件的抽象方法的接口,通常包含一个返回该复杂对象的方法getResult();
    • ConcreteBulider:具体的建造者,实现Builder接口,完成复杂产品的各个部件的具体创建方法;
    • Director:指挥者,调用建造者对象中的部件构造和装配方法来完成复杂对象的创建,在指挥者中不涉及具体产品的信息,提供客户端用来创建产品的对象;负责隔离客户与对象生产的过程,负责控制产品对象的生产过程;
  • 代码实现
    • 产品类 ```java /**
  • @author xulihua
  • @date 2021/1/18 17:28
  • 产品类 / @Data public class Product { /*

    • 部件1 / private String part1; /*
    • 部件2 / private String part2; /*
    • 部件3 / private String part3; /*
    • 设置部件1
    • @param part1 / public void setPart1(String part1){ this.part1=part1; } /*
    • 设置部件2
    • @param part2 / public void setPart2(String part2){ this.part2=part2; } /*
    • 设置部件3
    • @param part3 / public void setPart3(String part3){ this.part3=part3; } /*
    • 展示产品
    • @return */ public void show(){ System.out.println(“part1:”+part1+”\n”+”part2:”+part2+”\n”+”part3:”+part3); } } ```

      • 抽象构建者 ```java /**
  • @author xulihua
  • @date 2021/1/18 17:27
  • 抽象构建者 / public abstract class Builder { /*

    • 产品 / protected Product product =new Product(); /*
    • 构建产品部件1 / abstract void builderPart1(); /*
    • 构建产品部件2 / abstract void builderPart2(); /*
    • 构建产品部件3 / abstract void builderPart3(); /*
    • 获取产品
    • @return */ Product getResult(){ return product; } } ```

      • 具体构建者 ```java /**
  • @author xulihua
  • @date 2021/1/18 17:39
  • 具体的构建者 */ public class ConcreteBuilder extends Builder{ @Override void builderPart1() {

      product.setPart1("部件1");
    

    } @Override void builderPart2() {

      product.setPart2("部件2");
    

    } @Override void builderPart3() {

      product.setPart3("部件3");
    

    } } ```

    • 指挥者 ```java /**
  • @author xulihua
  • @date 2021/1/18 17:43
  • 指挥者 / public class Director { /*

    • 建造者 / private Builder builder; public Director(Builder builder){ this.builder=builder; } /*
    • 通过建造者构建产品
    • @return */ public Product construct(){ builder.builderPart1();; builder.builderPart2(); builder.builderPart3(); return builder.getResult(); } } ```

      • 测试类 ```java /**
  • @author xulihua
  • @date 2021/1/18 17:45 */ public class Client { public static void main(String[] args) {
      // 创建建造者
      Builder builder=new ConcreteBuilder();
      // 指挥者
      Director director=new Director(builder);
      // 指挥者通过建造者构建出产品
      Product product = director.construct();
      // 具体产品
      product.show();
    
    } } ```
  • 应用实例:在kfc选餐的时候,可以选择不同的组合,然后形成对应的套餐,选餐的时候,汉堡和薯条是必选的,可乐,鸡腿,披萨是选点的,通过建造者模式进行对套餐的组合
  • 结构图:image.png
    • Kfc:具体的食物类[产品]
    • Builder:抽象建造者,定义构建鸡腿,可乐,披萨,获取套餐结果的方法;
    • ConcreteBulider1/ConcreteBulider2:具体的建造者,实现抽象建造者中的抽象方法,具体实现构建方法;
    • Director:指挥者,指挥建造者对产品进行构建;
  • 代码实现
    • Kfc产品类 ```java /**
  • @author xulihua
  • @date 2021/1/19 11:10
  • 具体的食品 / @Data public class Kfc { /*

    • 汉堡 / private String hamburger; /*
    • 薯条 / private String chips; /*
    • 鸡腿 / private String chicken; /*
    • 披萨 / private String pizza; /*
    • 可乐 / private String cola; public Kfc(){} /*
    • 必选组合
    • @param hamburger
    • @param chips / public Kfc(String hamburger,String chips){ this.hamburger=hamburger; this.chips=chips; } /*
    • 设置鸡腿
    • @param chicken / public void setChicken(String chicken){ this.chicken=chicken; } /*
    • 设置可乐
    • @param cola / public void setCola(String cola){ this.cola = cola; } /*
    • 设置披萨
    • @param pizza */ public void setPizza(String pizza) { this.pizza = pizza; } } ```

      • 抽象建造者 ```java /**
  • @author xulihua
  • @date 2021/1/19 11:19
  • 抽象建造者:定义构建产品部件的方法 / public interface Builder { /*

    • 构建鸡腿 / void buildChicken(); /*
    • 构建可乐 / void buildCola(); /*
    • 构建披萨 / void buildPizza(); /*
    • 获取套餐
    • @return */ Kfc getResult(); } ```

      • 具体建造者1 ```java /**
  • @author xulihua
  • @date 2021/1/19 11:22
  • 具体的建造者1,组装套餐1 */ public class ConcreteBuilder1 implements Builder { // 必选项 private Kfc kfc=new Kfc(“香辣鸡腿汉堡”,”小薯条”); @Override public void buildChicken() {

      kfc.setChicken("大鸡腿");
    

    } @Override public void buildCola() {

      kfc.setCola("百事可乐");
    

    } @Override public void buildPizza() {

      kfc.setPizza(null);
    

    } @Override public Kfc getResult() {

      return kfc;
    

    } } ```

    • 具体建造者2 ```java /**
  • @author xulihua
  • @date 2021/1/19 11:22 具体建造者2,组装套餐2 / public class ConcreteBuilder2 implements Builder { // 必选项 private Kfc kfc=new Kfc(“藤椒鸡腿汉堡”,”大薯条”); @Override public void buildChicken() {

      kfc.setChicken(null);
    

    } @Override public void buildCola() {

      kfc.setCola("可口可乐");
    

    } @Override public void buildPizza() {

      kfc.setPizza("榴莲披萨");
    

    } @Override public Kfc getResult() {

      return kfc;
    

    } } ```

    • 指挥者 ```java /**
  • @author xulihua
  • @date 2021/1/19 11:35
  • 指挥者,根据建造者构建出产品 / public class Director { /*
    • 建造者 */ private Builder builder;
public Director() {}
public Director(Builder builder) {
    this.builder = builder;
}
/**
 * 构建套餐
 * @return
 */
public Kfc build() {
    builder.buildChicken();
    builder.buildCola();
    builder.buildPizza();
    return builder.getResult();
}

} ○ 测试类 /**

  • @author xulihua
  • @date 2021/1/19 11:39 */ public class Client { public static void main(String[] args) {
      // 使用建造者1构建出套餐1
      Builder builder=new ConcreteBuilder1();
      Director director=new Director(builder);
      Kfc kfc1 = director.build();
      System.out.println("套餐1:"+kfc1+"-------------------");
      // 使用建造者2构建出套餐2
      Builder builder1 =new ConcreteBuilder2();
      Director director1=new Director(builder1);
      Kfc kfc2 = director1.build();
      System.out.println("套餐2:"+kfc2+"-------------------");
    
    } } ```
  • 总结
    • 优点
      • 封装性好,构建与表示分离;
      • 扩展性好,具体的构建者之间相互独立,有利于系统的解耦;
      • 客户端不知道产品内部的具体构成,便于控制细节风险;
    • 缺点
      • 如果产品内部发生变化,相应的建造者都要进行相应的修改,后期的维护成本较大
      • 产品的组成部分必须相同,这将限制了使用范围;
    • 建造者模式就是对构建的过程进行封装,并且构建过程可以有不同的构建方式,根据这些构建方式得到不同展示的产品,产品的部件是不变的,构建过程是可变的,根据可变的构建获得最终不变的产品;

装饰者模式

  • 允许一个现有的对象添加新的功能,同时又不改变其结构,动态地将责任附加到对象上,解决子类膨胀问题,比生成子类更加灵活。
  • 原来已有的功能上动态添加更多功能,向旧的类中添加新的代码,这些新的代码通常装饰了原有类的核心职责或主要行为;
  • 结构图:image.png
    - Component:抽象构件
    - ConcreteComponent:具体构件
    - Decorator:抽象装饰者
    - ConcreteDecorator:具体装饰者
    - 装饰者和被装饰者都有相同的超类型;
    - 一个对象可以被多个装饰者包装;
    - 每个组件都可以单独使用,或者被装饰者包起来使用;
    - 装饰者有一个实例变量来保存某个Component引用;
    - ConcreteDecorator有一个实例变量,记录所装饰的事物;
    
    • 咖啡馆例子:
      • 在咖啡馆订单系统中,顾客可以选择饮料(深培咖啡/浓缩咖啡/低咖啡因咖啡/)的同时可以加入不同的调料(摩卡/奶泡/豆浆),根据下单的饮料和调料计算出最终的价格.
      • 设计图:image.png
        • Beverage作为超类(抽象类),定义了饮料名称属性,获取饮料名称以及饮料的收费方法;
        • 饮料(深培咖啡/低咖啡因咖啡/浓缩咖啡/)继承于Beverage饮料类,分别重写cost,设置自己的价格,和description(名称);
        • CondimentDecorator装饰类共同抽象类(继承于Beverage类)
        • 调料(摩卡/豆浆/奶泡)继承于CondimentDecorator类,可接收到被装饰者,也可以自己扩展Component状态,重写getDescription()和cost(),得到最终装饰后的饮料以及价格;
    • 代码实现
      • 饮料抽象类 ```java /**
  • @author xulihua
  • @date 2021/1/6 14:12
  • 相当于Component */ public abstract class Beverage{ public String description=”unKnown…”; public String getDescription(){

     return description;
    

    } /**

    • 返回饮料价格
    • @return */ public abstract BigDecimal cost(); } ```

      • 深培咖啡类
        ```java /**
  • @author xulihua
  • @date 2021/1/6 14:18
  • 具体对象:相当于ConcreteComponent */ public class DarkRoast extends Beverage { public DarkRoast(){

      description="DarkRoast...深培咖啡";
    

    } /**

    • 深培咖啡类价格
    • @return */ @Override public BigDecimal cost() { return new BigDecimal(“3.0”); } } ```

      • 低咖啡因咖啡

/
@author xulihua
@date 2021/1/6 14:23
具体对象:相当于ConcreteComponent
/
public class Decaf extends Beverage {
public Decaf(){
description=”Decaf…低咖啡因咖啡”;
}
/

低咖啡因咖啡价钱
@return
*/
@Override
public BigDecimal cost() {
return new BigDecimal(“4.0”);
}
}

  - 浓缩咖啡

/
@author xulihua
@date 2021/1/6 14:25
具体对象:相当于ConcreteComponent
/
public class Espresso extends Beverage {
public Espresso(){
description=”Espresso…浓缩咖啡”;
}
/

浓缩类咖啡价格
@return
*/
@Override
public BigDecimal cost() {
return new BigDecimal(“5.0”);
}
}

  - 装饰者抽象类[调料抽象类]

/
@author xulihua
@date 2021/1/6 14:28
装饰者抽象类
/
public abstract class CondimentDecorator extends Beverage {
/

所有的调料装饰者必须重新实现getDescription()
@return
*/
@Override
public abstract String getDescription() ;
}

  - 摩卡调料类[装饰者]

/
@author xulihua
@date 2021/1/6 14:35
摩卡调料
/
public class Mocha extends CondimentDecorator {
/

被装饰者
/
Beverage beverage;
public Mocha(Beverage beverage){
this.beverage=beverage;
}
/
摩卡调料
@return
*/
@Override
public String getDescription() {
return beverage.getDescription()+”,Mocha摩卡”;
}
/

原来的饮料加上摩卡调料(原来的饮料被摩卡调料修饰)
价格是原来饮料价格加上摩卡的价格
@return
/
@Override
public BigDecimal cost() {
return beverage.cost().add(new BigDecimal(“0.5”));
}
}

  - 豆浆调料类[装饰者]

/
@author xulihua
@date 2021/1/6 14:41
豆浆调料
/
public class Soy extends CondimentDecorator {
/

被修饰者(原来的饮料)
/
Beverage beverage;
/
构造初始化饮料变量
@param beverage
*/
public Soy(Beverage beverage){
this.beverage=beverage;
}
/

豆浆调料
@return
/
@Override
public String getDescription() {
return beverage.getDescription()+”,Soy…豆浆调料”;
}
/**
原来的饮料加上豆浆调料(原来的饮料被豆浆修饰)
价格为原来的饮料价格加上豆浆的价格
@return
*/
@Override
public BigDecimal cost() {
return beverage.cost().add(new BigDecimal(“0.1”));
}
}

  - 奶泡调料类

/
@author xulihua
@date 2021/1/6 14:44
奶泡调料
/
public class Whip extends CondimentDecorator{
/

原来饮料:被装饰者
/
Beverage beverage;
/
构造初始化饮料变量
@param beverage
*/
public Whip(Beverage beverage){
this.beverage=beverage;
}
/

原来的饮料描述加上奶泡描述
@return
*/
@Override
public String getDescription() {
return beverage.getDescription()+”,Whip…奶泡”;
}
@Override
public BigDecimal cost() {
return beverage.cost().add(new BigDecimal(“0.8”));
}
}

  - 模拟下单

/*
@author xulihua
@date 2021/1/6 15:00
/
public class StarbuzzCoffee {
public static void main(String[] args) {
//创建深培咖啡
Beverage beverage=new DarkRoast();
// 不加任何调料
System.out.println(“下单:”+beverage.getDescription()+”—————-收费:”+beverage.cost()+”元”);
System.out.println(“————————-“);
Beverage beverage1=new Espresso();
beverage1=new Mocha(beverage1);
beverage1=new Mocha(beverage1);
beverage1 = new Whip(beverage1);
//浓缩类咖啡加两份摩卡和一份奶泡
System.out.println(“下单:”+beverage1.getDescription()+”—————-收费:”+beverage1.cost()+”元”);
System.out.println(“————————-“);
Beverage beverage2=new Decaf();
beverage2=new Soy(beverage2);
beverage2 = new Mocha(beverage2);
beverage2 = new Whip(beverage2);
// 低咖啡因咖啡+豆浆+摩卡+奶泡
System.out.println(“下单:”+beverage2.getDescription()+”—————-收费:”+beverage2.cost()+”元”);
}
}

  • 总结
    • 利用抽象类/接口的方式,抽象类/接口之间相互继承,后面的装饰者继承/实现对应的顶级抽象类/接口,根据上层基类实例化对应的被装饰者;
    • 优点:装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能;
    • 缺点:多层装饰比较复杂;
    • 使用场景:扩展一个类的功能;动态撤销,动态添加功能。

策略模式

  • UML类图
    - ![image.png](https://cdn.nlark.com/yuque/0/2022/png/2787894/1655196488498-a83d75eb-a2c0-4549-ab60-46d8fcf6787f.png#clientId=u42253b36-3f38-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=ufedd2167&margin=%5Bobject%20Object%5D&name=image.png&originHeight=292&originWidth=570&originalType=url&ratio=1&rotation=0&showTitle=false&size=15873&status=done&style=none&taskId=uf4bcc63d-60c1-4960-94a4-f8555cd65c8&title=)
    - Strategy抽象策略角色:策略类,通常是一个接口或者是抽象类,定义所支持算法的公共接口;
    - ConcreteStrategyA具体策略角色:包装了相关的算法和行为,继承/实现于Strategy;
    - Context上下文角色:用一个ConcreteStrategy来配置,持有一个策略类的引用,最终给客户端调用;起承上启下的作用,屏蔽高层模块对策略/算法的直接访问,把可能存在的变化封装起来;
    - 设计原则:封装变化部分;多用组合,少用继承;针对接口编程,而不是针对实现编程;
    
  • 结合简单工厂实现策略者模式
    - UML类图:![image.png](https://cdn.nlark.com/yuque/0/2022/png/2787894/1655196488531-419320b6-1e13-440f-98dd-6972e537c84d.png#clientId=u42253b36-3f38-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=ud78aedd5&margin=%5Bobject%20Object%5D&name=image.png&originHeight=335&originWidth=950&originalType=url&ratio=1&rotation=0&showTitle=false&size=28340&status=done&style=none&taskId=udc20eb8e-2bd9-4ac0-9da0-4f455854e43&title=)
    - 思路:
       - 把计算类抽成一个抽象类,封装计算方法;
       - Context根据传递的策略辨识,加载相应的策略类,同时根据传递过来的值设置给具体的策略类;
       - 具体的计算类实现策略类的计算方法,进行自己的算法计算;
    - 代码实现
       - Strategy
    

@Setter
@Getter
public abstract class OperationStrategy {
/
数据1
/
double num_1;
/

数据2
/
double num_2;
/*
抽象算法方法
@return
/
public abstract BigDecimal getResult();
}

     - Context

/
@author xulihua
@date 2021/1/7 11:37
上下文对象
/
public class OperationContext {
/

声明一个策略对象
/
OperationStrategy operationStrategy = null;
/*
根据传递过来的字符串加载不同的策略具体类

@param type
/
public OperationContext(String type, double num_1, double num_2) {
switch (type) {
case “-“:
operationStrategy = new SubOperationStrategy();
break;
case “+”:
operationStrategy = new AddOperationStrategy();
break;
case “
“:
operationStrategy = new MulOperationStrategy();
break;
case “/“:
operationStrategy = new DivOperationStrategy();
break;
default:
System.out.println(“非法操作!!”);
break;
}
operationStrategy.setNum_1(num_1);
operationStrategy.setNum_2(num_2);
}
public BigDecimal getResult() {
return operationStrategy.getResult();
}
}

     - 具体的策略实现类

/
@author xulihua
@date 2021/1/7 10:55
加法策略实现类
/
public class AddOperationStrategy extends OperationStrategy{
@Override
public BigDecimal getResult() {
return new BigDecimal(getNum_1() + getNum_2());
}
}
———————————————————————-
/

@author xulihua
@date 2021/1/7 11:25
除法策略实现类
/
public class DivOperationStrategy extends OperationStrategy {
@Override
public BigDecimal getResult() {
if (getNum_2() == 0){
try {
throw new Exception(“除数不能为0”);
} catch (Exception e) {
e.printStackTrace();
}
}
return new BigDecimal(getNum_1() / getNum_2()).setScale(2,BigDecimal.ROUND_HALF_UP);
}
}
———————————————————————
/
@author xulihua
@date 2021/1/7 11:36
乘法策略实现类
/
public class MulOperationStrategy extends OperationStrategy {
@Override
public BigDecimal getResult() {
// 保留两位小数
return new BigDecimal(getNum_1() * getNum_2()).setScale(2,BigDecimal.ROUND_HALF_UP);
}
}
———————————————————————-
/

@author xulihua
@date 2021/1/7 11:24
减法策略实现类
/
public class SubOperationStrategy extends OperationStrategy {
@Override
public BigDecimal getResult() {
return new BigDecimal(getNum_1() - getNum_2());
}
}

     - 测试

/
@author xulihua
@date 2021/1/7 10:16
*/
public class Demo2Test {
public static void main(String[] args) {
/

算法符号
/
String type=”“;
/**
数据1
/
double num_1=5.60;
/**
数据2
/
double num_2=6.60;
// 通过上下文对象加载到相应的策略
OperationContext context=new OperationContext(type,num_1,num_2);
// 计算结果
BigDecimal result = context.getResult();
System.out.println(“计算结果为:”+result);
}
}
*代理模式

  • 一个类代表另一个类的功能,属于结构型模式
  • 为其他对象提供一个代理以控制这个对象的访问
  • 创建一个具有现有对象的对象,以便于像外界提供功能接口
  • 结构图:image.png
    • Subject:抽象角色:通过接口/抽象类去声明真实角色实现的业务方法;
    • Proxy代理角色:实现抽象角色,真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并且可以附加自己的操作;
    • RealSubject真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用;
  • 主要解决:直接访问某个对象会带来很多麻烦,或者访问的这个对象创建的开销比较大,或者某些操作需要安全控制。
  • 应用场景:
    • 远程代理:为一个对象在不同的 地址空间提供局部代表,隐藏了对象存在于不同地址的事实;
    • 虚拟代理:当一个对象创建的开销比较大时,通过代理来存放这个实例化需要很长时间的真实对象;
    • 安全代理:用于控制真实对象的访问权限;
    • 智能指引:调用真实对象时,代理可以去做其他的事情。
  • 代码实现
    • 公共接口

/
@author xulihua
@date 2021/1/11 10:16
公共接口
/
public interface Operation {
/

计算方法
@return
*/
public BigDecimal getResult();
}

  • 真实实体

/
@author xulihua
@date 2021/1/11 10:18
真实的实体
/
public class MulOperation implements Operation {
/

数据1
/
double num_1;
/
数据2
/
double num_2;
/

乘法计算
@return
/
public MulOperation(double num_1,double num_2){
this.num_1=num_1;
this.num_2=num_2;
}
public MulOperation(){}
@Override
public BigDecimal getResult() {
// 结果保留两位小数
return new BigDecimal(num_1
num_2).setScale(2,BigDecimal.ROUND_HALF_UP);
}
}

  • 代理类

/*
@author xulihua
@date 2021/1/11 10:24
代理类
*/
public class ProxyOperation implements Operation {
MulOperation mulOperation;
double num_1;
double num_2;
public ProxyOperation(double num_1,double num_2){
this.num_1=num_1;
this.num_2=num_2;
}
public ProxyOperation(){}
@Override
public BigDecimal getResult() {
if (mulOperation == null){
mulOperation=new MulOperation(num_1,num_2);
}
return mulOperation.getResult();
}
}

  • 测试

/*
@author xulihua
@date 2021/1/11 10:32
测试
*/
public class ProxyTest {
public static void main(String[] args) {
// 通过创建代理类
Operation operation=new ProxyOperation(5.6,8.8);
// 调用计算方法
BigDecimal result = operation.getResult();
System.out.println(“计算结果:”+result);
}
}

  • 静态代理:就是在编译的时候就已经确定了代理类与被代理类的关系(上面的代码实现的就是静态代理的过程)
    - 代理类和真实类都是实现同一个接口
    - 代理可以起到保护真实对象的作用
    - 代理可以对目标对象的功能进行扩展
    - 本质是直接调用真实的类去实现接口定义的方法
    - 缺点:由于代理类和真实类都要实现同一个接口,所以会有很多代理类,随着接口的增多,目标对象和代理对象都要进行维护(静态代理的缺点可以用动态代理去解决)
    
  • 动态代理
    • 代理类在程序运行时动态创建,不需要在代码中编写代理类
    • 方便对代理类的统一管理,不需要修改每一个代理类的函数
    • 动态代理方式:
      • JDK代理:需要实现接口(InvocationHandler ),通过实现该接口定义横切逻辑,并且通过反射机制调用目标类的方法代码,动态地将横切逻辑和业务逻辑编制在一起。
        • 在运行时,根据一组定义好的接口,使用Proxy 、 InvocationHandler等工具类去生成一个代理类和代理类实例;image.png
        • jdk动态代理实现接口的实现方法的逻辑是由InvocationHandler实例的invoke()方法去实现决定的;
        • 直接在运行的时候生成类的字节码,并且载入到虚拟机执行,不存在class文件;
        • 结构图:image.png
          • InvocationHandler接口中的invoke()方法
            • Object proxy:表示的是代理实例
            • method:表示调用方法
            • args()为调用方法的参数数组
          • Proxy:动态代理类,通过newProxyInstance(ClassLoader,Class[],InvocationHandler)返回代理类的一个实例
            • ClassLoader: subjectProxy.getClass().getClassLoader():代理类的加载器,加载这个代理类;
            • Class[]: subject.getClass().getInterfaces():被代理类的接口,如果有多个可以是数组的形式;
            • InvocationHandler: InvocationHandler实例;传入代理类实例subjectProxy;
        • 代码实现
          • 接口

/
@author xulihua
@date 2021/1/11 10:16
公共接口
/
public interface Operation {
/

计算方法
@return
*/
public BigDecimal getResult();
}

        - 实现类,具体的目标对象

public class MulOperation implements Operation {
/
数据1
/
double num_1;
/

数据2
/
double num_2;
/*
乘法计算
@return
/
public MulOperation(double num_1,double num_2){
this.num_1=num_1;
this.num_2=num_2;
}
public MulOperation(){}
@Override
public BigDecimal getResult() {
// 结果保留两位小数
return new BigDecimal(num_1 * num_2).setScale(2,BigDecimal.ROUND_HALF_UP);
}
}

        - 代理类:创建一个实现InvocationHandler接口的类,实现invoke方法

/*
@author xulihua
@date 2021/1/11 14:53
jdk动态代理
*/
public class JDKProxy implements InvocationHandler {
// 目标对象的引用
private Object operation;

public JDKProxy(Operation operation){<br />        this.operation=operation;<br />    }<br />    @Override<br />    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {<br />        System.out.println("开始----------");<br />        // 利用反射调用类里面的实际方法<br />        Object invoke = method.invoke(operation, args);<br />        System.out.println("结束----------结果:"+invoke);<br />        return invoke;<br />    }<br />}

        - 测试

public class ProxyTest {
public static void main(String[] args) {
/*JDK动态代理模式/
// 创建被代理的类
Operation operation=new MulOperation(5.6,8.8);
// 获取代理类
InvocationHandler proxy=new JDKProxy(operation);
// 通过Proxy的静态方法newProxyInstance创建代理实例
Operation proxyInstance = (Operation) Proxy.newProxyInstance(proxy.getClass().getClassLoader(), operation.getClass().getInterfaces(), proxy);
// 通过代理调用方法
BigDecimal result = proxyInstance.getResult();
System.out.println(“计算结果:”+result);
}
}

     - 总结:
        - 创建一个含业务方法的接口
        - 创建被代理的类
        - 创建一个实现InvocationHandler接口的处理器,实现invoke方法
        - 通过Proxy的静态方法Proxy.newProxyInstance(proxy.getClass().getClassLoader(), operation.getClass().getInterfaces(), proxy)创建一个代理实例;指定ClassLoader和一组interface来创建动态代理;
           - 通过反射机制获取动态代理类的构造方法,其唯一参数类型为调用的处理器接口类型;
           - 通过构造函数创建动态代理的实例,构造时调用处理器对象作为参数传入
        - 通过代理实例去调用方法
        - JDK动态代理是通过反射机制去生成代理类实例
        - newProxyInstance详细解读

public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
//如果h为空将抛出异常
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();//拷贝被代理类实现的一些接口,用于后面权限方面的一些检查
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
//在这里对某些安全权限进行检查,确保我们有权限对预期的被代理类进行代理
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/
下面这个方法将产生代理类
/
Class<?> cl = getProxyClass0(loader, intfs);
/

使用指定的调用处理程序获取代理类的构造函数对象
/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
// 通过反射获取代理类的构造器
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
//假如代理类的构造函数是private的,就使用反射来set accessible
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
//根据代理类的构造函数来生成代理类的对象并返回
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}

  • cglib动态代理
    • 底层是借助ASM (java字节码生成框架)
    • 代理类对象是由Enhancer类创建的,(Enhance是cglib的字节码增强器),可以方便对类的扩展
    • 创建代理类对象的过程
      • 生成代理类的二进制字节码文件
      • 加载字节码文件,生成Class对象
      • 通过反射机制获取构造器,生成代理类实例
    • 对委托类进行处理
      • 生成代理类,如果委托类被final修饰,那么不可被继承,不可被代理,方法亦是一样的道理;
      • 委托类会创建两个委托方法,其中一个是重写了之前父类的方法,另一个是自己的方法,但是方法里面还是调用了父类方法;
      • 在代理对象执行目标方法的时候,先去判断是否实现类MethodInterceptor接口,如果实现了就直接调用intercept方法;
    • 代码实现
      • 导入依赖



cglib
cglib
3.3.0

     - 目标类

/
@author xulihua
@date 2021/1/12 14:36
目标对象
/
@Data
public class Operation {
public Operation(){
}
/


@param num_1
@param num_2
@return
/
public BigDecimal add(double num_1,double num_2){
return new BigDecimal(num_1+num_2);
}
/**

@param num_1
@param num_2
@return
/
public BigDecimal mul(double num_1,double num_2){
return new BigDecimal(num_1 num_2).setScale(2,BigDecimal.ROUND_HALF_UP);
}
/**

@param num_1
@param num_2
@return
/
@SneakyThrows
public BigDecimal div(double num_1, double num_2){
if (num_2 == 0){
throw new Exception(“非法操作!!”);
}
return new BigDecimal(num_1/num_2).setScale(2,BigDecimal.ROUND_HALF_UP);
}
}

     - 代理处理器

/
@author xulihua
@date 2021/1/12 14:41
处理器 :实现MethodInterceptor接口,重写intercept方法
/
@Slf4j
public class OperationProxy implements MethodInterceptor {
public Object getProxy(Class cla){
// 生成字节码文件后的输出位置
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,”F:\XLH\java学习\JavaStudy”);
// cglib字节码增强器(用于扩展委托类)
Enhancer enhancer=new Enhancer();
// 设置超类(cglib的底层为继承目标类的方式生成对应的代理类,并且对目标类进行增强[前置方法,后置方法])
enhancer.setSuperclass(cla);
// 回调方法,被代理类的public的非final方法被调用时,触发的处理类
enhancer.setCallback(this);
// 动态创建代理类
return enhancer.create();
}
/

拦截方法
@param o :目标类的实例对象
@param method :目标方法的反射对象
@param objects :方法的参数
@param methodProxy :代理类的实例对象
@return 代理调用方法后的方法返回值
@throws Throwable
/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
log.info(“代理介入开始—————“);
Object res = methodProxy.invokeSuper(o, objects);
log.info(“方法返回值:”+res);
log.info(“代理介入结束—————“);
return res;
}
}

     - 测试类

/*
@author xulihua
@date 2021/1/12 14:54
测试
*/
public class OperationTest {
public static void main(String[] args) {
// 创建处理类
OperationProxy operation=new OperationProxy();
// 获取代理对象
Operation proxy = (Operation) operation.getProxy(Operation.class);
// 使用代理进行方法调用
BigDecimal add = proxy.add(88, 136);
System.out.println(“add结果—-“+add);
BigDecimal mul = proxy.mul(5, 9);
System.out.println(“mul结果—-“+mul);
BigDecimal div = proxy.div(99, 3);
System.out.println(“div结果—-“+div);
}
}

  - 总结
     - 无需实现委托类的接口,没有jdk必须实现接口的局限性;
     - 可以在运行时对类进行增强操作;
     - 但是底层是通过继承委托类生产代理类的,所以final类以及final方法都不能被代理;

责任链模式[行为型模式]

  • 介绍
    • 为请求接收者创建一个处理者的链路;
    • 避免请求发送者和多个请求处理者的耦合在一起;
    • 在上一个处理者对象保存下一个处理者对象的引用,形成一个链路;
    • 请求会自动进行传递,知道找到合适的处理者对请求进行处理,不需要关心请求过程和请求的传递过程
    • 客户端只需要将请求发送到责任链上即可;
    • 责任链模式主要是解耦请求和处理,将处理者形成一个链状结构,并且允许节点是否进行请求,否则就将请求传给下一个节点;
  • 结构图:image.png
    • Handler:抽象处理者,定义一个请求处理的接口,包含抽象的处理方法和一个后续的连接;
    • Handler1/Handler2:具体的处理者,实现抽象处理者的处理方法,判断当前处理对象是否满足条件进行处理请求,否则交给下一个处理对象;
    • client:客户端,创建处理链,指定处理者的下一个处理对象,不关心请求处理过程和传递过程,让处理者去处理请求;
  • 应用实例:模拟学校的请假流程
    • 分析:学生请假2天以内,包括2天,班主任批假即可;请假3-5天以内需要找系主任批假;请假6-10天需要找院长批假;请假11-20天需要找教务处长批假,超过20天不予以批假.
    • UML类图
      • image.png
      • Leader作为抽象的领导处理类:定义了各个处理者能处理的条件,然后在里面创建处理者链式处理方法;并且定义抽象的处理请求方法;
      • ClassAdviser:班主任处理类:只处理请假2天以内的请求,并且初始化的时候设置下一个处理者DepatmentHead;
      • DepatmentHead:系主任处理类,只处理请假3-5天的情况,初始化的时候设置下一个处理者Dean;
      • Dean院长处理类:只处理6-10天的情况,初始化的时候设置下一个处理类DeanOfStudies;
      • DeanOfStudies教务处长处理类:只处理11-20天的请假,初始化的时候设置下一个处理类为null[需扩展的时候直接扩展即可];
    • 代码实现
      • Leader:

/
@author xulihua
@date 2021/1/13 9:50
领导抽象类
/
public abstract class Leader {
/

班主任处理请假天数
/
public static int classAdviser = 2;
/
系主任处理请假天数
/
public static int departmentHead = 5;
/

院长处理请假天数
/
public static int dean = 10;
/
教务处长处理请假天数
/
public static int deanOfStudies = 20;
/

请假天数
/
int leaveDay;
/
下一个处理者
/
private Leader next;
/

设置处理者

@return
/
public void setNext(Leader leader) {
this.next = leader;
}
/
获取下一个处理者

@return
/
public Leader getNext() {
return next;
}
/

处理传递过来的请求

@param leaveDay
/
public void handleManager(int leaveDay) {
if (this.leaveDay >= leaveDay) {
handle(leaveDay);
return;
}
if (Objects.nonNull(getNext())) {
next.handleManager(leaveDay);
} else {
System.out.println(“请假天数过多,不通过!”);
}
}
/*
请假处理方法

@param leaveDay
*/
public abstract void handle(int leaveDay);
}

  - 班主任

/*
@author xulihua
@date 2021/1/13 10:04
班主任
*/
public class ClassAdviser extends Leader {
public ClassAdviser(){
this.leaveDay=classAdviser;
}
@Override
public void handle(int leaveDay) {
System.out.println(“班主任批准了你的请假,请假”+leaveDay+”天”);
}
}

  - 系主任

/*
@author xulihua
@date 2021/1/13 10:06
系主任
*/
public class DepartmentHead extends Leader{
public DepartmentHead(){
this.leaveDay=departmentHead;
}
@Override
public void handle(int leaveDay) {
System.out.println(“系主任处理的你的请假,请假”+leaveDay+”天”);
}
}

  - 院长

/*
@author xulihua
@date 2021/1/13 10:09
院长
*/
public class Dean extends Leader {
public Dean(){
this.leaveDay=dean;
}
@Override
public void handle(int leaveDay) {
System.out.println(“院长处理了你的请假,请假”+leaveDay+”天”);
}
}

  - 教务处长

public class DeanOfStudies extends Leader {
public DeanOfStudies(){
this.leaveDay=deanOfStudies;
}
@Override
public void handle(int leaveDay) {
System.out.println(“教务处长处理了你的请假,请假”+leaveDay+”天”);
}
}

  - 测试类

public class ChainTest {
public static void main(String[] args) {
// 声明一个最低层的处理者,并且指定处理者的下一个节点
Leader classAdviser = new ClassAdviser();
DepartmentHead departmentHead = new DepartmentHead();
classAdviser.setNext(departmentHead);
Dean dean = new Dean();
departmentHead.setNext(dean);
DeanOfStudies deanOfStudies = new DeanOfStudies();
dean.setNext(deanOfStudies);
// 发送请求
classAdviser.handleManager(1);
System.out.println(“————————-“);
classAdviser.handleManager(3);
System.out.println(“————————-“);
classAdviser.handleManager(7);
System.out.println(“————————-“);
classAdviser.handleManager(11);
System.out.println(“————————-“);
classAdviser.handleManager(100);
}
}

  • 总结
    • 优点:
      • 降低对象之间的耦合,发送者和接收者都无需持有对方的明确信息;
      • 增加系统的扩展性,可以根据需要进行添加处理类,符合开闭原则;
      • 增强对象之间指派任务的灵活性,当流程发送改变的时候,可以对链路内的成员或者调用他们的次序,动态改变成员之间的链路指向即可;
      • 职责分担,每个处理者只关注于自己的职责,符合类单一职责原则;
    • 缺点
      • 由于请求者没有明确处理者,有可能请求得不到处理;
      • 存在链路,当处理者比较多的情况下,系统的效率会降低;

状态模式[行为型模式]

  • 定义:
    • 对有状态的对象,把复杂的”判断逻辑”抽取到不同的状态对象中,允许状态对象在其内部状态改变时改变其行为;
    • 不同的状态有不同的行为;
  • 解决思路:
    • 当控制一个对象状态转换的条件表达过于复杂的时候,把相关的”逻辑判断”提取出来,用各个不同的类进行表示,系统处于哪个状态情况,直接使用相应的状态类对象进行处理,消除if-else冗余语句;
    • 当一个对象的行为取决于他的状态,并且他必须在运行时刻根据状态改变其行为,可以考虑使用状态模式;
  • 结构图:image.png
    • 把受环境改变的对象的对象行为封装在不同的状态对象里,其意图是让一个对象在其内部状态改变的时候,其行为随之改变;
    • State抽象状态类:定义一个接口以封装与Context的一个特定状态相关的行为;
    • ConcreteStateA/ConcreteStateB:具体状态,每一个类实现一个与Context的一个状态相关的行为,并且在需要的情况下进行状态切换;
    • Context类:维护一个ConcreteState子类实例,这个实例定义当前的状态;
  • 应用例子:线程状态之间的转换问题,首先新建状态,然后调用start()——->就绪状态,获取到CPU时间片getCPU()—->运行状态,调用suspend()—->阻塞状态;如果调用stop()—->死亡状态;阻塞状态调用resume()—->就绪状态
    • image.png
    • uml类图:image.png
    • 代码实现
      • 抽象状态类

/
@author xulihua
@date 2021/1/13 17:00
抽象状态类
/
public abstract class ThreadState {
/

线程状态名称
/
protected String stateName;
}

  - 上下文类

/
@author xulihua
@date 2021/1/13 17:02
*/
public class ThreadContext {
/

线程状态
/
private ThreadState state;

public ThreadContext() {<br />        state = new New();<br />    }<br />    public void setState(ThreadState state) {<br />        this.state = state;<br />    }<br />    public ThreadState getState() {<br />        return state;<br />    }<br />    public void Start() {<br />        if ("新建状态".equals(state.stateName)) {<br />            ((New) state).start(this);<br />        }else {<br />            System.out.println("非新建状态不能调用start()方法");<br />        }<br />    }<br />    public void getCPU() {<br />        if ("就绪状态".equals(state.stateName)) {<br />            ((Runnable) state).getCPU(this);<br />        }else {<br />            System.out.println("非就绪状态不能调用getCPU()方法");<br />        }<br />    }<br />    public void suspend() {<br />        if ("运行状态".equals(state.stateName)) {<br />            ((Running) state).suspend(this);<br />        }else {<br />            System.out.println("非运行状态不能调用suspend()方法");<br />        }<br />    }<br />    public void resume() {<br />        if ("阻塞状态".equals(state.stateName)) {<br />            ((Blocked) state).resume(this);<br />        }else {<br />            System.out.println("非阻塞状态不能调用resume()方法");<br />        }<br />    }<br />    public void stop() {<br />        if ("运行状态".equals(state.stateName)) {<br />            ((Running) state).stop(this);<br />        }else {<br />            System.out.println("非运行状态不能调用stop()方法");<br />        }<br />    }<br />}

  - 新建状态类

/
@author xulihua
@date 2021/1/13 17:05
*/
public class New extends ThreadState {
public New(){
stateName=”新建状态”;
System.out.println(“当前线程为新建状态”);
}
/

调用start方法
@param context
*/
public void start(ThreadContext context){
System.out.println(“执行start()方法—->”);
context.setState(new Runnable());
System.out.println(“由”+stateName+”变为”+context.getState().stateName);
}
}

  - 就绪状态类

/
@author xulihua
@date 2021/1/13 17:14
*/
public class Runnable extends ThreadState{
public Runnable(){
stateName=”就绪状态”;
System.out.println(“当前线程为就绪状态”);
}
/

调用getCpu方法
@param context
*/
public void getCPU(ThreadContext context){
System.out.println(“执行getCPU()方法—->”);
context.setState(new Running());
System.out.println(“由”+stateName+”变为”+context.getState().stateName);
}
}

  - 运行状态

/
@author xulihua
@date 2021/1/13 17:15
*/
public class Running extends ThreadState{
public Running(){
stateName=”运行状态”;
System.out.println(“当前线程状态为运行状态”);
}
/

调用suspend方法
@param context
/
public void suspend(ThreadContext context){
System.out.println(“调用suspend()方法——>”);
context.setState(new Blocked());
System.out.println(“由”+stateName+”变为”+context.getState().stateName);
}
/**
调用stop方法
@param context
/
public void stop(ThreadContext context){
System.out.println(“调用stop()方法——>”);
context.setState(new Dead());
System.out.println(“线程状态由”+stateName+”变为”+context.getState().stateName);
}
}

  - 阻塞状态类

/
@author xulihua
@date 2021/1/13 17:17
*/
public class Blocked extends ThreadState {
public Blocked(){
stateName=”阻塞状态”;
System.out.println(“当前线程为阻塞状态”);
}
/

调用resume方法
@param context
*/
public void resume(ThreadContext context){
System.out.println(“调用resume()方法—->”);
context.setState(new Runnable());
System.out.println(“由”+stateName+”变为”+context.getState().stateName);
}
}

  - 死亡状态类

/*
@author xulihua
@date 2021/1/13 17:17
/
public class Dead extends ThreadState{
public Dead(){
stateName=”死亡状态”;
System.out.println(“当前线程状态为死亡状态”);
}
}

  - 测试类

/*
@author xulihua
@date 2021/1/13 17:30
/
public class StateTest {
public static void main(String[] args) {
// 创建上下文对象
ThreadContext context=new ThreadContext();
// 通过上下文调用方法,切换状态
context.Start();
context.getCPU();
context.suspend();
context.resume();
context.getCPU();
context.stop();
context.resume();
}
}

  • 总结
    • 优点
      • 结构清晰,将与特定状态相关的行为局部化成到一个状态中,并且将不同状态行为分割开来,满足单一职责原则;
      • 将状态转换显示化,减少对象之间的相互依赖,将不同的状态引入独立的对象中会让状态转换变得更加明确,减少了对象之间的相互依赖;
      • 状态类职责明确,利于程序的扩展;
    • 缺点
      • 会增加系统中类和对象的个数;
      • 结构与实现较为复杂,使用不当会引起程序结构和代码混乱;
      • 不利于开闭原则,对于可以切换状态的状态模式,在增加新的状态时候,需要修改负责切换状态的代码,同时也需要修改对应状态的行为代码;

享元模式[结构型模式]

  • 介绍
    • 运用共享技术有效地支持大量细粒度的对象的复用,通过共享已经存在的对象来大幅度减少需要创建的对象数量,避免大量类似的开销,提高系统资源的利用率;
    • 细粒度对象的信息分为两部分
      • 内部状态:对象共享出来的信息,存储在享元信息内部,并且不会随着环境的改变而变化;
      • 外部状态:(每个实例不同的参数)对象得以依赖的一个标记,随着环境的改变而改变,不可共享,就是对象独有的部分,主要是以方法参数传入;
    • 本质为缓存共享对象,降低内存消耗
  • 结构图:image.png
    • Flyweight抽象享元角色:是所有具体享元类的基类或者接口,为具体享元规范需要实现的公共接口,非享元的外部状态以参数的形式通过方法传入;通过这个接口可以接收并作用于外部状态;
    • FlyweightFactory享元工厂角色:负责创建和管理享元角色,当客户对象请求一个享元对象时,享元工厂会检查系统使用有符合的享元对象,有则提供给客户,如果没有就创建出一个新的享元对象;
    • ConcreteFlyweightA/ConcreteFlyweightB具体享元角色:实现抽象享元角色中所规定的接口;
    • UnsharedConcreteFlyweight非享元角色:是不可以共享的外部状态,它以参数的形式注入具体享元的相关方法中;
  • 代码实现
    • 非享元角色:

/*
@author xulihua
@date 2021/1/14 15:51
非享元角色
*/
@NoArgsConstructor
@AllArgsConstructor
@Setter
@Getter
public class UnsharedFlyweight {
// 非共享的外部状态
private String info;
}

  • 抽象享元角色

/
@author xulihua
@date 2021/1/14 15:49
抽象享元角色
/
public interface Flyweight {
/

各个享元角色的统一方法
@param unsharedFlyweight:非享元角色
*/
void operation(UnsharedFlyweight unsharedFlyweight);
}

  • 具体的享元角色

/
@author xulihua
@date 2021/1/14 15:57
具体的享元角色
/
public class ConcreteFlyweight implements Flyweight {
/

享元内部状态
/
private String key;
public ConcreteFlyweight(String key){
this.key=key;
System.out.println(“具体享元角色:”+key+”被创建”);
}
/*
实现统一的方法,柔和外部状态
@param unsharedFlyweight:非享元角色
/
@Override
public void operation(UnsharedFlyweight unsharedFlyweight) {
System.out.println(“具体享元:”+key+”被调用”);
System.out.println(“非享元信息为:”+unsharedFlyweight.getInfo());
}
}

  • 享元工厂

/
@author xulihua
@date 2021/1/14 16:04
享元工厂类
/
public class FlyweightFactory {
/

管理对象的map
/
public HashMap flyweights=new HashMap<>(16);
/*
获取享元对象
@param key
@return
*/
public Flyweight getFlyweight(String key){
// 获取享元对象
Flyweight flyweight = flyweights.get(key);
if (Objects.nonNull(flyweight)){
System.out.println(key+”对象已经存在,可直接获取”);
}else {
// 不存在则进行创建
flyweight = new ConcreteFlyweight(key);
flyweights.put(key,flyweight);
}
return flyweight;
}
}

  • 测试类

/*
@author xulihua
@date 2021/1/14 16:03
/
public class FlyweightTest {
public static void main(String[] args) {
// 通过工厂获取对象
FlyweightFactory factory=new FlyweightFactory();
Flyweight a = factory.getFlyweight(“a”);
Flyweight b = factory.getFlyweight(“b”);
Flyweight a1 = factory.getFlyweight(“a”);
// 调用享元对象方法,并且传入外部状态
a.operation(new UnsharedFlyweight(“第一次调用a”));
b.operation(new UnsharedFlyweight(“第一次调用b”));
}
}

  • 应用
    • 在一个应用程序里面使用了大量的对象,而这些大量的对象会造成很大的存储开销就需要考虑使用享元模式;
    • 对象的大多数状态可以外部状态,如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象,那么也可以考虑使用享元模式;
    • 使用享元模式相当于定义了一个”池”的概念,然后去管理这些享元对象,通过标识去获取对应的对象,用完之后就放回到池子里面;
    • 比如线程池/数据库连接池/字符串常量池中都使用了享元模式,来减少开销;
  • 实例:
    • 根据不同的用户,创建出用户所需要的网站,网站名称作为内部状态,网站是享元对象,用户作为外部状态,非共享部分;
    • uml类图:image.png
    • 代码实现
      • 网站接口

/
@author xulihua
@date 2021/1/15 10:35
网站接口:定义统一的方法,传入网站的外部状态:user
/
public interface Website {
/

使用网站
@param user:网站用户,传入网站的外部状态
*/
public void use(User user);
}

  - 外部状态:用户

/
@author xulihua
@date 2021/1/15 10:37
外部状态:网站的用户
/
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class User {
/

用户名
/
private String name;
/*
密码
*/
private String password;
}

  - 具体的网站

/
@author xulihua
@date 2021/1/15 10:40
具体的网站,实现网站接口
/
@Data
@NoArgsConstructor
public class ConcreteWebsite implements Website {
/

网站名称
/
private String name;

public ConcreteWebsite(String name){
this.name=name;
System.out.println(“创建网站:”+name);
}
/*
网站使用
@param user:网站用户,传入网站的外部状态
/
@Override
public void use(User user) {
System.out.println(user.getName()+”使用”+name+”网站”);
}
}

  - 网站工厂

/
@author xulihua
@date 2021/1/15 10:48
网站工厂类,管理网站
/
@Slf4j
public class WebsiteFactory {
private HashMap websites=new HashMap<>(16);
/

获取网站对象
@param name
@return
/
public Website getWebsite(String name){
Website website=null;
if (websites.containsKey(name)){
website=websites.get(name);
log.info(name+”网站被调用”);
}else {
website=new ConcreteWebsite(name);
websites.put(name,website);
log.info(“创建网站:”+name);
}
return website;
}
/*
统计网站个数
@return
/
public int countWebsite(){
return websites.size();
}
}

  - 测试

public class Client {
public static void main(String[] args) {
// 创建网站工厂
WebsiteFactory factory=new WebsiteFactory();
// 从工厂中获取博客网站
Website websiteBlog = factory.getWebsite(“博客”);
// 博客网站用户为小花
websiteBlog.use(new User(“小花”,”123654”));
// 获取新闻网站
Website websiteNew = factory.getWebsite(“新闻”);
// 新闻网站用户为小哈
websiteNew.use(new User(“小哈”,”1425”));
// 获取博客网站
Website websiteBlog2 = factory.getWebsite(“博客”);
// 博客网站的用户为小明
websiteBlog2.use(new User(“小明”,”22222”));
// 获取新闻网站
Website websiteNew2 = factory.getWebsite(“新闻”);
// 新闻网站用户为小鹏
websiteNew2.use(new User(“小鹏”,”99990”));
// 获取时尚网站
Website websiteFan = factory.getWebsite(“时尚”);
// 时尚网站用户为露西
websiteFan.use(new User(“露西”,”000987”));
// 统计网站的个数
System.out.println(“网站的个数:”+factory.countWebsite());
}
}

  • 总结
    • 优点:相同的对象只保存一份,降低了系统中对象的数量,降低了系统中细粒度对象给内存带来的压力;
    • 缺点:
      • 为了使得对象可共享,需要将一些不能共享的状态外部化,增加程序的复杂性;
      • 读取享元模式的外部状态会让运行时间较慢;

观察者模式[发布-订阅模式]

  • 介绍
    • 定义一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象.这个主题对象在状态发生改变时会通知所有注册进来的观察者对象,让他们自己更新自己;
  • 结构图:image.png
    • Subject:抽象主题:把所有对观察者对象的引用保存到一个集合中,每一个主题都可以有任何数量的观察者,抽象主题提供增加/删除观察者对象的接口;
    • Observer:抽象观察者,为所有的具体观察者定义一个接口,拿到主题的通知时更新自己;
    • ConcreteSubject:具体主题,将有关状态存入具体的观察者对象,在具体的主题内部状态发生改变时,将所有的注册的观察者发出通知;
    • ConcreteObserver:具体观察者,实现抽象观察者的更新接口,以便于本身的状态和主题状态相协调;
  • 应用实例:上班时间员工摸鱼,前台看到老板来了立即通知或者是老板进来,员工停止摸鱼,认真工作;
    • 结构图:image.png
    • 代码实现:
      • 抽象目标:

/
@author xulihua
@date 2021/1/20 11:11
抽象目标
/
@Data
public abstract class Subject {
/

状态
/
String subjectStatus;
/
注册观察者
@param observer
*/
abstract void register(Observer observer);
/

移除观察者
@param observer
/
abstract void remove(Observer observer);
/**
通知
*/
abstract void notifyObserver();
}

  - 抽象观察者

/
@author xulihua
@date 2021/1/20 11:11
抽象观察者
/
public interface Observer {
/

更新
/
void update(Subject subject);
}

  - 具体通知者1

/*
@author xulihua
@date 2021/1/20 11:12
boss通知者
*/
@Data
public class Boss extends Subject {
// 管理注册进来的观察者
private List observers =new ArrayList<>();
// 状态
private String action;
@Override
public void register(Observer observer) {
observers.add(observer);
}
@Override
public void remove(Observer observer) {
observers.remove(observer);
}
@Override
public void setSubjectStatus(String subjectStatus) {
this.subjectStatus=action;
}
@Override
public String getSubjectStatus() {
return action;
}
@Override
public void notifyObserver() {
observers.stream().forEach(observer -> observer.update(this));
}
}

  - 具体通知者2

/
@author xulihua
@date 2021/1/20 11:16
前台通知者
/
@Data
public class Secretary extends Subject {
/

管理注册上来的观察者
/
private List observers =new ArrayList<>();
/*
状态
*/
private String action;
@Override
public void register(Observer observer) {
observers.add(observer);
}
@Override
public void remove(Observer observer) {
observers.remove(observer);
}
@Override
public void setSubjectStatus(String subjectStatus) {
this.subjectStatus=action;
}
@Override
public String getSubjectStatus() {
return action;
}
@Override
public void notifyObserver() {
observers.stream().forEach(observer -> observer.update(this));
}
}

  - 具体观察者1

/*
@author xulihua
@date 2021/1/20 11:53
看报纸的观察者
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ReportObserver implements Observer {
private String name;
@Override
public void update(Subject subject) {
System.out.println(subject.getSubjectStatus()+”———“+name+”不能看报了”);
}
}

  - 具体观察者2

/*
@author xulihua
@date 2021/1/20 11:57
看视频的观察者
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class VideoObserver implements Observer {
private String name;
@Override
public void update(Subject subject) {
System.out.println(subject.getSubjectStatus()+”————-“+name+”不能看视频了”);
}
}

  - 测试类

public class Client {
public static void main(String[] args) {
// 老板进来公司
Boss boss=new Boss();
boss.setAction(“我胡汉三回来了”);
VideoObserver videoObserver= new VideoObserver(“张三”);
ReportObserver reportObserver= new ReportObserver(“李四”);
boss.register(videoObserver);
boss.register(reportObserver);
boss.notifyObserver();
// 前台进行通知
Secretary secretary =new Secretary();
secretary.setAction(“老板回来了”);
secretary.register(videoObserver);

secretary.notifyObserver();

}<br />}
  • 总结:
    • 优点
      • 降低了目标和观察者之间的耦合关系,两者都是实现抽象,符合依赖倒置原则;
      • 目标和观察者之间建立了一套触发机制;
    • 缺点
      • 目标和观察者的依赖关系没有完全解除,可能会循环引用;
      • 观察者增多时,通知的发布会花费很多时间,影响效率;
    • 一个对象的改变,其他的对象也会进行相应的改变的时候,就需要使用到观察者模式进行解耦;
    • 类似于广播机制,观察者进行对广播的监听,只要广播进行广播,观察者就可以收到消息,然后做出相应的动作;