责任链

98 阅读17分钟

软件设计模式中的责任链在安卓开发中的应用

一、责任链模式的概念和特点

责任链模式(Chain of Responsibility Pattern),是一种行为型设计模式,它使多个对象都有机会处理请求,从而避免了请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。

uml图

责任链模式的主要特点有:

  • 灵活性:可以动态地改变链上的处理者或者调整其顺序。
  • 低耦合:请求者不需要知道链上的具体处理者,只需将请求发送到链上即可。
  • 可拓展性:可以根据需要增加或删除责任对象,或者改变它们的职责。
  • 高内聚:每个责任对象只关注自己感兴趣的请求,并且可以简化复杂的条件判断逻辑。

二、责任链模式在安卓开发中的应用

安卓开发中有很多场景可以使用责任链模式来简化代码和提高可维护性。例如:

2.1 触摸事件分发

触摸事件(MotionEvent)是安卓系统中最常见和重要的事件之一,它描述了用户通过手指在屏幕上进行各种操作(点击、滑动、缩放等)所产生的动作和坐标信息。触摸事件在视图层级(View Hierarchy)中按照一定规则进行分发和消费,形成了一个典型的责任链结构。

如下图所示:

触摸事件从Activity开始分发,首先经过Window和DecorView,然后到达根布局(Root View),再依次传递给子布局(Child View),最后到达目标视图(Target View)。每个视图都可以选择拦截或传递该事件,如果拦截则自己消费该事件并结束分发;如果传递则将该事件交给下一个视图继续分发。如果没有任何视图拦截该事件,则默认由Activity消费该事件。

在这个过程中,每个视图都是一个处理者(Handler),它们通过onInterceptTouchEvent()方法和onTouchEvent()方法来决定是否拦截消费该事件。每个视图都持有其子视图列表(Child List)作为下一个处理者(Next Handler),并通过dispatchTouchEvent()方法来沿着这条列表进行顺序发送。这样就构成了一个由Activity开始到Target View结束的责任链结构。

2.2 日志打印

日志打印(Log)是安卓开发中常用的调试工具之一,它可以将程序运行时产生的各种信息输出到控 制台或文件中,方便开发者进行分析和定位问题。日志打印有不同的级别(Level),例如VERBOSE、DEBUG、INFO、WARN、ERROR等,每个级别代表了不同的重要性和紧急程度。日志打印也可以使用责任链模式来实现,使得不同级别的日志可以被不同的处理者处理。

如下图所示:

日志打印责任链由一个抽象处理者(AbstractLogger)和多个具体处理者(ConsoleLogger、FileLogger、ErrorLogger)组成。每个具体处理者都有一个固定的级别(level),并且持有下一个处理者(nextLogger)的引用。当一个日志请求到达时,每个处理者都会根据自己的级别和请求的级别来决定是否处理该请求。如果自己的级别大于等于请求的级别,则自己处理该请求,并且将该请求传递给下一个处理者;如果自己的级别小于请求的级别,则直接将该请求传递给下一个处理者。这样就形成了一个由抽象处理者开始到最后一个具体处理者结束的责任链结构。

2.3 OkHttp拦截器

OkHttp是安卓开发中常用的网络请求库之一,它提供了一种拦截器(Interceptor)机制,可以在网络请求和响应过程中插入一些额外的操作,例如添加公共参数、缓存策略、重试机制等。拦截器也是一种典型的责任链模式应用。

如下图所示:

OkHttp拦截器责任链由多个拦截器对象组成,每个拦截器都实现了Interceptor接口,并且持有下一个拦截器对象(chain.proceed()方法)。当一个网络请求到达时,每个拦截器都可以对该请求进行修改或增强,并且将修改后的请求传递给下一个拦截器;当网络响应返回时,每个拦截器也可以对该响应进行修改或增强,并且将修改后的响应返回给上一个拦截器。这样就形成了一个由第一个拦截器开始到最后一个拦截器结束,并且在中间包含了真正发送网络请求和接收网络响应的责任链结构。

三、责任链模式的实现和比较

责任链模式可以使用不同的编程语言来实现,例如java、kotlin、c++等。下面以日志打印为例,分别使用这三种语言来实现和比较责任链模式。

