设计模式(4)——责任链模式

105 阅读4分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第32天,点击查看活动详情


责任链模式是一种软件设计模式,用于对请求进行处理,并将请求的处理责任分派给一系列的处理器。

在责任链模式中,请求从一个处理器开始,然后逐个传递给下一个处理器,直到最终的处理器能够处理该请求为止。这种模式的优点在于,它允许系统动态地增加或删除处理器,并且可以轻松地改变处理器的执行顺序。

下面是用 Rust 语言实现责任链模式的一个示例:

struct Request {
    content: String,
}

trait Handler {
    fn set_next(&mut self, handler: Box<dyn Handler>);
    fn handle_request(&self, request: &Request);
}

struct ConcreteHandlerA {
    next: Option<Box<dyn Handler>>,
}

impl ConcreteHandlerA {
    fn new() -> ConcreteHandlerA {
        ConcreteHandlerA { next: None }
    }
}

impl Handler for ConcreteHandlerA {
    fn set_next(&mut self, handler: Box<dyn Handler>) {
        self.next = Some(handler);
    }

    fn handle_request(&self, request: &Request) {
        if request.content.starts_with("A") {
            println!("ConcreteHandlerA: Handling request with content starting with 'A'");
        } else {
            if let Some(ref next) = self.next {
                next.handle_request(request);
            }
        }
    }
}

struct ConcreteHandlerB {
    next: Option<Box<dyn Handler>>,
}

impl ConcreteHandlerB {
    fn new() -> ConcreteHandlerB {
        ConcreteHandlerB { next: None }
    }
}

impl Handler for ConcreteHandlerB {
    fn set_next(&mut self, handler: Box<dyn Handler>) {
        self.next = Some(handler);
    }

    fn handle_request(&self, request: &Request) {
        if request.content.starts_with("B") {
            println!("ConcreteHandlerB: Handling request with content starting with 'B'");
        } else {
            if let Some(ref next) = self.next {
                next.handle_request(request);
            }
        }
    }
}

struct ConcreteHandlerC {
    next: Option<Box<dyn Handler>>,
}

impl ConcreteHandlerC {
    fn new() -> ConcreteHandlerC {
        ConcreteHandlerC { next: None }
    }
}

impl Handler for ConcreteHandlerC {
    fn set_next(&mut self, handler: Box<dyn Handler>) {
        self.next = Some(handler);
    }

    fn handle_request(&self, request: &Request) {
        if request.content.starts_with("C") {
            println!("ConcreteHandlerC: Handling request with content starting with 'C'");
        } else {
            if let Some(ref next) = self.next {
                next.handle_request(request);
            }
        }
    }
}

fn main() {
    let mut handler_a = ConcreteHandlerA::new();
    let mut handler_b = ConcreteHandlerB::new();
    let mut handler_c = ConcreteHandlerC::new();

    handler_a.set_next(Box::new(handler_b));
    handler_b.set_next(Box::new(handler_c));

    let request = Request { content: "A message".to_string() };
    handler_a.handle_request(&request);

    let request = Request { content: "B message".to_string() };
    handler_a.handle_request(&request);

    let request = Request { content: "C message".to_string() };
    handler_a.handle_request(&request);

    let request = Request { content: "D message".to_string() };
    handler_a.handle_request(&request);
}

在这个示例中,我们定义了一个名为 Request 的结构体,其中包含一个名为 content 的字符串字段。这个结构体表示一个请求,其中 content 字段表示请求的内容。

然后,我们定义了一个名为 Handler 的 trait,其中包含两个方法:set_nexthandle_requestset_next 方法允许将下一个处理器设置为给定的处理器,而 handle_request 方法允许处理器处理给定的请求。

然后,我们定义了三个具体的处理器(ConcreteHandlerAConcreteHandlerBConcreteHandlerC),它们分别能够处理内容以字母 A、B 或 C 开头的请求。每个具体的处理器都包含一个名为 next 的字段,表示下一个处理器。

main 函数中,我们创建了三个处理器并将它们连接在一起,形成一条责任链。然后,我们创建了四个请求并将它们传递给链的起点(即 handler_a)。每个处理器将检查请求的内容,并根据是否能够处理请求来决定是否转发请求。

如果处理器能够处理请求,则它会打印一条消息,表示它正在处理请求。如果处理器不能处理请求,则它会转发请求到下一个处理器,并打印一条消息表示正在转发请求。这样,请求将沿着责任链传递,直到到达末尾。

当所有的处理器都无法处理请求时,请求将被丢弃。这个示例的代码实现了责任链模式的基本思想,你可以根据自己的需要进行修改和扩展。

责任链模式具有以下优点:

  • 减少了对象间的耦合。责任链模式使得每个对象只需要知道其直接的下一个对象,而不需要知道整个链的结构。这减少了对象间的耦合,使得对象更易于复用和维护。
  • 增加或改变处理程序的方便性。责任链模式允许你增加或改变处理程序的方便性。如果需要增加新的处理程序,只需要在责任链的末尾添加新的处理器即可。同样,如果需要改变处理程序的执行顺序,只需要调整责任链中处理器的顺序即可。
  • 提供了请求的传递路径。责任链模式为请求的传递提供了一条明确的路径,这有助于确定请求最终是否被处理,并且可以帮助开发人员调试系统。
  • 允许每个处理器决定是否处理请求。责任链模式允许每个处理器自己决定是否处理请求。这有助于减少系统的复杂性,并使得系统更易于维护和扩展。

但它同时也有一些缺点:

  • 责任链可能会变得很长。如果链中包含较多的处理器,则责任链可能会变得很长,这可能会导致系统性能下降。
  • 不太好调试。责任链模式的缺点之一是调试可能会变得困难。因为请求可能会在链的任意位置中断,所以必须对链中的每个处理器进行检查,才能确定出错的原因。
  • 可能会导致滥用。如果不当使用责任链模式,可能会导致滥用,这会使系统变得复杂,并降低系统的性能。