解释器 责任链_责任链-解释

解释器 责任链

介绍 (Introduction)

This article discusses the Chain of Responsibility pattern, explaining

本文讨论了责任链模式,解释了

  • What it is;

    这是什么;
  • Why it is; and

    为什么呢? 和
  • How it is

    怎么样

背景 (Background)

在您的编程生涯中,您可能已经遇到了以下情况:您有多个命令或请求,并且每个命令必须以某种特定方式进行处理。 为此,我们创建了处理这些命令的处理程序对象。 我们以一对一的映射方式创建这些命令处理程序,即一个命令或请求的一个处理程序。

Generally, handlers are selected by the type of request, and this is done by a method that might handle the selection via an if/then/else statement, though in some cases typeOf or type checking is used. The problem is with the "if" condition. This kind of common problem can be resolved easily by designing an object hierarchy based on good object-oriented design principles. One of the best applicable patterns is described by the Gang of Four as "Chain of Responsibility".

通常,处理程序是根据请求的类型选择的,这是通过可能通过if / then / else语句处理选择的方法完成的,尽管在某些情况下使用typeOf或类型检查。 问题出在“如果”条件下。 通过基于良好的面向对象设计原则设计对象层次结构,可以轻松解决此类常见问题。 四人帮称为“责任链”是最适用的模式之一。

它是什么? (What is it?)

The Chain of Responsibility is a behavioural pattern, as it is used to manage algorithms, relationships and responsibilities between objects. According to GoF, the formal definition is

责任链是一种行为模式,因为它用于管理对象之间的算法,关系和职责。 根据GoF,正式定义是

Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it.

通过给一个以上的对象一个处理请求的机会,避免将请求的发送者耦合到其接收者。 链接接收对象,并沿着链传递请求,直到对象处理该请求为止。

This pattern puts multiple command handler objects into the chain. Each command handler contains the reference of the next object in the chain, so basically it creates a linklist of handlers. The request or command is passed along this chain of objects; each object checks the request to determine whether it can process the request or not. If current object can't process the request, it simply ignores the request and pushed the original request to next object in chain, thus giving another object the opportunity to process the request. If the current object is capable of processing the request then it processes the request. Once the processing is done, the current object can either exit the chain or pass the original request to the next handler in the chain for further processing.

此模式将多个命令处理程序对象放入链中。 每个命令处理程序都包含链中下一个对象的引用,因此基本上它会创建处理程序的链接列表。 请求或命令沿着该对象链传递。 每个对象都会检查请求,以确定它是否可以处理该请求。 如果当前对象无法处理该请求,则它只会忽略该请求并将原始请求推入链中的下一个对象,从而为另一个对象提供处理该请求的机会。 如果当前对象能够处理该请求,则它将处理该请求。 处理完成后,当前对象可以退出链,也可以将原始请求传递给链中的下一个处理程序以进行进一步处理。

This pattern is all about providing a mechanism to allow a number of objects to attempt to handle a request, independently of any other object in the chain. Once the request is handled, it either exits from that point in the chain or travels along through the complete chain.

此模式的全部目的是提供一种机制,允许多个对象尝试独立于链中的任何其他对象来尝试处理请求。 处理完请求后,它要么从链中的该点退出,要么沿整个链传播。

为什么? (Why is it?)

As discussed earlier in this article, generally handlers are selected through if/then/else or switch statements. The following problems are possible if we use this kind of statement