3.1 java实现

java是一种面向对象的编程语言,它可以使用类和接口来定义抽象处理者和具体处理者,并且使用继承和多态来实现责任链结构。具体代码如下:

// 抽象处理者
public abstract class AbstractLogger {
    // 日志级别常量
    public static final int VERBOSE = 1;
    public static final int DEBUG = 2;
    public static final int INFO = 3;
    public static final int WARN = 4;
    public static final int ERROR = 5;

    // 处理者级别
    protected int level;

    // 下一个处理者
    protected AbstractLogger nextLogger;

    // 设置下一个处理者
    public void setNextLogger(AbstractLogger nextLogger) {
        this.nextLogger = nextLogger;
    }

    // 处理请求
    public void logMessage(int level, String message) {
        if (this.level <= level) {
            write(message); // 处理请求
        }
        if (nextLogger != null) {
            nextLogger.logMessage(level, message); // 传递给下一个处理者
        }
    }

    // 抽象方法,由子类实现具体的日志输出方式
    protected abstract void write(String message);
}

// 具体处理者:控制台日志
public class ConsoleLogger extends AbstractLogger {

   public ConsoleLogger(int level){
      this.level = level;
   }

   @Override
   protected void write(String message) {    
      System.out.println("Console Logger: " + message);
   }
}
// 具体处理者:文件日志
public class FileLogger extends AbstractLogger {

   public FileLogger(int level){
      this.level = level;
   }

   @Override
   protected void write(String message) {    
      System.out.println("File Logger: " + message);
   }
}
// 具体处理者:错误日志
public class ErrorLogger extends AbstractLogger {

   public ErrorLogger(int level){ this.level = level; }

@Override protected void write(String message) {
System.out.println("Error Logger: " + message); } }

// 客户端测试 public class Client {

// 创建责任链 private static AbstractLogger getChainOfLoggers(){

  AbstractLogger errorLogger = new ErrorLogger(AbstractLogger.ERROR);
  AbstractLogger fileLogger = new FileLogger(AbstractLogger.DEBUG);
  AbstractLogger consoleLogger = new ConsoleLogger(AbstractLogger.INFO);

  errorLogger.setNextLogger(fileLogger);
  fileLogger.setNextLogger(consoleLogger);

  return errorLogger;  
}

public static void main(String[] args) { AbstractLogger loggerChain = getChainOfLoggers();

  loggerChain.logMessage(AbstractLogger.INFO, "This is an information.");

  loggerChain.logMessage(AbstractLogger.DEBUG, "This is a debug level information.");

  loggerChain.logMessage(AbstractLogger.ERROR, "This is an error information.");
}
 }

输出结果:

Console Logger: This is an information.
File Logger: This is a debug level information.
Console Logger: This is a debug level information.
Error Logger: This is an error information.
File Logger: This is an error information.
Console Logger: This is an error information.

3.2 kotlin实现

kotlin是一种基于java虚拟机的静态类型编程语言,它可以与java互操作,并且提供了更简洁和优雅的语法。kotlin也可以使用类和接口来定义抽象处理者和具体处理者,并且使用继承和多态来实现责任链结构。具体代码如下:

// 抽象处理者
abstract class AbstractLogger(val level: Int) { // 日志级别常量 companion object { const val VERBOSE = 1 const val DEBUG = 2 const val INFO = 3 const val WARN = 4 const val ERROR = 5 }

// 下一个处理者
var nextLogger: AbstractLogger? = null

// 处理请求
fun logMessage(level: Int, message: String) {
    if (this.level <= level) {
        write(message) // 处理请求
    }
    nextLogger?.logMessage(level, message) // 传递给下一个处理者
}

// 抽象方法,由子类实现具体的日志输出方式
protected abstract fun write(message: String)
}

// 具体处理者:控制台日志 class ConsoleLogger(level: Int) : AbstractLogger(level) {

override fun write(message: String) {
    println("Console Logger: $message")
}
}

// 具体处理者:文件日志 class FileLogger(level: Int) : AbstractLogger(level) {

override fun write(message: String) {
    println("File Logger: $message")
}
}

