万字详解常用设计模式(1)

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 总的来说,责任链模式适用于存在多个处理步骤、每个处理步骤具有独立逻辑或条件、需要灵活组合和扩展的场景。通过责任链模式,可以将复杂的处理逻辑拆分为多个独立的处理步骤,并且可以动态地组合和调整处理步骤的顺序,从而提高系统的灵活性和可维护性。希望本文能够帮助读者理解和应用责任链模式,提升软件设计和开发的能力。

本文是博主在工作中对常用设计模式的使用经验总结归纳而来分享给大家。

设计模式一共有23种,本文讲解涉及如下:

  1. 责任链模式
  2. 模板方法模式
  3. 发布订阅模式
  4. 策略模式

三大分类

业界一般将设计模式分为三大类:

  • 创建型模式:对类的实例化过程进行了抽象,能够将软件模块中对象的创建和对象的使用分离。有五种创建型模式,分别是工厂方法模式、抽象工厂模式、单例模式、建造者模式和原型模式。
  • 结构型模式:关注于对象的组成以及对象之间的依赖关系,描述如何将类或者对象结合在一起形成更大的结构,就像搭积木,可以通过简单积木的组合形成复杂的、功能更为强大的结构。有七种结构型模式,分别是适配器模式、装饰者模式、代理模式、外观模式、桥接模式、组合模式和享元模式。
  • 行为型模式:关注于对象的行为问题,是对在不同的对象之间划分责任和算法的抽象化;不仅仅关注类和对象的结构,而且重点关注它们之间的相互作用。有十一种行为型模式,分别是策略模式、模板方法模式、观察者模式、迭代器模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式和解释器模式。

六大原则

设计模式遵循了六大原则,也称为SOLID原则:

  • 单一职责原则(Single Responsibitity Principle):一个类应该只有一个发生变化的原因。不要存在多于一个导致类变更的原因,也就是说每个类应该实现单一的职责,否则就应该把类拆分。
  • 开闭原则(Open Close Principle):一个软件实体,如类、模块和函数应该对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,而是要扩展原有代码,实现一个热插拔的效果。
  • 里氏替换原则(Liskov Substitution Principle):所有引用基类的地方必须能透明地使用其子类的对象。任何基类可以出现的地方,子类一定可以出现。里氏替换原则是继承复用的基石,只有当衍生类可以替换基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。
  • 接口隔离原则(Interface Segregation Principle):客户端不应该依赖它不需要的接口。类间的依赖关系应该建立在最小的接口上。每个接口中不存在子类用不到却必须实现的方法,如果不然,就要将接口拆分。使用多个隔离的接口,比使用单个接口要好。
  • 依赖倒置原则(Dependence Inversion Principle):上层模块不应该依赖底层模块,它们都应该依赖于抽象。抽象不应该依赖于细节,细节应该依赖于抽象。面向接口编程,依赖于抽象而不依赖于具体。
  • 迪米特法则(Law Of Demter):只与你的直接朋友交谈,不跟“陌生人”说话。一个类对自己依赖的类知道的越少越好。无论被依赖的类多么复杂,都应该将逻辑封装在方法的内部,通过public方法提供给外部。这样当被依赖的类变化时,才能最小的影响该类。

设计模式的好处

  • 可以重用设计,减少代码的重复,提高代码的可维护性。
  • 可以为设计提供共同的词汇,方便程序员间的交流和理解。
  • 可以实现开闭原则,增加新的功能或者修改旧的功能不影响原有的结构。
  • 可以让重构系统变得容易,确保开发正确的代码,并降低出错的可能。
  • 可以支持变化,为重写其他应用程序提供很好的系统架构。
  • 后期可以节省大量时间,提高开发效率。

设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。

1. 责任链模式

概述

责任链模式(Chain of Responsibility Pattern)是一种行为型设计模式,它通过将请求的发送者和接收者解耦,使多个对象都有机会处理请求。在这个模式中,请求沿着一个处理链依次传递,直到有一个对象能够处理它为止。

责任链模式的核心思想是将请求的发送者和接收者解耦,使得多个对象都有机会处理请求。在责任链模式中,请求会沿着一个处理链依次传递,每个处理者都有机会处理请求,如果一个处理者不能处理请求,则将请求传递给下一个处理者,直到有一个处理者能够处理它。