如本文前面所述,通常通过if / then / else或switch语句选择处理程序。 如果使用这种陈述,可能会出现以下问题

  • The client must know about the command handler objects, i.e. you have to define handlers explicitly in your code. So the client and the handler objects are tightly coupled.

    客户端必须了解命令处理程序对象,即您必须在代码中显式定义处理程序。 因此,客户端和处理程序对象紧密耦合。
  • As the process is decided at design time, it is almost impossible to determine if multiple "if" objects (determined at runtime) are candidates to handle a request without duplication of code.

    由于该过程是在设计时决定的,因此几乎不可能确定是否有多个“如果”对象(在运行时确定)是否是无需重复代码即可处理请求的候选对象。
  • System design in this way is less extensible and requires a lot of impact analysis while doing code changes in maintenance, as any changes in handler objects can easily break the client code.

    这种方式的系统设计扩展性较差,并且在进行维护代码更改时需要进行大量影响分析,因为处理程序对象中的任何更改都可能轻易破坏客户端代码。
  • It decouples the command from its handler objects. No direct explicit code is required to map between one and the other.

    它将命令与其处理程序对象解耦。 不需要直接的显式代码即可在彼此之间进行映射。
  • More than one handler can be made to process one command, and the client doesn't even need this information about such behaviour. So, such changes can be subtracted from the client.

    可以使一个以上的处理程序处理一个命令,并且客户端甚至不需要此行为信息。 因此,可以从客户端减去此类更改。
  • As all handler objects are added to the chain, each can be associated or disassociated to the chain dynamically.

    将所有处理程序对象添加到链后,每个对象都可以动态地关联或取消关联到链。

()

How is it?

如何?

In this pattern, one or more request handlers are associated to create a single chain. Each request handler object keeps a single reference to the next handler in the queue. The request is pushed to first handler in the queue that inspects whether it can process the request or not, and then it is pushed to the next handler in the queue and so on.

在这种模式下,一个或多个请求处理程序被关联以创建单个链。 每个请求处理程序对象都对队列中的下一个处理程序保留单个引用。 该请求被推送到队列中的第一个处理程序,以检查它是否可以处理该请求,然后将其推送到队列中的下一个处理程序,依此类推。

This is applicable to those scenarios where there are many different types of request and each request has specific processing requirements, where the processing logic is contained inside another object, and where we want both the request and the handler object to couple together explicitly in the code.

这适用于以下情况:请求的类型很多,每个请求都有特定的处理要求,处理逻辑包含在另一个对象中,并且我们希望请求和处理程序对象在代码中明确地耦合在一起。

Let’s start with a scenario. I have an application (service) that talks to multiple devices running on multiple platforms (iPhone, Android and Windows). There are different applications for each mobile device type with which the application service communicates. For the sake of simplicity, I will use it like the base of our example.

让我们从一个场景开始。 我有一个应用程序(服务),可以与在多个平台(iPhone,Android和Windows)上运行的多个设备进行通讯。 应用程序服务与之通信的每种移动设备类型都有不同的应用程序。 为了简单起见,我将其用作示例的基础。

The application service allows the user to perform following operations on any of the devices:

应用程序服务允许用户在任何设备上执行以下操作:

  • Fetch/Search Contact List

    获取/搜索联系人列表
  • Fetch Messages

    提取消息
  • Fetch Memory Information

    获取内存信息

The flow of our example is illustrated by the diagram below.

下图说明了示例的流程。

COR-Diagram.png
 
        /// <summary>
        /// Enum to define different types of Requests.
        /// </summary>
        internal enum RequestType
        {
            Contact,
            Messages,
            Memory
        }  

First, an abstract class is defined that will be base class for all the concrete request classes that exist in our application. It holds only the abstract behaviour whose concrete implementation must be provided by the derived classes. A single parameter-less contractor is defined to keep track of the type of request, i.e. whether it is of type Contact or Message or Memory.

首先,定义一个抽象类,它将作为我们应用程序中存在的所有具体请求类的基类。 它仅包含抽象行为,抽象行为的具体实现必须由派生类提供。 定义了一个无参数的承包商来跟踪请求的类型,即请求的类型是

  
        /// <summary>
        ///  Base class for all Requests.
        /// </summary>
        internal abstract class RequestBase
        {
            public readonly RequestType type ;
            protected RequestBase(RequestType type)
            {
                this.type = type;
            }
        } 

Concrete request classes are defined which implement the RequestBase base class. The concrete request classes will hold data specific to their own requirements. Basically, these classes are DTOs (data transfer objects) that hold only data; they generally do not know anything about the data or how it will be processed. The processing algorithms and logic are contained in a separate class known as Request Handlers (in current context), which will be seen in action later in this article.

定义了实现RequestBase基类的具体请求类。 具体的请求类将保存特定于其自身需求的数据。 基本上,这些类是仅保存数据的DTO(数据传输对象)。 他们通常对数据或如何处理一无所知。 处理算法和逻辑包含在一个单独的类中,该类称为“请求处理程序”(在当前上下文中),本文稍后将对其进行介绍。

