自学内容网 自学内容网

设计模式-装饰器模式(结构型)与责任链模式(行为型)对比,以及链式设计

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

装饰器模式和设计模式同为GoF23中设计模式之一,两者在结构上有相似之处,本文章用来学习两个设计模式,并进行对比。


1.装饰器模式

1.1概念

如果我们想要为一个已经写好的类增加新功能,在不修改该类源码的情况下,我们有以下选择:

  • 创建该类的子类,并用该子类调用父类方法,并额外添加其他功能
  • 创建一个新的类,引用已有类的对象,调用原有方法并添加其他功能

这里第二种实现思路就是我们现在说的装饰器模式。

1.2作用

为一个已有的类增加新的功能。

1.3应用场景

  • 需要动态地给对象添加职责时:当你需要给某个对象添加职责,但又不想修改其类定义时,可以使用装饰器模式。
  • 需要灵活组合多个职责时:当你有多个职责需要组合,并且这些职责的排列组合可能会变化时,装饰器模式是一个很好的选择。
  • 需要保持类的单一职责原则时:当你想保持类的单一职责原则,但又需要给类添加额外的职责时,可以使用装饰器模式将这些职责分离到装饰器

1.4特点

  • 动态扩展:装饰器模式能够在运行时动态地给对象添加职责,而无需修改类的定义。
  • 灵活性:通过组合的方式而不是继承来扩展功能,这使得装饰器模式比继承更加灵活。
  • 遵循开闭原则:装饰器模式可以在不修改现有代码的情况下添加新的功能,符合开闭原则(对扩展开放,对修改关闭)。
  • 装饰器与被装饰对象拥有相同的接口:这使得装饰器可以透明地替代被装饰对象

1.5类与对象关系

在这里插入图片描述

  • 组件接口:对应上图Component,用来规定被装饰对象和装饰器应该有哪些功能(或者说原组件中哪些功能是可以装饰的)
  • 组件类:对应上图ConcreteComponent,是被装饰对象
  • 抽象装饰器:对应上图Decorator,是一个接口,因为我们会有多个不同的具体装饰器,所以需要使用一个抽象装饰器接口来指定具体装饰器应该有哪些功能
  • 具体装饰器:是我们真正的额外功能编写的地方,因为实现了抽象装饰器所以拥有与被装饰对象相同但更强大的功能,可以在使用时进行替换。

1.6实现

// 定义一个接口,规定哪些方法可以被装饰
interface Component {
    void operation();
}
 
// 具体组件类,实现了Component接口
class ConcreteComponent implements Component {
    @Override
    public void operation() {
        System.out.println("ConcreteComponent operation");
    }
}
 
// 抽象装饰器类,持有一个Component对象,并实现了Component接口
abstract class Decorator implements Component {
  // 该对象存储原对象或者被其他装饰器装饰过的对象
  // 因为可以是其他装饰器装饰过的对象,所以才实现装饰器的叠加,是动态添加功能的关键
    protected Component component;
 
    public Decorator(Component component) {
        this.component = component;
    }
 
    @Override
    public void operation() {
        component.operation();
    }
}
 
// 具体装饰器类A,增加了额外的行为
class ConcreteDecoratorA extends Decorator {
    public ConcreteDecoratorA(Component component) {
        super(component);
    }
 
    @Override
    public void operation() {
        super.operation();
        addedBehavior();
    }
 
    private void addedBehavior() {
        System.out.println("ConcreteDecoratorA added behavior");
    }
}
 
// 具体装饰器类B,增加了额外的行为
class ConcreteDecoratorB extends Decorator {
    public ConcreteDecoratorB(Component component) {
        super(component);
    }
 
    @Override
    public void operation() {
        addedBehavior();
        super.operation();
    }
 
    private void addedBehavior() {
        System.out.println("ConcreteDecoratorB added behavior");
    }
}
 
// 客户端代码
public class DecoratorPatternDemo {
    public static void main(String[] args) {
        Component component = new ConcreteComponent();
 
        // 使用装饰器A装饰
        Component decoratorA = new ConcreteDecoratorA(component);
        // 使用装饰器B装饰装饰器A
        Component decoratorB = new ConcreteDecoratorB(decoratorA);
 
        // 执行操作,将展示所有组件和装饰器的行为
        decoratorB.operation();
    }
}