责任链模式包含以下几个角色:

  • 抽象处理者(Handler):定义了处理请求的接口,通常包含一个指向下一个处理者的引用,用于将请求传递给下一个处理者。
  • 具体处理者(ConcreteHandler):实现了处理请求的接口,具体处理者可以决定是否处理请求,如果不能处理,则将请求传递给下一个处理者。
  • 客户端(Client):创建处理者对象并组成责任链的结构,负责将请求发送给第一个处理者。

优缺点

优点:

  • 责任链模式可以实现请求的发送者和接收者之间的解耦。发送者只需要将请求发送给第一个处理者,无需关心具体是哪个处理者来处理。这样,系统的灵活性大大增强,可以随时增加或修改处理者的顺序。
  • 责任链模式能够避免请求的发送者和接收者之间的紧耦合。每个处理者只需要关心自己负责的请求类型,无需关心其他请求。这样,系统的可维护性也得到了提升。
  • 责任链模式可以灵活地动态添加或删除处理者。我们可以根据实际情况来调整责任链的结构,以满足不同的业务需求。

缺点:

  • 复杂度会明显提升,如果责任链过长或者处理者之间的关系复杂,可能还会导致性能下降和调试困难。

应用场景

责任链模式在许多不同的应用场景中都有广泛的应用。下面列举了一些常见的应用场景:

  • 请求处理链:当一个请求需要经过多个处理步骤或处理者进行处理时,可以使用责任链模式。每个处理者负责一部分逻辑,处理完后可以选择将请求传递给下一个处理者,从而形成一个处理链。
  • 日志记录:在日志系统中,可以使用责任链模式来记录日志。不同的处理者可以负责不同级别的日志记录,例如,一个处理者负责记录错误日志,另一个处理者负责记录调试日志,然后按照链式结构传递日志。
  • 身份验证和权限检查:在身份验证和权限检查系统中,可以使用责任链模式来验证用户的身份和权限。每个处理者可以检查特定的条件,例如用户名和密码的正确性、账户是否锁定等。如果一个处理者无法通过验证,可以将请求传递给下一个处理者。
  • 数据过滤和转换:在数据处理过程中,可以使用责任链模式来进行数据过滤和转换。每个处理者可以根据特定的条件过滤数据或对数据进行转换,然后将处理后的数据传递给下一个处理者。
  • 错误处理和异常处理:在错误处理和异常处理系统中,可以使用责任链模式来处理错误和异常。不同的处理者可以处理不同类型的错误或异常,并根据需要将错误或异常传递给下一个处理者进行进一步处理或记录。

Java 代码示例

Java 中实现责任链模式有多种方式,包括基于接口、基于抽象类、基于注解等。下面将详细介绍基于接口的常见实现方式。

基于接口的实现方式是通过定义一个处理请求的接口,每个处理者实现这个接口,并在自己的实现中决定是否处理请求和传递请求给下一个处理者。

首先,我们定义一个处理请求的接口 Handler 以及请求入参 Request

java

复制代码

public interface Handler {
    void handleRequest(Request request);
}
public class Request {
    private String type;
    // 省略getter、setter
}
然后,我们创建3个具体的处理者类实现这个接口,在具体处理者类的实现中,首先判断自己是否能够处理请求,如果能够处理,则进行处理;否则将请求传递给下一个处理者。代码如下:
java
复制代码
public class ConcreteHandlerA implements Handler {
    private Handler successor;
    public void setSuccessor(Handler successor) {
        this.successor = successor;
    }
    public void handleRequest(Request request) {
        if (request.getType().equals("A")) {
            // 处理请求的逻辑
        } else if (successor != null) {
            successor.handleRequest(request);
        }
    }
}