// 具体处理者:错误日志 class ErrorLogger(level: Int) : AbstractLogger(level) {

override fun write(message: String) {
    println("Error Logger: $message")
}
}

// 客户端测试 
fun main() { 
// 创建责任链 
val loggerChain = ErrorLogger(AbstractLogger.ERROR).apply { 
    nextLogger = FileLogger(AbstractLogger.DEBUG).apply { 
        nextLogger = ConsoleLogger(AbstractLogger.INFO) 
    } 
}

loggerChain.logMessage(AbstractLogger.INFO, “This is an information.”)

loggerChain.logMessage(AbstractLogger.DEBUG, “This is a debug level information.”)

loggerChain.logMessage(AbstractLogger.ERROR, “This is an error information.”) 
}

输出结果:

Console Logger: This is an information.
File Logger: This is a debug level information.
Console Logger: This is a debug level information.
Error Logger: This is an error information.
File Logger: This is an error information.
Console Logger: This is an error information.

3.3 c++实现

c++是一种面向对象的编程语言,它可以使用类来定义抽象处理者和具体处理者,并且使用继承和多态来实现责任链结构。具体代码如下:

// 抽象处理者
class AbstractLogger {
public:
    // 日志级别常量
    static const int VERBOSE = 1;
    static const int DEBUG = 2;
    static const int INFO = 3;
    static const int WARN = 4;
    static const int ERROR = 5;

    // 构造函数,设置处理者级别
    AbstractLogger(int level) {
        this->level = level;
        this->nextLogger = nullptr; // 初始化下一个处理者为空
    }

    // 设置下一个处理者
    void setNextLogger(AbstractLogger* nextLogger) {
        this->nextLogger = nextLogger;
    }

    // 处理请求
    void logMessage(int level, std::string message) {
        if (this->level <= level) {
            write(message); // 处理请求
        }
        if (this->nextLogger != nullptr) {
            this->nextLogger->logMessage(level, message); // 传递给下一个处理者
        }
    }

protected:
    // 处理者级别
    int level;

private:
   // 下一个处理者 
   AbstractLogger* nextLogger;

   // 抽象方法,由子类实现具体的日志输出方式
   virtual void write(std::string message) = 0; 
};
// 具体处理者:控制台日志
class ConsoleLogger : public AbstractLogger { 
public: 
// 构造函数,调用父类构造函数 
ConsoleLogger(int level) : AbstractLogger(level) {}

private: 
// 实现具体的日志输出方式 
void write(std::string message) override { std::cout << "Console Logger: " << message << std::endl; } };

// 具体处理者:文件日志
 class FileLogger : public AbstractLogger {
 public: 
// 构造函数,调用父类构造函数 
FileLogger(int level) : AbstractLogger(level) {}

private: 
// 实现具体的日志输出方式 
void write(std::string message) override { std::cout << "File Logger: " << message << std::endl; } };
// 具体处理者:错误日志 
class ErrorLogger : public AbstractLogger {
 public: 
// 构造函数,调用父类构造函数 
ErrorLogger(int level) : AbstractLogger(level) {}

private: 
// 实现具体的日志输出方式 
void write(std::string message) override { std::cout << "Error Logger: " << message << std::endl; } };

// 客户端测试 
int main() { 
// 创建责任链 
AbstractLogger* loggerChain = new ErrorLogger(AbstractLogger::ERROR); 
loggerChain->setNextLogger(new FileLogger(AbstractLogger::DEBUG));
loggerChain->setNextLogger(new ConsoleLogger(AbstractLogger::INFO));

loggerChain->logMessage(AbstractLogger::INFO, “This is an information.”);

loggerChain->logMessage(AbstractLogger::DEBUG, “This is a debug level information.”);

loggerChain->logMessage(AbstractLogger::ERROR, “This is an error information.”);

// 释放内存 
delete loggerChain;

return 0; 
}

输出结果:

Console Logger: This is an information.
File Logger: This is a debug level information.
Console Logger: This is a debug level information.
Error Logger: This is an error information.
File Logger: This is an error information.
Console Logger: This is an error information.

3.4 责任链模式不同语言的比较

从上面的代码可以看出,责任链模式的实现和比较主要有以下几个方面:

①抽象处理者:

不同的编程语言可以使用不同的方式来定义抽象处理者,例如java和kotlin可以使用接口或抽象类,c++可以使用纯虚函数。抽象处理者主要负责定义处理请求的方法和设置下一个处理者的方法。

②具体处理者:

不同的编程语言可以使用不同的方式来定义具体处理者,例如java和kotlin可以使用类,并且实现或继承抽象处理者,c++可以使用类,并且继承抽象处理者。具体处理者主要负责实现具体的日志输出方式,并且根据自己的级别来判断是否需要处理请求。

③责任链结构:

不同的编程语言可以使用不同的方式来创建责任链结构,例如java和kotlin可以使用对象引用来连接各个处理者,c++可以使用指针来连接各个处理者。责任链结构主要负责将请求沿着责任链传递给合适的处理者。

④客户端测试:

不同的编程语言可以使用不同的方式来测试责任链模式,例如java和kotlin可以使用main函数或单元测试框架,c++也可以使用main函数或单元测试框架。客户端测试主要负责创建责任链并发送请求给第一个处理者。

四、责任链模式的优缺点

优缺点描述优化建议
优点降低了耦合度。它将请求的发送者和接收者解耦,让多个对象都有可能接收请求,将这些对象连成一条链,并且沿着这条链传递请求,直到有对象处理它为止。可以用责任链模式替代多重if-else或者switch-case语句来判断请求的类型和处理方式。
优点简化了对象。使得对象不需要知道链的结构,提高了系统的灵活性。可以用责任链模式封装对象之间的依赖关系,让客户端只需要知道第一个处理者即可。 这样客户端就不需要知道后续的处理者是谁,也不需要关心链的结构。
优点增强了给对象指派职责的灵活性。可以动态地改变链中的成员或者调动它们的次序。可以用责任链模式根据不同的情况调整处理者之间的顺序或者增加或删除某个处理者。
缺点产生很多细粒度的对象。因为功能处理都分散到了单独的职责对象中,每个对象功能单一,要把整个流程处理完,需要很多的职责对象,会产生大量的细粒度职责对象责任链模式会导致系统中出现很多小类,增加了系统复杂度和开发难度。可以考虑使用枚举类型或者注解来简化处理类的数量和判断逻辑
缺点不一定能处理。每个职责对象都只负责自己的部分,这样就可以出现某个请求,即使把整个链走完,都没有职责对象处理它。这就需要提供默认处理,并且注意构造链的有效性。责任链模式可能会导致某些请求无法得到满足,或者造成不必要的性能损耗。可以考虑使用一个默认处理者来应对未知类型或异常情况,并且根据请求频率或优先级来调整链中各个处理者的顺序。

1.责任链模式有以下几个优点:

①降低了耦合度。它将请求的发送者和接收者解耦,让多个对象都有可能接收请求。例如,我们可以用责任链模式替代多重if-else或者switch-case语句来判断请求的类型和处理方式。

// 使用 if-else 或 switch-case 的方式
if (request.type == "A") {
    // handle A
} else if (request.type == "B") {
    // handle B
} else if (request.type == "C") {
    // handle C
} else {
    // default handle
}
// 使用责任链模式的方式
Handler handlerA = new ConcreteHandlerA(); // 创建 A 类型的处理者
Handler handlerB = new ConcreteHandlerB(); // 创建 B 类型的处理者
Handler handlerC = new ConcreteHandlerC(); // 创建 C 类型的处理者
handlerA.setSuccessor(handlerB); // 设置 A 的后继者为 B
handlerB.setSuccessor(handlerC); // 设置 B 的后继者为 C
handlerA.handleRequest(request); // 从 A 开始沿着链传递请求

②简化了对象。使得对象不需要知道链的结构,提高了系统的灵活性。例如,我们可以用责任链模式封装对象之间的依赖关系,让客户端只需要知道第一个处理者即可。

Client client = new Client(); // 创建客户端对象
Handler handlerA = new ConcreteHandlerA(); // 创建 A 类型的处理者
client.setFirstHandler(handlerA); // 设置客户端的第一个处理者为 A 
client.sendRequest(request); // 客户端发送请求