The following code demonstrates the three concrete implementations of the requests Contact RequestMessage Request and the Memory Request.

以下代码演示了请求

   #region Request Implementation

        /// <summary>
        /// Contact Request object to hold contact information.
        /// </summary>
        internal class ContactRequest : RequestBase
        {
            public int DeviceID { get; private set; }
            public string ContactName { get; private set; }

            public ContactRequest(int ID, string name) : base(RequestType.Contact)
            {
                this.DeviceID = ID;
                this.ContactName = name;
            }
        }

        /// <summary>
        /// Message Request object to hold Messages information.
        /// </summary>
        internal class MessagesRequest : RequestBase
        {
            
            public int DeviceID { get; private set; }
            public string Message { get; private set; }

            public MessagesRequest(int ID, string message)
                : base(RequestType.Messages)
            {
                this.DeviceID = ID;
                this.Message = message;
            }

        }

        /// <summary>
        /// Memory Request object to hold Memory information.
        /// </summary>
        internal class MemoryRequest : RequestBase
        {
            public int DeviceID { get; private set; }
            public int TotalMemory { get; private set; }
            public int MemoryLeft { get; private set; }

            public MemoryRequest(int ID, int totalMemory, int memoryLeft) : base(RequestType.Memory)
            {
                this.DeviceID = ID;
                this.TotalMemory = totalMemory;
                this.MemoryLeft = memoryLeft;
            }

        }

       
        #endRegion "End of Request Implementation"  

That's it. We have defined all the request classes that our application will deal with.

而已。 我们已经定义了应用程序要处理的所有请求类。

Now, we define another abstract class RequestHandlerBase that will be base class for all the request Handlers, as all handlers have some common behaviours to share. This base class will also serve the purpose of being the connecting link to create a queue or chain. These links will contain the reference to another object in the list; in our example NextHandler is going to hold the reference.

现在,我们定义另一个抽象类RequestHandlerBase ,它将作为所有请求处理程序的基类,因为所有处理程序都有一些共同的行为要共享。 该基类还将用作连接队列以创建队列或链的目的。 这些链接将包含对列表中另一个对象的引用。 在我们的示例中, NextHandler将保留引用。

        /// <summary>
        /// Base class for all Request Handlers.
        /// </summary>
        internal abstract class RequestHandlerBase
        {
            private RequestHandlerBase NextHandler { get; set; }

            public void Execute(RequestBase request)
            {
                this.Process(request);
                PushNext(request);
            }

            protected abstract void Process(RequestBase request);

            public RequestHandlerBase SetNextHandler(RequestHandlerBase handler)
            {
                this.NextHandler = handler;
                return this.NextHandler;
            }
            
            private void PushNext(RequestBase request)
            {
                if (this.NextHandler != null)
                    this.NextHandler.Execute(request);
            }
        }  

Once the abstract class for the handler is defined, we are now ready to define concrete implementations for the Contact, Message and Memory requests. These request handlers know how to process specific requests and they hold the request's specific business logic. For example, the ContactRequestHandler object knows how to process the ContactRequest objects.

一旦定义了处理程序的抽象类,我们现在就可以为Contact,Message和Memory请求定义具体的实现了。 这些请求处理程序知道如何处理特定的请求,并且它们保存请求的特定业务逻辑。 例如, ContactRequestHandler对象知道如何处理ContactRequest对象。

        #region Request Handler Implementation

        /// <summary>
        /// Contact Handler object to Handle Contact Request information.
        /// </summary>
        internal class ContactRequestHandler : RequestHandlerBase
        {
            protected override void Process(RequestBase request)
            {
                if (request.type == RequestType.Contact)
                {
                    var contact = (ContactRequest)request;
                    Console.WriteLine("Processing Contact Request. \n Device ID-> {0} - Contact Name ->{1}", contact.DeviceID, contact.ContactName);
                }
            }
        }

        /// <summary>
        /// Messages Handler object to Handle Message Request information.
        /// </summary>
        internal class MessagesRequestHandler : RequestHandlerBase
        {
            protected override void Process(RequestBase request)
            {
                if (request.type == RequestType.Messages)
                {
                    var message = (MessagesRequest)request;
                    Console.WriteLine("Processing Messages Request. \n Device ID-> {0} - Message ->{1}", message.DeviceID, message.Message);
                }
            }
        }

        /// <summary>
        /// Memory Handler object to Handle Memory Request information.
        /// </summary>
        internal class MemoryRequestHandler : RequestHandlerBase
        {
            protected override void Process(RequestBase request)
            {
                if (request.type == RequestType.Memory)
                {
                    var memory = (MemoryRequest)request;
                    Console.WriteLine("Processing Messages Request. \n Device ID-> {0} - Total Memory ->{1}  - Memory Left -> {2}", memory.DeviceID, memory.TotalMemory, memory.MemoryLeft);
                }
            }
        }
        
        #endregion "End of Request Handler Implementation"   