2责任链模式

2.1概念

责任链模式是一种行为设计模式,它允许你将请求沿着处理者链进行传递。每个处理者可以对请求进行处理,或者将请求传递给链中的下一个处理者。这使得你可以在不明确具体接收者的情况下,向多个对象发送请求。

2.2作用

  • 解耦请求与处理:请求者只要向链头发送一次请求,而不用关心具体由谁处理。
  • 可以动态管理处理类:可以按需添加或删除处理类。

2.3应用场景

  • 多个对象有机会处理请求:当请求可以在多个对象之间传递,并且每个对象都有可能处理它时。
  • 请求处理顺序可变:当请求的处理顺序不固定,或者你想在不修改代码的情况下改变处理顺序时。
  • 解耦请求发送者和处理者:当你想解耦请求发送者和处理者之间的依赖关系时。

2.4特点

  • 链式传递:请求在多个处理者之间传递,直到有一个处理者处理它或链结束。
  • 解耦请求发送者和接收者:发送者不需要知道哪个具体的处理者会处理请求,它只需要将请求发送到链的头部即可。
  • 增强灵活性:你可以通过动态地添加或删除处理者来改变链的结构,从而改变请求的处理流程。

2.5类与对象关系

在这里插入图片描述

责任链模式中我们有以下角色:

  • 抽象处理者(Handler)角色:声明“处理”方法,以及一个“设置下一处理器”的方法
  • 具体处理者(Concrete Handler)角色:实现了抽象处理者接口,并包含处理请求的逻辑。如果它不能处理请求,它会将请求传递给链中的下一个处理者。

2.6实现

// 抽象处理者接口
abstract class Handler {
    protected Handler nextHandler;
 
    // 设置下一个处理者
    public void setNextHandler(Handler nextHandler) {
        this.nextHandler = nextHandler;
    }
 
    // 处理请求的方法,由具体处理者实现
    public abstract void handleRequest(String request);
}


// 具体处理者类A
class ConcreteHandlerA extends Handler {
    @Override
    public void handleRequest(String request) {
        if ("A".equals(request)) {
            System.out.println("ConcreteHandlerA handled request: " + request);
        } else {
            if (nextHandler != null) {
                nextHandler.handleRequest(request);
            }
        }
    }
}


// 具体处理者类B
class ConcreteHandlerB extends Handler {
    @Override
    public void handleRequest(String request) {
        if ("B".equals(request)) {
            System.out.println("ConcreteHandlerB handled request: " + request);
        } else {
            if (nextHandler != null) {
                nextHandler.handleRequest(request);
            }
        }
    }
}


// 客户端代码
public class ChainOfResponsibilityDemo {
    public static void main(String[] args) {
        // 创建处理者对象
        Handler handlerA = new ConcreteHandlerA();
        Handler handlerB = new ConcreteHandlerB();
 
        // 构建处理者链
        handlerA.setNextHandler(handlerB);
 
        // 创建并发送请求
        String[] requests = {"A", "B", "C"};
 
        for (String request : requests) {
            handlerA.handleRequest(request); // 请求从handlerA开始传递
        }
    }
}

3.对比

  1. 首先,链式设计最重要的一点是——一个节点中要存储相邻节点的信息,这一点在上述两种设计模式中都有体现。这里要先清楚各自具体存储的是什么。
  • 装饰器模式存储的是原组件或被修饰过的组件,这说明在该种模式下各个装饰器的作用是可以叠加的
  • 责任链模式存储的是下一个处理节点,这里各个处理器效果并不会叠加
  1. 其次,各个节点应该有同样的任务。这两种模式中不论是各个装饰器,还是各个处理器,其实都有同样的任务。但是两个模式在具体实现时有区别,这也是装饰器模式可叠加而责任链模式不可叠加的原因。
  • 装饰器中的方法逻辑是——上一节点处理逻辑"及"本节点处理逻辑
  • 责任链中的方法逻辑是——本节点处理逻辑"或"下节点处理逻辑

总结

本文章比较了装饰器模式和责任链模式,因为两者在结构上十分相似都使用了链式设计,所以容易混淆,这里需要重点理解两者在处理方法中的实现逻辑的不同,装饰器是"且",责任链是"或"。


原文地址:https://blog.csdn.net/m0_74606902/article/details/144338102

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!