责任链与类列表相比有哪些优点?

发布于 2024-07-25 05:20:11 字数 1311 浏览 8 评论 0原文

最近,我正在与另一位程序员讨论重构一个充满“if”语句的巨大(1000 行)方法的最佳方法。

代码是用 Java 编写的,但我想这个问题也可能发生在其他语言(例如 C#)中。

为了解决这个问题,他建议使用责任链模式。 他建议建立一个“Handler”基类。 然后,“Handler1”、“Handler2”等将扩展“Handler”。
然后,处理程序将有一个“getSuccessor”方法,该方法将返回 null(如果它是链中的最后一个)或链中的下一个处理程序。
然后,“handleRequest(Request)”函数要么处理 Request,要么将其传递到链的下一个,如果前面的解决方案都不起作用,它将仅返回 null 或引发异常。
要向链中添加新的处理程序,编码器将转到链的最后一个元素并告诉它有一个新元素。 为了做某事,他只需调用链的第一个元素上的handleRequest。

为了解决这个问题,我建议使用不同的方法。
我还有一个基本的“Handler”类,其中包含“Handler1”、“Handler2”,就像前面提到的方法一样。
但是,不会有“getSuccessor”方法。 相反,我有一个带有处理程序列表的 Collection 类(Vector、ArrayList 或本例中最好的任何类型)。
handleRequest 函数仍然存在,但它不会将调用传播到下一个处理程序。 它只会处理请求或返回 null。
为了处理请求,可以使用

for(Handler handle : handlers){
    result = handle.handleRequest(request);
    if(result!=null) return result;
}
throw new CouldNotParseRequestException(); //just like in the other approach

或者,为了防止代码重复,可以将“parseRequest(request)”方法添加到集合类中。 要添加新的处理程序,可以转到集合构造函数(或 static{} 块,或等效的东西)并简单地添加代码“addHandler(new Handler3());”。

这种方法到底缺少责任链的哪些优点? 哪种方法最好(假设有最好的方法)? 为什么? 每种设计方法会导致哪些潜在的错误和问题?

对于那些需要上下文的人,原始代码如下:

if(x instanceof Type1)
{
//doSomething1
} else if(x instanceof Type2)
{
//doSomething2
}
//etc.

Recently, I was discussing with another programmer the best way to refactor a huge(1000 lines) method full of "if" statements.

The code is written in Java, but I guess this issue could happen in other languages such as C# as well.

To solve this problem, he suggested using a chain-of-responsibility pattern.
He proposed having a base "Handler" class. Then, "Handler1", "Handler2", etc. would extend "Handler".
Then, handlers would have a "getSuccessor" method, which would either return null(if it was the last of the chain) or the next Handler of the chain.
Then, a "handleRequest(Request)" function would either deal with Request, or pass it to the next of the chain and, if none of the previous solutions worked, it would return just null or throw an exception.
To add a new Handler to the chain, the coder would go to the last element of the chain and tell it there was a new element. To do something, he'd just call handleRequest on the first element of the chain.

To solve this problem, I suggested using a different approach.
I'd have a base "Handler" class as well, with "Handler1", "Handler2", just like the previous method mentioned.
However, there would be no "getSuccessor" method. Instead, I'd have a Collection class with a list of handlers(a Vector, an ArrayList, or whatever is best in this case).
The handleRequest function would still exist, but it wouldn't propagate the call to the next handlers. It would just process the request or return null.
To handle a request, one would use

for(Handler handle : handlers){
    result = handle.handleRequest(request);
    if(result!=null) return result;
}
throw new CouldNotParseRequestException(); //just like in the other approach

Or, to prevent code duplication, a "parseRequest(request)" method could be added to the collection class.
To add a new handler, one would go to the collection constructor(or static{} block, or something equivaleng) and simply add the code "addHandler(new Handler3());".

Exactly what advantages of chain-of-responsibility am I missing with this approach? Which method is best(assuming there is a best method)? Why? What potential bugs and issues can each design method cause?

For those who need context, here is what the original code looked like:

if(x instanceof Type1)
{
//doSomething1
} else if(x instanceof Type2)
{
//doSomething2
}
//etc.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

两个我 2024-08-01 05:20:11

哪种方法最好取决于您的处理程序想要做什么。

如果处理程序可以完全自行处理请求,那么您的方法就很好。 处理程序没有对其他处理程序的引用,这使得处理程序接口变得简单。 与责任链的标准实现不同,您可以在链的中间添加或删除处理程序。 事实上,您可以根据请求类型选择构建不同的链。

您的方法的一个问题是处理程序无法对请求进行预处理或后处理。 如果需要此功能,那么责任链更好。 在 CoR 中,处理程序负责委托给链上的下一个处理程序,因此处理程序可以进行预处理和/或后处理,包括修改或替换链上下一个处理程序的响应。 这样看来,CoR 和 Decorator 很相似; 只是意图不同而已。

由于在 CoR 中,处理程序保留对链上下一个项目的引用,因此您无法从链的中间添加或删除项目。 CoR 的一个变体是过滤器链,它允许您在链的中间添加或删除项目(例如,请参见 javax.servlet.FilterChain)。

您展示的代码示例是一堆“if”语句,它们根据对象的类型执行不同的行为。 如果这是您正在清理的代码的典型情况,您只需拥有从请求类型到所需处理程序的映射即可。

删除“if”语句的另一种方法是继承。 如果您需要执行某些行为,并且 Web 服务器有一种变体,而 SOAP 服务器有另一种变体,那么您可以拥有一个 WebServerRequestHandler 和一个 SoapServerRequestHandler,每个都扩展 RequestHandler。 继承的优点是有一个更清晰的位置来放置两种类型的请求所共有的逻辑。 缺点是由于 Java 没有多重继承,因此只能对单维问题进行建模。

Which approach is best depends on what your handlers want to do.

If the handlers can completely handle a request request on their own, your approach is fine. The handlers do not have a reference to other handlers, which makes the handler interface simple. Unlike the standard implementation of Chain of Responsibility, you can add or remove handlers from the middle of the chain. In fact, you can choose to build different chains depending on the type of request.

One problem with your approach is that a handler cannot do pre-processing or post-processing on the request. If this functionality is required, then Chain of Responsibility is better. In CoR, the handler is the one responsible for delegating to the next handler on the chain, so the handler can do pre-processing and/or post-processing, including modifying or replacing the response from the next handler on the chain. In this way, CoR is very similar to Decorator; it's just the intent that's different.

Since in CoR, the handler keeps a reference to the next item on the chain, you cannot add or remove items from the middle of the chain. A variation of CoR that allows you to add or remove items from the middle of the chain is a Filter Chain (see, for example, javax.servlet.FilterChain).

The code example you showed was a bunch of "if" statements that did different behavior based on the type of an object. If that is typical for the code you are cleaning up, you can simply have a map from the request type to the handler required.

Another approach to removing "if" statements is inheritance. If you had some behavior that you needed to do, and there was one variation for a web server, and other variation for a SOAP server, you could have a WebServerRequestHandler and a SoapServerRequestHandler, each extending RequestHandler. The advantage with inheritance is there a clearer place to put logic that is common to both types of request. The disadvantage is that since Java doesn't have multiple inheritance, you can only model single-dimensional problems.

翻了热茶 2024-08-01 05:20:11

与那些后继者相比,我更喜欢你的收藏理念。 它使得操作这组处理程序变得简单明了:集合接口是众所周知的,每个人都知道如何迭代列表或不迭代。

如果你使用朋友建议的这种后继方式,请注意不要陷入非常深的递归(除非你的平台支持尾部调用,我不知道JVM是否能够做到这一点)。

我不建议向集合中添加任何方法。 你会得到更复杂的设计,更难理解、更难修改。 有两个不同的问题:存储一组处理程序以及将此处理程序解释为责任链。 通过迭代集合来处理请求的方法比集合管理方法具有更高的抽象级别,因此不应属于集合接口。

I like your idea with collection better than those successors. It makes it easy and clear to manipulate this set of handlers: the collections interface is well known and everybody understands how to iterate over a List or what not.

If you use this successor way suggested by a friend, take care not to fall into a very deep recursion (unless your platform supports tail calls, I don't know if JVMs are capable of that).

I wouldn't recommend adding any methods to the collection. You get much more complicated design that's harder to comprehend and harder to modify. There are two separate concerns: storing a set of handlers and the interpretation of this handlers as a chain of responsibility. A method that handles requests by iterating over a collection is on higher level of abstraction than collection housekeeping methods, therefore shouldn't belong to collection interface.

聽兲甴掵 2024-08-01 05:20:11

无法判断责任链是否是您的答案,或者即使 GoF 是否适用。 访客可能是对的。 没有足够的信息来确定。

您的问题可能可以通过良好的老式多态性来处理。 或者可能是一个使用键来挑选适当的处理程序对象的映射。

牢记“尽可能做最简单的事情”。 在你向自己证明你需要它之​​前,不要跳入复杂的环境。

我最近读到了关于推广这一想法的Anti-IF 运动。 这里听起来很相关。

Can't tell if Chain of Responsibility is your answer, or even if GoF applies. Visitor might be the right thing. Not enough information to be certain.

It could be that your problem can be handled with good old-fashioned polymorphism. Or maybe a Map that used keys to pick out the appropriate handler object.

Keep 'do the simplest thing possible' in mind. Don't leap to the complex until you've proven to yourself that you need it.

I recently read about the Anti-IF campaign that promotes this idea. Sounds quite pertinent here.

冷血 2024-08-01 05:20:11

如果满足以下任一条件,请选择 CoR:

  • 您的处理程序相互依赖。
  • 您不需要处理程序之间的并行性。
  • 需要在这些处理程序之间进行排序
  • 如果您想从中间处理程序返回并且不想处理其余处理程序,则 。 使用 CoR 更有意义,因为您不需要在 for 循环内编写 ifs。 您可以只从当前链返回,而不是进入链中的下一个。

Go for CoR, if One of below satisfies:

  • Your handlers depend on each other.
  • You do not need parallelism between your handlers.
  • You need ordering between these handlers
  • If you want to return from say, the intermediate handler and do not want to process the rest of the handlers. It makes much more sense to use CoR, as you do not need to write ifs inside for loop. You can instead just return from the current chain instead of going next in the chain.
农村范ル 2024-08-01 05:20:11

IMO List 通常更有意义。 包括所描述的案例。

不过,在某些情况下,递归方法似乎很方便。
例如,让我们考虑一下 DOM 节点并处理单个 div 中的“click”事件并将其传递给 parent,直到我们找到附加到当前节点的点击处理程序(或访问window.document)。 这里的递归似乎比在开始之前构建所有父级列表更方便。

IMO List<Handler> usually makes more sense. Including the described case.

The recursive approach seems handy in certain situations though.
E.g. lets think about DOM nodes and handling the 'click' event from a single div and passing it up to parent, until we find a click-handler attached to the current node (or get to window.document). Here recursion appears more convenient than having to build the list of all parents before starting.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文