万字详解常用设计模式(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日志并进行多维度分析。
目录
相关文章
|
8月前
|
设计模式 算法 Java
探索设计模式的魅力
设计模式是软件开发中的一种指导性概念,它提供了一套被广泛接受的解决方案,用于常见的设计问题。设计模式有助于提高软件的可重用性、可扩展性和可维护性,并促进团队之间的沟通。以下是一些常见的设计模式:这些只是设计模式中的一部分,每种模式都有其适用的场景和用法。设计模式帮助开发人员解决常见的设计问题,并提供了一种标准化的方法,促进了代码的可读性和可重用性。
131 1
|
设计模式 算法 前端开发
设计模式上篇
设计模式上篇
88 0
|
8月前
|
设计模式 算法 架构师
【搞懂设计模式】设计模式与面向对象原则
【搞懂设计模式】设计模式与面向对象原则
81 1
|
设计模式 开发框架 算法
万字详解常用设计模式(2)
模板方法模式是一种行为型设计模式,它定义一个操作(模板方法)的基本组合与控制流程,将一些步骤(抽象方法)推迟到子类中,在使用时调用不同的子类,就可以达到不改变一个操作的基本流程情况下,即可修改其中的某些特定步骤。这种设计方式将特定步骤的具体实现与操作流程分离开来,实现了代码的复用和扩展,从而提高代码质量和可维护性。
59 0
|
8月前
|
设计模式 算法 Java
【设计模式系列笔记】设计模式与设计原则
设计模式,是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。 设计原则是一些通用的设计指导方针,它们提供了如何设计一个优秀的软件系统的基本思想和规则。指导着设计者如何组织代码以实现高内聚、低耦合、易扩展和易维护的软件系统。
92 4
|
设计模式 算法 关系型数据库
设计模式系列教程(13) - 策略模式
设计模式系列教程(13) - 策略模式
40 0
|
8月前
|
设计模式 算法 Java
设计模式实战
**设计模式的应用与案例** 设计模式是解决常见软件设计问题的最佳实践,有助于提升代码质量和可维护性。有效利用设计模式的步骤包括:理解业务需求、识别问题、选择合适模式、学习研究和适时调整。在实际工作中,例如,通过结合工厂模式和策略模式,解决了多端页面配置筛选逻辑,避免接口爆炸;使用模板方法模式,将复杂业务逻辑拆分为可复用步骤,提高了代码扩展性。设计模式虽好,但应适度,避免过度复杂化。
66 1
|
8月前
|
设计模式 算法 Java
【设计模式系列笔记】策略模式
策略模式(Strategy Pattern)是一种行为设计模式,它定义了一系列的算法,将每个算法封装起来,并且使它们可以互相替换。策略模式使得算法可以独立于客户端而变化。
120 0
|
8月前
|
设计模式 开发者
【设计模式】设计模式综述
【1月更文挑战第12天】【设计模式】设计模式综述
|
8月前
|
设计模式 存储 算法
设计模式笔记
设计模式笔记