Now execute this. In the main section, we will create and associate all the handlers to form a chain, and then different requests are created that need to be processed.

现在执行这个。 在主要部分,我们将创建并关联所有处理程序以形成一个链,然后创建需要处理的不同请求。

Note : In a real application, the creation of the handlers may be delegated to the some builder objects that can have the mechanisms to dynamically create the lists of handlers based on certain rules. To keep this example simple and small, I have just created these objects in the main section itself.

注意:在实际的应用程序中,可以将处理程序的创建委派给某些生成器对象,这些对象可以具有根据某些规则动态创建处理程序列表的机制。 为了使该示例简单易行,我刚刚在主体部分中创建了这些对象。

 
        static void Main(string[] args)
        {
            // Create Request Handler Lists.
            var handler = new ContactRequestHandler();
            handler.SetNextHandler(new MessagesRequestHandler())
            .SetNextHandler(new MemoryRequestHandler());

            
            // Create Multiple requests.
            List request = new List();
            request.Add(new ContactRequest(2342244, "Nokia-X23"));
            request.Add(new MessagesRequest(2342244, "Hello Everyone ! how r u?"));
            request.Add(new MemoryRequest(2342244,2048,543));
            
            request.ForEach(x => {
                handler.Execute(x);
            });
        }

Once we execute, the following will be the output.

执行后,将输出以下内容。

Pic-2.png

What is happening here is that we create lists of different requests, and each request is pushed to first handler object in the queue. That handler object checks the type of object it received to identify whether it should process this object or not. If the handler object found the request suitable for processing then it starts processing, and otherwise it simply ignores the request object and passes it to the next handler via the reference it holds. Thus the whole process works.

这里发生的是我们创建了不同请求的列表,并且每个请求都被推送到队列中的第一个处理程序对象。 该处理程序对象检查收到的对象的类型,以标识是否应处理该对象。 如果处理程序对象找到适合处理的请求,则它将开始处理,否则它将忽略该请求对象,并通过其持有的引用将其传递给下一个处理程序。 这样,整个过程就可以了。

How does this affect the extensibility and maintainability of the application?

这如何影响应用程序的可扩展性和可维护性?

Suppose we are asked to implement a new requirement about Location to our sample application. The customer wants the application to capable of handling the Location information.

假设要求我们对示例应用程序实现有关位置的新要求。 客户希望应用程序能够处理位置信息。

As this is a new request type, we add it to the RequestType Enum.

由于这是新的请求类型,因此将其添加到RequestType枚举。

        /// <summary>
        /// Enum to define different types of Requests.
        /// </summary>
        internal enum RequestType
        {
            Contact,
            Messages,
            Memory,
            Location
        }  

Then define a LocationRequest class to represent the Location request to hold location specific data. Remember, it should be derived from the RequestBase class.

然后定义一个LocationRequest类,以表示Location请求以保存特定于位置的数据。 请记住,它应该派生自RequestBase类。

        /// <summary>
        /// Location Request object to hold Location information.
        /// </summary>
        internal class LocationRequest : RequestBase
        {
            public int DeviceID { get; private set; }
            public int Latitude  { get; private set; }
            public int Longitude  { get; private set; }

            public LocationRequest(int ID, int latitude, int longitude) : base(RequestType.Location)
            {
                this.DeviceID = ID;
                this.Latitude = latitude;
                this.Longitude = longitude;
            }

        }  