// 客户端不需要知道后续的处理者是谁,也不需要关心链的结构。

③增强了给对象指派职责的灵活性。可以动态地改变链中的成员或者调动它们的次序。例如,我们可以用责任链模式根据不同的情况调整处理者之间的顺序或者增加或删除某个处理者。

// 原来的顺序是 A -> B -> C 
Handler handlerA = new ConcreteHandlerA();
Handler handlerB = new ConcreteHandlerB();
Handler handlerC = new ConcreteHandlerC();
handlerA.setSuccessor(handlerB);
handlerB.setSuccessor(handlerC);
// 现在要改成 B -> C -> A 或者增加一个 D 处理者

handlerA.setSuccessor(null); // 删除 A 的后继者 
handlerB.setSuccessor(handlerC); // 设置 B 的后继者为 C 
handlerC.setSuccessor(handlerD); // 设置 C 的后继者为 D 或新增一个 D 处理 者

2.责任链模式也有以下几个缺点:

① 产生很多细粒度的对象。因为功能处理都分散到了单独的职责对象中,每个对象功能单一,要把整个流程处理完,需要很多的职责对象。

  • 例如,我们每种类型产生很多细粒度的对象。因为功能处理都分散到了单独的职责对象中,每个对象功能单一,要把整个流程处理完,需要很多的职责对象。
  • 例如,我们每种类型的请求都需要一个对应的处理类,这样会导致系统中出现很多小类,增加了系统复杂度和开发难度。
// 每种类型的请求都需要一个对应的处理类
class RequestTypeA {} // A 类型的请求
class RequestTypeB {} // B 类型的请求
class RequestTypeC {} // C 类型的请求

class ConcreteHandlerA extends Handler { // A 类型的处理者
    public void handleRequest(Request request) {
        if (request instanceof RequestTypeA) {
            // handle A
        } else {
            successor.handleRequest(request); // 转交给后继者处理
        }
    }
}

class ConcreteHandlerB extends Handler { // B 类型的处理者
    public void handleRequest(Request request) {
        if (request instanceof RequestTypeB) {
            // handle B
        } else {
            successor.handleRequest(request); // 转交给后继者处理
        }
    }
}

class ConcreteHandlerC extends Handler { // C 类型的处理者
    public void handleRequest(Request request) {
        if (request instanceof RequestTypeC) {
            // handle C
        } else {
            successor.handleRequest(request); // 转交给后继者处理
        }
    }
}

②不一定能处理。每个职责对象都只负责自己的部分,这样就可以出现某个请求,即使把整个链走完,都没有职责对象处理它。这就需要提供默认处理,并且注意构造链的有效性。例如,我们可能会遇到以下两种情况:

  • 如果请求类型不是 A、B、C 中的任何一个,则无法得到处理。
  • 如果请求类型是 A,则需要遍历整条链才能找到合适的处理者。
// 如果请求类型不是 A、B、C 中的任何一个,则无法得到处理 
Handler handlerA = new ConcreteHandlerA(); 
Handler handlerB = new ConcreteHandlerB(); 
Handler handlerC = new ConcreteHandlerC(); 
handlerA.setSuccessor(handlerB); 
handlerB.setSuccessor(handlerC); 
handlerA.handleRequest(new Request("D")); 

// 如果请求类型是 A,则需要遍历整条链才能找到合适的处理者 
handlerA.handleRequest(new Request("A"));

3.为了克服这些缺点,我们可以采取以下一些优化建议:

①使用枚举类型或者注解来简化处理类的数量和判断逻辑。

例如 我们可以用枚举类型来表示不同类型的请求,并用注解来标记不同类型的处理者,这样就可以减少判断语句和处理类的数量。

enum RequestType { // 枚举类型表示不同类型的请求 
    A, B, C; 
} 

@HandlerAnnotation(type = RequestType.A) // 注解标记 A 类型 的 处理 者 
class ConcreteHandlerA extends Handler { 
public void handleRequest(Request request) { 
// handle A 
}
} 

@HandlerAnnotation(type = RequestType.B) // 注解标记 B 类型 的 处理 者 
class ConcreteHandlerB extends Handler { 
public void handleRequest(Request request) {
// handle B 
}
} 