public class ConcreteHandlerB implements Handler {
    private Handler successor;
    public void setSuccessor(Handler successor) {
        this.successor = successor;
    }
    public void handleRequest(Request request) {
        if (request.getType().equals("B")) {
            // 处理请求的逻辑
        } else if (successor != null) {
            successor.handleRequest(request);
        }
    }
}
public class ConcreteHandlerC implements Handler {
    private Handler successor;
    public void setSuccessor(Handler successor) {
        this.successor = successor;
    }
    public void handleRequest(Request request) {
        if (request.getType().equals("C")) {
            // 处理请求的逻辑
        } else if (successor != null) {
            successor.handleRequest(request);
        }
    }

接下来,我们创建一个客户端类 Client,用于创建处理者对象并组成责任链的结构:

java

复制代码

public class Client {
    public static void main(String[] args) {
        Handler handlerA = new ConcreteHandlerA();
        Handler handlerB = new ConcreteHandlerB();
        Handler handlerC = new ConcreteHandlerC();
        handlerA.setSuccessor(handlerB);
        handlerB.setSuccessor(handlerC);
        // 创建请求并发送给第一个处理者
        Request request = new Request("A");
        handlerA.handleRequest(request);
    }
}

在客户端类中,我们创建了具体的处理者对象,并通过 setSuccessor() 方法将它们组成一个责任链的结构。然后,创建一个请求对象,并将请求发送给第一个处理者。

基于接口的实现方式简单直观,每个处理者只需要实现一个接口即可。但是它的缺点是如果责任链较长,需要创建多个处理者对象,增加了系统的复杂性和资源消耗。下面基于 Spring 框架实现一个高级版的责任链模式。

Spring 代码示例

在实际开发中,一个请求会在多个处理器之间流转,每个处理器都可以处理请求。

假设我们有一个 Spring 框架开发的订单处理系统,订单需要依次经过订单检查、库存处理、支付处理。如果某个处理环节无法处理订单,将会终止处理并返回错误信息,只有每个处理器都完成了请求处理,这个订单才算法下单成功。

首先,我们定义一个订单类 Order

java

复制代码

@Data
@AllArgsConstructor
public class orderNo {
    private String orderNumber;
    private String paymentMethod;
    private boolean stockAvailability;
    private String shippingAddress;
}

然后,我们定义一个抽象订单处理者类 OrderHandler

java

复制代码

public abstract class OrderHandler {
    public abstract void handleOrder(Order order);
}

接下来,我们创建具体的订单处理者类继承自抽象订单处理者类,实现相应的方法,并注册到 Spring 中,

java

复制代码

@Component
public class CheckOrderHandler extends OrderHandler {
    public void handleOrder(Order order) {
        if (StringUtils.isBlank(order.getOrderNo())) {
            throw new RuntimeException("订单编号不能为空");
        }
        if (order.getPrice().compareTo(BigDecimal.ONE) <= 0) {
            throw new RuntimeException("订单金额不能小于等于0");
        }
        if (StringUtils.isBlank(order.getShippingAddress())) {
            throw new RuntimeException("收货地址不能为空");
        }
        System.out.println("订单参数检验通过");
    }
}

@Component
public class AliPaymentHandler extends OrderHandler {
    public void handleOrder(Order order) {
        if (!order.getPaymentMethod().equals("支付宝")) {
            throw new RuntimeException("不支持支付宝以外的支付方式");
        }
        System.out.println("支付宝预下单成功");
    }
}
@Component
public class StockHandler extends OrderHandler {
    public void handleOrder(Order order) {
        if (!order.isStockAvailability()) {
            throw new RuntimeException("订单库存不足");
        }
        System.out.println("库存扣减成功");
    }
}

在具体订单处理者类的实现中,CheckOrderHandler 负责做订单参数检查、StockHandler 负责做库存扣减、AliPaymentHandler 负责做预下单,每个处理者的逻辑都是相互独立各不不干扰。


最后,我们创建一个订单生产链条 BuildOrderChain ,用于组成责任链的链条处理结构:

java

复制代码

@Component
public class BuildOrderChain {
    @Autowired
    private AliPaymentHandler aliPaymentHandler;
    @Autowired
    private CheckOrderHandler checkOrderHandler;
    @Autowired
    private StockHandler stockHandler;
    List<OrderHandler> list = new ArrayList<>();
    @PostConstruct
    public void init() {
        // 1. 检查订单参数
        list.add(checkOrderHandler);
        // 2. 扣减库存
        list.add(stockHandler);
        // 3. 支付宝预下单
        list.add(aliPaymentHandler);
    }
    public void doFilter(Order order) {
        for (OrderHandler orderHandler : this.list) {
            orderHandler.handleOrder(order);
        }
    }
}

订单生产链条 BuildOrderChain 类中,我们通过 @PostConstruct 注解下的 init() 初始化方法,将具体的订单处理者按代码顺序组成一个责任链的结构。然后通过 doFilter(order) 方法遍历处理者集合依次处理。

运行代码:

java

复制代码

@Slf4j
@SpringBootTest
@RunWith(SpringRunner.class)
public class OrderChainTest {
    @Autowired
    private BuildOrderChain buildOrderChain;
    @Test
    public void test() {
        Order order = new Order("123456", "支付宝",
                      true, "长沙", new BigDecimal("100"));
        buildOrderChain.doFilter(order);
    }
}

-------------------------------订单参数检验通过库存扣减成功支付宝预下单成功

可以看到订单依次经过校验处理器、库存处理器和支付处理器进行处理,直到最后完成整个订单的处理。

在举个例子,假如我们的订单针对的是虚拟不限库存商品,我们不需要进行库存扣减,那我们可以直接新建 VirtualGoodsOrderChain 虚拟商品订单生产链条类,代码如下,

java

复制代码

@Component
public class VirtualGoodsOrderChain {
    @Autowired
    private AliPaymentHandler aliPaymentHandler;
    @Autowired
    private CheckOrderHandler checkOrderHandler;
    List<OrderHandler> list = new ArrayList<>();
    @PostConstruct
    public void init() {
        // 1. 检查订单参数
        list.add(checkOrderHandler);
        // 2 支付宝预下单
        list.add(aliPaymentHandler);
    }
    public void doFilter(Order order) {
        for (OrderHandler orderHandler : this.list) {
            orderHandler.handleOrder(order);
        }
    }
}

运行代码:

java

复制代码

@Test
public void virtualOrderTest() {
    Order order = new Order("123456", "支付宝", true, "长沙", new BigDecimal("100"));
    virtualGoodsOrderChain.doFilter(order);
}

-------------------------------------------订单参数检验通过支付宝预下单成功

总的来说,责任链模式适用于存在多个处理步骤、每个处理步骤具有独立逻辑或条件、需要灵活组合和扩展的场景。通过责任链模式,可以将复杂的处理逻辑拆分为多个独立的处理步骤,并且可以动态地组合和调整处理步骤的顺序,从而提高系统的灵活性和可维护性。希望本文能够帮助读者理解和应用责任链模式,提升软件设计和开发的能力。

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
2月前
|
设计模式 安全 Java
24种设计模式详解(下)
24种设计模式详解(下)
37 0
|
1月前
|
设计模式 Java 数据库连接
Java编程中的设计模式:单例模式的深度剖析
【10月更文挑战第41天】本文深入探讨了Java中广泛使用的单例设计模式,旨在通过简明扼要的语言和实际示例,帮助读者理解其核心原理和应用。文章将介绍单例模式的重要性、实现方式以及在实际应用中如何优雅地处理多线程问题。
37 4
|
3月前
|
设计模式 C# 开发者
C#设计模式入门实战教程
C#设计模式入门实战教程
|
4月前
|
设计模式
设计模式:从理论到实际应用
【8月更文挑战第18天】设计模式是软件工程中解决特定问题的有效方案,提升代码质量并促进团队协作。本文从理论出发,探讨设计模式在实际项目中的应用。设计模式分为创建型、结构型和行为型,遵循如开闭原则等设计原则。通过工厂模式创建不同类型的电子签章,观察者模式实现在状态变更时的通知机制,以及建造者模式灵活组装复杂对象。以虚拟运营商平台为例,采用责任链模式优化审批流程,展示设计模式的实际价值。
|
6月前
|
设计模式 开发框架 前端开发
设计模式一
设计模式是面向对象编程的实践经验,旨在解决一系列问题,促进软件的可升级和维护。它分为创建型(如工厂、单例模式)、结构型(适配器、装饰器模式等)和行为型(观察者、策略模式等)三大类。J2EE模式关注表示层,包括MVC模式等。设计模式的七大原则包括里氏替换、依赖倒置、开闭原则、单一职责、接口隔离、迪米特原则和合成复用原则,这些原则有助于减少耦合和提高代码可读性。接口作为契约和层间对接工具,约束类型行为并允许实现多继承。
|
7月前
|
设计模式 SQL 算法
【设计模式系列笔记】模板方法模式
模板方法模式是一种行为设计模式,它定义了一个算法的骨架,并允许子类在不改变该算法结构的情况下重新定义算法的某些步骤。这种模式属于行为型模式,它通过将算法的不同部分封装在不同的方法中,从而使子类能够在不改变算法结构的前提下定制算法的某些步骤。
66 0
|
7月前
|
设计模式 存储 安全
[设计模式] 常见的设计模式
[设计模式] 常见的设计模式
|
设计模式 算法 Java
万字详解常用设计模式(3)
客户端必须知道所有的策略类,并自行决定使用哪一个策略类,这增加了客户端的复杂度。 策略模式会产生很多的策略类,增加系统的类数量。
53 0
|
设计模式
23种设计模式
23种设计模式主要分为三大类:创建型模式、结构型模式和行为型模式。
79 0
|
设计模式 算法 程序员
【3W2H设计模式】什么是设计模式
【3W2H设计模式】什么是设计模式
274 0
下一篇
DataWorks