Then define a handler for this newly defined request. Let’s name it LocationRequestHandler class and make it derived from the RequestHandlerBase class.

然后为这个新定义的请求定义一个处理程序。 让我们将其命名为LocationRequestHandler类,并使它派生自RequestHandlerBase类。

 
        /// <summary>
        /// Location Handler object to Handle Location Request information.
        /// </summary>
        internal class LocationRequestHandler : RequestHandlerBase
        {
            protected override void Process(RequestBase request)
            {
                if (request.type == RequestType.Location)
                {
                    var location = (LocationRequest)request;
                    Console.WriteLine("Processing Location Request. \n Device ID-> {0} -  Latitude->{1}  - Longitude -> {2}", location.DeviceID, location.Latitude, location.Longitude);
                }
            }
        }

Attach this LocationReqeustHandler to the Handler lists, as we have done below in the main section.

像下面在主要部分中所做的那样,将此LocationReqeustHandler附加到Handler列表。

 
        static void Main(string[] args)
        {
            // Create Request Handler Lists.
            var handler = new ContactRequestHandler();
            handler.SetNextHandler(new MessagesRequestHandler())
            .SetNextHandler(new MemoryRequestHandler())
            .SetNextHandler(new LocationRequestHandler());

            
            // Create Multiple requests.
            List<RequestBase> request = new List<RequestBase>();
            request.Add(new ContactRequest(2342244, "Nokia-X23"));
            request.Add(new MessagesRequest(2342244, "Hello Everyone ! how r u?"));
            request.Add(new MemoryRequest(2342244,2048,543));
            request.Add(new LocationRequest(2134324, 23434, 4645654));
            request.ForEach(x => {
                handler.Execute(x);
            });
        }   

After executing this, following result will be shown on the console.

执行此操作后,以下结果将显示在控制台上。

Pic-1.png

We have successfully added a new feature to our example in the form of Request without changing the existing code much, so this allows the system to be more extensible and maintainable without the risk of breaking the existing code.

我们已经以请求的形式成功地向我们的示例添加了一个新功能,而无需太多更改现有代码,因此这使系统具有更高的可扩展性和可维护性,而不会破坏现有代码。

Points of Interest

兴趣点

This pattern is recommended when either of the following scenarios occur in your application:

当您的应用程序中发生以下两种情况之一时,建议使用此模式:

  • When there are multiple objects that can handle a specific request. In other words, you don't want to specify handlers explicitly in your code.

    当有多个对象可以处理特定请求时。 换句话说,您不想在代码中显式指定处理程序。
  • When the handler is to be decided dynamically, i.e. which handler object should be allowed to handle the request objects is determined at runtime

    当要动态决定处理程序时,即在运行时确定应允许哪个处理程序对象处理请求对象
  • If a request not being handled, then is this an acceptable scenario, i.e. no exception is thrown at any given point of time, but rather is simply ignored by the system.

    如果未处理请求,那么这是否可以接受,即在任何给定时间点都不会引发异常,而是系统会简单地忽略它。
  • When you want to decouple the request from its sender to its receiver or processor objects.

    当您想将请求从发送者分离到接收者或处理器对象时。

Remember:

记得:

  • Many other patterns like Mediator, Command and Observer also decouple the senders of the requests from its receiver’s objects, as the Chain of Responsibility pattern does, but they all achieve this in a different way. Chain of Responsibility uses the chain of handlers to pass the request through it.

    像“责任链”模式一样,许多其他模式(例如Mediator,Command和Observer)也将请求的发送者与其接收者的对象分离开来,但是它们都以不同的方式来实现。 责任链使用处理程序链通过它传递请求。
  • Patterns can me mixed to achieve the best result. For example, the Command pattern can be used to wrap the request, and Chain of Responsibility can decouple the requests from their handlers.

    我可以混合模式以获得最佳结果。 例如,命令模式可用于包装请求,责任链可将请求与处理程序分离。
ChainOfResponsibility.zip ChainOfResponsibility.zip

翻译自: https://www.experts-exchange.com/articles/17702/Chain-of-Responsibility-ReExplained.html

解释器 责任链

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值