• 设计模式笔记


    工厂模式

    工厂模式分为:

            1. 简单工厂模式

            2. 工厂方法模式

            3. 抽象工厂模式

    简单工厂模式

    简单⼯⼚模式的核⼼思想是将产品的创建过程封装在⼀个⼯⼚类中,把创建对象的流程集中在这个工厂类里面

    简单⼯⼚模式包括三个主要⻆⾊,⼯⼚类、抽象产品、具体产品,下⾯的图示则展示了⼯⼚类的基本结构。

    抽象产品(接口)

            比如进行影子链路复制的时候,需要创建Kakfa、RocketMQ、ClickHouse、Doris等等引擎实体,而且这些引擎实体复制的流程类型,那我们可以抽离出一个抽象产品

    ⽐如上图中的 Shape 接⼝,描述产品的通⽤⾏为

    1. public interface thirdPartyService{
    2. /**
    3. 在各自平台中查询引擎实体信息
    4. **/
    5. R searchEntityInformation();
    6. /**
    7. 修改部分信息
    8. **/
    9. void replaceInformation();
    10. /**
    11. 创建影子实体
    12. **/
    13. R createShadowEntity();
    14. }

    具体产品:

            需要复制的引擎实体Kafka、RocketMq, ClickHouse等等需要各自实现抽象产品接口实现方法

    1. public class kafkaThirdPartySercviceImpl implements thirdPartyService {
    2. /**
    3. 在各自平台中查询引擎实体信息
    4. **/
    5. R searchEntityInformation();
    6. /**
    7. 修改部分信息
    8. **/
    9. void replaceInformation();
    10. /**
    11. 创建影子实体
    12. **/
    13. R createShadowEntity();
    14. }

    ⼯⼚类:        

    负责创建产品,根据传递的不同参数创建不同的产品示例

    1. public class ThirdPartyServiceFactory {
    2. public static Entity getEntity(String type) {
    3. if(type.equalsIgnoreCase("kafka")){
    4. return new kafkaThirdPartyServiceImpl();
    5. }
    6. else if(type.equalsIgnoreCase("rocketMQ")){
    7. return new rocketMqThirdPartyServiceImpl();
    8. }
    9. else{
    10. .....
    11. }
    12. }
    13. }

    我们通过访问工厂类来创建各个引擎实体

    缺点:简单⼯⼚类简化了客户端操作,客户端可以调⽤⼯⼚⽅法来获取具体产品,⽽⽆需直接与具体产品类交互,降低了 耦合,但是有⼀个很⼤的问题就是不够灵活,如果需要添加新的产品,就需要修改⼯⼚类的代码

    工厂方法模式

    ⼯⼚⽅法模式也是⼀种创建型设计模式,简单⼯⼚模式只有⼀个⼯⼚类,负责创建所有产品,如果要添加新的产 品,通常需要修改⼯⼚类的代码。⽽⼯⼚⽅法模式引⼊了抽象⼯⼚具体⼯⼚的概念,每个具体⼯⼚只负责创建⼀个具体产品,添加新的产品只需要添加新的⼯⼚类⽽⽆需修改原来的代码,这样就使得产品的⽣产更加灵活,⽀持 扩展,符合开闭原则。

    ⼯⼚⽅法模式分为以下⼏个⻆⾊:

            抽象⼯⼚:⼀个接⼝,包含⼀个抽象的⼯⼚⽅法(⽤于创建产品对象)。

            具体⼯⼚:实现抽象⼯⼚接⼝,创建具体的产品。

            抽象产品:定义产品的接⼝ 

            具体产品:实现抽象产品接⼝,是⼯⼚创建的对象。

    其实就是在工厂这一层又做了一层封装,每一个产品有自己的工厂。

    当需要添加新产品的时候:

             简单工厂模式需要修改工厂类的代码(多加一个if else判断),不符合开闭原则。

             工厂方法模式的话,新建一个类实现抽象工厂对应新产品,这样不需要修改源代码

    具体实现

     抽象产品(接口)

            比如进行影子链路复制的时候,需要创建Kakfa、RocketMQ、ClickHouse、Doris等等引擎实体,而且这些引擎实体复制的流程类型,那我们可以抽离出一个抽象产品

    ⽐如上图中的 Shape 接⼝,描述产品的通⽤⾏为

    1. public interface ThirdPartyService {
    2. /**
    3. 在各自平台中查询引擎实体信息
    4. **/
    5. R searchEntityInformation();
    6. /**
    7. 修改部分信息
    8. **/
    9. void replaceInformation();
    10. /**
    11. 创建影子实体
    12. **/
    13. R createShadowEntity();
    14. }

    具体产品:

            需要复制的引擎实体Kafka、RocketMq, ClickHouse等等需要各自实现抽象产品接口实现方法

    1. public class KafkaThirdPartySercviceImpl implements ThirdPartyService {
    2. /**
    3. 在各自平台中查询引擎实体信息
    4. **/
    5. @Override
    6. R searchEntityInformation();
    7. /**
    8. 修改部分信息
    9. **/
    10. @Override
    11. void replaceInformation();
    12. /**
    13. 创建影子实体
    14. **/
    15. @Override
    16. R createShadowEntity();
    17. }

    抽象工厂:

    1. // 抽象⼯⼚
    2. public interface ThirdPartyserviceFactory {
    3. ThirdPartyService createThirdPartyservice();
    4. }

    具体工厂:

    1. // 具体⼯⼚ - kafka创建服务
    2. class KafkaServiceFactory implements ThirdPartyserviceFactory {
    3. @Override
    4. public ThirdPartyService createThirdPartyservice() {
    5. return new KafkaThirdPartySercviceImpl();
    6. }
    7. }
    8. // 具体⼯⼚ - rocketMq创建服务
    9. class RocketMqServiceFactory implements ThirdPartyserviceFactory{
    10. @Override
    11. public ThirdPartyService createThirdPartyservice() {
    12. return new rocketMqThirdPartySercviceImpl();
    13. }
    14. }

    客户端代码

    1. // 客户端代码
    2. public class Client {
    3. public static void main(String[] args) {
    4. ShapeFactory circleFactory = new CircleFactory();
    5. Shape circle = circleFactory.createShape();
    6. circle.draw(); // 输出:Circle
    7. ShapeFactory squareFactory = new SquareFactory();
    8. Shape square = squareFactory.createShape();
    9. square.draw(); // 输出:Square
    10. }
    11. }

    总结:

            ⼯⼚⽅法模式使得每个⼯⼚类的职责单⼀,每个⼯⼚只负责创建⼀种产品,当创建对象涉及⼀系列复杂的初始化逻 辑,⽽这些逻辑在不同的⼦类中可能有所不同时,可以使⽤⼯⼚⽅法模式将这些初始化逻辑封装在⼦类的⼯⼚中。

             在现有的⼯具、库中,⼯⼚⽅法模式也有⼴泛的应⽤,⽐如: Spring 框架中的 Bean ⼯⼚:通过配置⽂件或注解,Spring 可以根据配置信息动态地创建和管理对象。 JDBC 中的 Connection ⼯⼚:在 Java 数据库连接中, DriverManager 使⽤⼯⼚⽅法模式来创建数据库连 接。不同的数据库驱动(如 MySQL、PostgreSQL 等)都有对应的⼯⼚来创建连接。

    抽象工厂模式

            与工厂方法模式做对比:

            这就涉及到创建“多类”对象了,在⼯⼚⽅法模式中,每个具体⼯⼚只负责创建单⼀的产品。但是如果有多类产品 呢,⽐如说“⼿机”,⼀个品牌的⼿机有⾼端机、中低端机之分,这些具体的产品都需要建⽴⼀个单独的⼯⼚类,但 是它们都是相互关联的,都共同属于同⼀个品牌,这就可以使⽤到【抽象⼯⼚模式】。

    应⽤场景

            抽象⼯⼚模式能够保证⼀系列相关的产品⼀起使⽤,并且在不修改客户端代码的情况下,可以⽅便地替换整个产品 系列。但是当需要增加新的产品类时,除了要增加新的具体产品类,还需要修改抽象⼯⼚接⼝及其所有的具体⼯⼚ 类,扩展性相对较差。因此抽象⼯⼚模式特别适⽤于⼀系列相关或相互依赖的产品被⼀起创建的情况,典型的应⽤ 场景是使⽤抽象⼯⼚模式来创建与不同数据库的连接对象

    总结:

            简单⼯⼚、⼯⼚⽅法、抽象⼯⼚的区别

    简单⼯⼚模式:⼀个⼯⼚⽅法创建所有具体产品

    ⼯⼚⽅法模式:⼀个⼯⼚⽅法创建⼀个具体产品

    抽象⼯⼚模式:⼀个⼯⼚⽅法可以创建⼀类具体产品

    职责链设计模式

            责任链模式是⼀种⾏为型设计模式,它允许你构建⼀个对象链,让请求从链的⼀端进⼊,然后沿着链上的对象依次处理,直到链上的某个对象能够处理该请求为⽌

            职责链模式(Chain  of Responsibility  Pattern),又叫责任链模式,为请求创建了一个接收者对象的链。这种模式对请求的发送者和接收者进行解耦

    案例分析

    学校采购项目

    采购员采购教学器材
    如果金额小于等于  5000,由教学主任审批
    如果金额小于等于  10000,由院长审批
    如果金额小于等于  30000,由副校长审批
    如果金额超过  30000 以上,有校长审批

    1. if(x <= 5000){
    2. 教导主任处理
    3. }else if(x > 5000 && x <= 10000){
    4. 院长处理
    5. }else if(x > 10000 && x < 50000){
    6. 副校长处理
    7. }else{
    8. 校长处理
    9. }

    缺点: 

    • 如果各个级别的人员审批金额发生变化,在客户端的也需要变化
    • 客户端必须明确的知道有多少个审批级别和访问
    • 这样对一个采购请求进行处理和Approver  (审批人)  就存在强耦合关系,不利于代码的扩展和维护

            类似之前项目中,对于启动压测任务的判断,需要涉及到很多的if else 判断。 首先第一点可读性就很差,其次启动压测任务的判断后续可能还需要添加,这样强耦合的写法,不利于代码的拓展和维护。

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


    Handler:抽象的处理者,定义了一个处理请求的接口。
    ConcreteHandlerA ,  B :具体的处理者,处理它自己负责的请求,可以访问它的后继者(即下一个处理者),    如果可以处理当前请求,则处理,否则就将该请求交个后继者去处理,从而形成一个职责链。
    Request:含有很多属性,表示一个请求。

    具体案例实现可以查看尚硅谷设计模式学习(二十四)职责链模式_责任链模式 java尚硅谷-CSDN博客

    项目中的实现

            项目中启动压测任务需要多重判断,如果将左右的判断都堆叠在一起,会有很多的if else判断,如同屎山,所以使用职责链设计模式进行改写。

    启动压测任务需要进行:

            1. 安全检测 --无法跳过

            2. 配置检测

            3.冲突检测 

    步骤

            1. 创建,StressTestCheckHandler接口----所有handler的父接口

    1. public interface StressTestCheckHandler {
    2. void handler(StressTestCheckContext context);
    3. default boolean canHandler(StressTestCheckContext context){
    4. return context.getCheckHandlerList().stream().anyMatch(check ->
    5. check.equals(this.getclass()));
    6. }
    7. }

            该接口有handler抽象方法和canHandler的default的默认实现方法,所有实现此接口的handler实现handler() 方法

            handler()方法是所有handler自定义的

            canHandler() 方法就是判断有没有在收集的handler里面,如果有的话说明可以handle

            2. 收集handler,有些handler可以需要跳过检测也就是不需要,则通过每一个handler上面的注解进行筛选,最后收集为一个List

            3.StressTestCheckProcessor 来实现职责链的过程 

            StressTestCheckProcessor方法收集所有的StressTestCheckHandler实现类,收集为一个List,依次执行。并且使用一个异常列表收集处理过程中的异常。如果异常列表为空,则说压测启动前的判断全部成功。

  • 相关阅读:
    XML文件
    软件开发平台流辰信息如何为客户分忧解难?
    vue基础教程(7)——构建项目级首页
    字节跳动工程师收入世界第五,2021年全球程序员收入报告出炉
    【SpringCloud】Spring Cloud Gateway实现接口防篡改
    【学习笔记】「JOI Open 2022」长颈鹿
    【云原生】Nacos-TaskManager 任务管理的使用
    Java 多线程 习题
    【计算机组成原理】加减运算和溢出判断
    JAVA入坑之Junit测试与断言
  • 原文地址:https://blog.csdn.net/qq_43144487/article/details/136742979