Spiga

模式开发之旅(20):职责链模式

2010-08-30 23:26:16

之前介绍了模板模式、策略模式。今天来介绍职责链模式,这三种模式具有相同的作用:复用和扩展,在实际的项目开发中比较常用,特别是框架开发中,我们可以利用它们来提供框架的扩展点,能够让框架的使用者在不修改框架源码的情况下,基于扩展点定制化框架的功能。

职责链模式:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。

关于职责链模式,我们先来看看它的代码实现。结合代码实现,你会更容易理解它的定义。职责链模式有多种实现方式,比较常用的2种:

1. 利用链表实现

实现方式如下所示。其中,Handler 是所有处理器类的抽象父类,Handle() 是抽象方法。每个具体的处理器类(HandlerA、HandlerB)的 Handle() 方法的代码结构类似,如果它能处理该请求,就不继续往下传递;如果不能处理,则交由后面的处理器来处理(也就是调用 successor.Handle())。HandlerChain 是处理器链,从数据结构的角度来看,它就是一个记录了链头、链尾的链表。其中,记录链尾是为了方便添加处理器。

public abstract class Handler 
{ 
    protected Handler successor = null;
    public void SetSuccessor(Handler successor) 
    {
        this.successor = successor;
    }
    public abstract void Handle(); 
}

public class HandlerA : Handler { 
    public override void Handle()
    {
        bool handled = false; 
        //... 
        if (!handled && successor != null) 
        {
            successor.Handle(); 
        }
    }
}

public class HandlerB : Handler
{
    public override void Handle()
    {
        bool handled = false;
        //... 
        if (!handled && successor != null)
        {
            successor.Handle();
        }
    }
}
public class HandlerChain
{
    private Handler head = null; 
    private Handler tail = null; 
    public void AddHandler(Handler handler) 
    {
        handler.SetSuccessor(null);
        if (head == null) 
        {
            head = handler; 
            tail = handler;
            return;
        } 
        tail.SetSuccessor(handler); 
        tail = handler;
    }
    public void Handle() 
    {
        if (head != null)
        {
            head.Handle();
        }
    }
}
// 使用举例
public class Application
{
    public static void main(string[] args)
    { 
        HandlerChain chain = new HandlerChain(); 
        chain.AddHandler(new HandlerA()); 
        chain.AddHandler(new HandlerB()); 
        chain.Handle();
    }
}

实际上,上面的代码实现不够优雅。处理器类的 Handle() 方法,不仅包含自己的业务逻辑,还包含对下一个处理器的调用,也就是代码中的 successor.handle()。一个不熟悉这种代码结构的程序员,在添加新的处理器类的时候,很有可能忘记在 Handle() 方法中调用 successor.Handle(),这就会导致代码出现 bug。

针对这个问题,我们对代码进行重构,利用模板模式,将调用 successor.Handle() 的逻辑从具体的处理器类中剥离出来,放到抽象父类中。这样具体的处理器类只需要实现自己的业务逻辑就可以了。重构之后的代码如下所示:

public abstract class Handler
{ 
    protected Handler successor = null; 
    public void SetSuccessor(Handler successor)
    {
        this.successor = successor;
    }
    public void Handle() 
    {
        bool handled = DoHandle();
        if (successor != null && !handled)
        {
            successor.Handle();
        }
    }
    protected abstract bool DoHandle();
}
public class HandlerA : Handler { 
    protected override bool DoHandle()
    {
        bool handled = false; 
        //... 
        return handled; 
    }
}
public class HandlerB : Handler
{
    protected override bool DoHandle()
    {
        bool handled = false;
        //... 
        return handled;
    }
}

public class HandlerChain
{
    private Handler head = null; 
    private Handler tail = null; 
    public void AddHandler(Handler handler) 
    {
        handler.SetSuccessor(null);
        if (head == null) 
        {
            head = handler; 
            tail = handler;
            return;
        } 
        tail.SetSuccessor(handler); 
        tail = handler;
    }
    public void Handle() 
    {
        if (head != null)
        {
            head.Handle();
        }
    }
}
// 使用举例
public class Application
{
    public static void main(string[] args)
    { 
        HandlerChain chain = new HandlerChain(); 
        chain.AddHandler(new HandlerA()); 
        chain.AddHandler(new HandlerB()); 
        chain.Handle();
    }
}

利用数组实现

我们再来看第二种实现方式,代码如下所示。这种实现方式更加简单。HandlerChain 类用数组而非链表来保存所有的处理器,并且需要在 HandlerChain 的 Handle() 方法中,依次调用每个处理器的 Handle() 方法。

public interface IHandler
{ 
    bool Handle();
}
public class HandlerA : IHandler
{ 
    public bool Handle()
    {
        bool handled = false; 
        //... 
        return handled; 
    }
}
public class HandlerB : IHandler
{
    public bool Handle()
    {
        bool handled = false;
        //... 
        return handled;
    }
}

public class HandlerChain
{
    private List<IHandler> handlers = new List<IHandler>();
    public void AddHandler(IHandler handler) 
    {
        handlers.Add(handler);
    }
    public void Handle() 
    {
        foreach (var item in handlers)
        {
            bool handled = item.Handle();
            if (handled) break;
        }
    }
}
// 使用举例
public class Application
{
    public static void main(string[] args)
    { 
        HandlerChain chain = new HandlerChain(); 
        chain.AddHandler(new HandlerA()); 
        chain.AddHandler(new HandlerB()); 
        chain.Handle();
    }
}

在 GoF 给出的定义中,如果处理器链上的某个处理器能够处理这个请求,那就不会继续往下传递请求。实际上,职责链模式还有一种变体,那就是请求会被所有的处理器都处理一遍,不存在中途终止的情况。这种变体也有两种实现方式:用链表存储处理器和用数组存储处理器,跟上面的两种实现方式类似,只需要稍微修改即可。

这里只给出其中一种实现方式,如下所示

public abstract class Handler
{ 
    protected Handler successor = null; 
    public void SetSuccessor(Handler successor)
    {
        this.successor = successor;
    }
    public void Handle() 
    {
        DoHandle();
        if (successor != null)
        {
            successor.Handle();
        }
    }
    protected abstract void DoHandle();
}
public class HandlerA : Handler { 
    protected override void DoHandle()
    {
        //... 
    }
}
public class HandlerB : Handler
{
    protected override void DoHandle()
    {
        //... 
    }
}

public class HandlerChain
{
    private Handler head = null; 
    private Handler tail = null; 
    public void addHandler(Handler handler) 
    {
        handler.SetSuccessor(null);
        if(head == null)
        {
            head = handler; 
            tail = handler;
            return;
        } 
        tail.SetSuccessor(handler); 
        tail = handler;
    }
    public void Handle() 
    {
        if (head != null)
        {
            head.Handle();
        }
    }
}
// 使用举例
public class Application
{
    public static void main(string[] args)
    { 
        HandlerChain chain = new HandlerChain(); 
        chain.addHandler(new HandlerA()); 
        chain.addHandler(new HandlerB()); 
        chain.Handle();
    }
}