@HandlerAnnotation(type = RequestType.C)//注解标记 C 处理者 
class ConcreteHandlerC extends Handler{ 
public void handleRequest(Request request){ 
//handle C 
}
}

②使用一个默认处理者来应对未知类型或异常情况,并且根据请求频率或优先级来调整链中各个顺序。 例如:

  • 产生很多细粒度的对象。因为功能处理都分散到了单独的职责对象中,每个对象功能单一,要把整个流程处理完,需要很多的职责对象。例如,我们每种类型的请求都需要一个对应的处理类,这样会导致系统中出现很多小类,增加了系统复杂度和开发难度。
//每种类型的请求都需要一个对应的处理类
class RequestTypeA {}// A 类型的请求 
class RequestTypeB {}// B 类型的请求 
class RequestTypeC {}// C 类型的请求 
class ConcreteHandlerA extends Handler {// A 类型的处理者 
public void handleRequest(Request request) { 
if (request instanceof RequestTypeA) { 
// handle A 
} else { 
successor.handleRequest(request); // 转交给后继者处理
} 
} 
} 
class ConcreteHandlerB extends Handler { // B 类型的处理者 
public void handleRequest(Request request) { 
if (request instanceof RequestTypeB) { 
// handle B 
} else { 
successor.handleRequest(request); // 转交给后继者处理 
}
} 
} 
class ConcreteHandlerC extends Handler { // C 类型的处理者 
public void handleRequest(Request request) { 
if (request instanceof RequestTypeC) { 
// handle C 
} else { 
successor.handleRequest(request); 
// 转交给后继者处理 
} 
} 
} 

②使用一个默认处理者来应对未知类型或异常情况,并且根据请求频率或优先级来调整链中各个处 理 者 的 顺 序 。

  • 例如,使用一个默认处理者来应对未知类型或异常情况,并且根据请求频率或优先级来调整链中各个处理者的顺序。
  • 例如,我们可以用一个默认处理者来处理所有无法识别的请求,并且把最常见或最重要的请求类型放在链的前面,以提高效率和用户体验。
class DefaultHandler extends Handler { // 默认处理者
    public void handleRequest(Request request) {
        // default handle
    }
}

Handler handlerA = new ConcreteHandlerA(); 
Handler handlerB = new ConcreteHandlerB(); 
Handler handlerC = new ConcreteHandlerC(); 
Handler defaultHandler = new DefaultHandler(); // 创建默认处理者
handlerA.setSuccessor(handlerB); 
handlerB.setSuccessor(handlerC); 
handlerC.setSuccessor(defaultHandler); // 设置 C 的后继者为默认处理者

// 假设 B 类型的请求最常见,那么可以把 B 放在链的前面
handlerB.setSuccessor(handlerA);
handlerA.setSuccessor(handlerC);
handlerC.setSuccessor(defaultHandler);

五、和其他相似的设计模式比较

设计模式相似之处不同之处应用场景
过滤器模式将请求和处理分离,可以在请求到达目标之前或之后执行一些操作需要定义不同的标准来过滤一组对象,可以自由组合过滤器,不一定需要处理所有请求Java Web 开发中的 Filter 过滤器,Spring MVC 中的拦截器
命令模式将请求和处理分离,可以将命令封装成对象,并传递给不同的处理者可以实现撤销和重做功能,可以将多个命令组合成宏命令,只能有一个接收者处理请求GUI 中的按钮操作,数据库事务管理, 线程池中的任务调度
中介者模式将请求和处理分离,可以减少对象之间的耦合度需要定义一个中介者对象来封装多个对象之间的交互逻辑, 可以动态地改变对象之间的关系, 中介者可能会变得复杂和庞大MVC 模式中的控制器层, 聊天室程序中的服务器端程序, 机场调度系统中的塔台控制员
责任链模式将请求和处理分离,可以使多个对象都有机会处理这个请求需要按照顺序将请求动态传递给一系列的潜在接收者 ,直至其中一个接收者对请求进行处理 ,每个接收者都知道下一个接收者是谁,不能保证一定有接收者能够处理请求Java Web 开发中的 Filter 过滤器链, 日志记录框架 Log4j 中的日志级别过滤