为什么我会在装饰者身上使用责任链?


72

我只是在阅读“责任链”模式,想像一个场景而不愿意使用装饰器,我很难想象。

你怎么看?CoR有利基用途吗?


1
请添加某种您认为是CoR的任务,但您是用装饰器解决的
Mykola Golubyev 09年

当然,我需要完成一个订单,在某些情况下,我需要打印一张账单。我的装饰器解决方案是将一个核心OrderCompleter包裹在一个OrderCompletionPrintDecorator中,该组件应用条件逻辑并进行打印。与任何链条一样有效。
乔治·莫尔

为什么在OrderCompleter中只有一个叫“ Print”的方法可以在(不希望)打印时使用(或不使用)?换句话说,我想知道您是否可以完全不使用任何模式来解决此任务?在我看来,这似乎不是一项需要真正引入抽象和复杂性的复杂任务。或者,您所说的只是问题的过分简化。
Son Do Lenh,2012年

@SonDo这取决于-但是,这是一个过于简化的版本。问题是,什么使打印出来的东西逻辑去哪里了?如果这是一个简单的决定,则可以直接输入,OrderCompleter.Complete()但它可能是这样的:“如果打印服务响应ping并且此订单或父订单尚未打印,并且下达订单的客户未直接集成用我们的系统。”
乔治·莫尔

Answers:


73

您可以随时中断链的事实将“责任链”模式与“装饰者”模式区分开。可以将装饰器视为一次执行所有操作,而无需与其他装饰器进行任何交互。链中的链接可以一次执行一个,因为它们每个都依赖于上一个链接。

当您可以将程序概念化为由链接组成的链时,请使用“责任链”模式,其中每个链接可以处理请求或将请求向上传递。

当我以前使用Win32 API时,有时需要使用它提供的挂钩功能。挂钩Windows消息大致遵循“责任链”模式。当您钩住WM_MOUSEMOVE之类的消息时,将调用您的回调函数。将回调函数视为链中的最后一个链接。链中的每个链接都可以决定是丢弃WM_MOUSEMOVE消息,还是将其沿链向上传递到下一个链接。

如果在该示例中使用了Decorator模式,则将已收到WM_MOUSEMOVE消息的通知,但您将无力阻止其他钩子也对其进行处理。

在游戏引擎中使用了“命令链”模式的另一个地方。同样,您可以挂钩引擎功能,事件和其他东西。对于游戏引擎,您不想简单地添加功能。您要添加功能并阻止游戏引擎执行其默认操作。


IMO在CoR模式中,我们可以有很多(或所有)处理程序(链接)对象可以帮助处理每个请求的处理/构建响应。
Rishi

因此,您说这可能是嵌套if ... else语句的替代,除非末尾没有其他部分?
Dipon Roy

@Rishi,很难想象,但是GoF没有提及。在其CoR模式中,每个请求仅处理一次或根本不处理。
jaco0646

@DiponRoy,是的,但是CoR模式的关键点是去耦。在if ... else块中,调用者可以看到每个分支。在CoR中,呼叫者甚至不知道链存在。
jaco0646

21

这些模式之间的区别与何时或如何断开链(假定为链)或何时执行额外行为无关。它们之间的联系在于,它们都使用合成来支持继承以提供更灵活的解决方案。

关键区别在于装饰器添加了新的行为,从而实际上扩展了原始接口。类似于普通扩展如何添加方法,只不过“子类”仅通过引用耦合,这意味着可以使用任何“超类”。

COR模式可以修改现有行为,类似于使用继承覆盖现有方法。您可以选择调用super.xxx()来继续执行“链”或自己处理消息。

因此,区别是微妙的,但是装饰器的示例应该有所帮助:

interface Animal
{
    Poo eat(Food food);
}

class WalkingAnimal implements Animal
{
    Animal wrapped;
    WalkingAnimal(Animal wrapped)
    {
        this.wrapped = wrapped;
    }

    Position walk(Human walker)
    {
    };

    Poo eat(Food food)
    {
      return wrapped.eat(food);
    }
}

class BarkingAnimal implements Animal
{
    Animal wrapped;
    BarkingAnimal(Animal wrapped)
    {
        this.wrapped = wrapped;
    }

    Noise bark()
    {
    };

    Poo eat(Food food)
    {
        bark();
        return wrapped.eat();
    }
}

您会看到我们可以组成步行,吠叫的动物……或者实际上增加了对任何动物吠叫的能力。要直接使用这种额外的行为,我们需要保留对BarkingAnimal装饰器的引用。

所有BarkingAnimal的食物在进食前也会吠一次,这改变了现有功能,因此类似于COR。但是其目的与COR不同,即从许多可以吃食物的动物中寻找一种。这里的目的是修改行为。

您可以想象使用COR来寻找将带走动物的人。可以将其实现为chained上面的链接列表,也可以实现为显式列表...等等。

希望这是相当清楚的!

约翰


3
这是使我最终意识到我理解这些模式的方式是错误的答案!我在结构上根据本书中介绍的类图进行思考,责任链具有抽象的父类和后继类。但是GoF指出,您也可以使用mixin继承来做到这一点,在这种情况下,类图看起来是相同的。但是意图和行为差异的关键要素,特别是DECORATOR如何扩展界面,至关重要。好答案。
有远见的软件解决方案

15

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

装饰器

动态地将附加职责附加到对象。装饰器为子类提供了灵活的替代方案,以扩展功能。

我会说事情发生的顺序。如果您将它们链接起来,它们将沿着链接被调用。对于装饰器,您不能保证会获得此命令,只能承担其他责任。


您可以按不同的顺序附加,不是吗?
Mykola Golubyev 09年

如果我们是从类消费者的POV谈起的,那是绝对正确的;但是,如果我们是从类设计者的POV谈起的话,则可以肯定的是,我们可以尽可能地保证这一点。
George Mauer


8

要向对象添加功能时使用装饰器。

当许多参与者之一可能对某个对象执行操作时,将使用COR。

特定装饰被称为采取动作,基于该类型; 而COR沿着已定义的链传递对象,直到一个参与者决定完成该动作为止。

当对不同的处理程序有多个级别的升级时,例如在呼叫中心,客户对公司的价值将确定呼叫是否获得特定级别的支持,则可以使用COR。


好的,但是我的意思是,您可以不花力气地使用装饰器。那么,为什么还要参加CoR?
乔治·莫尔

4
但是装饰器是一种不同的模式-使用COR,对象会在actor之间传递,直到有人说完成动作为止。使用装饰器,将在一个特定类的实现上执行该操作。
Ragoczy

4

好吧,我可以想到两种情况:

  • 您没有核心对象,即,在请求通过所有层/过滤器后,您不知道如何处理该请求。(就像拦截器链这样的方面,它们实际上并不关心请求在哪里结束)。
  • 您需要有选择地对请求进行一些预处理或后期处理。不像装饰器那样采用一般的增强形式。即过滤器可能会或可能不会处理特定的请求,但是添加装饰器总是会通过某些功能增强对象。

现在想不出什么了,很想听听这个话题。


2

我同意从结构角度来看,这两种模式非常相似。我的想法是关于最终行为:

在对CoR元素的经典解释中,处理请求打破了链条。

如果装饰器中的任何元素中断了链,那么它将是装饰器的错误实现,因为行为的基本部分将丢失。装饰器的想法是在基本行为保持不变的情况下透明地添加新行为。


2

装饰器

  1. 装饰器模式允许将行为动态添加到单个对象。

  2. 它提供了子类别的灵活替代方案,以扩展功能。即使使用继承,它也从最低公共分母(LCD)界面继承。

装饰器的UML图

装饰器的UML图

后果:

  1. 通过装饰,还可以动态删除添加的功能。
  2. 装饰在运行时为对象添加了功能,这将使调试系统功能更加困难。

有用的链接:

何时使用装饰图案?

来自维基百科的Decorator_pattern

装饰从sourcemaking

责任链:

责任链模式是一种设计模式,由命令对象的源和一系列处理对象组成。每个处理对象都包含定义其可以处理的命令对象类型的逻辑。其余的传递给链中的下一个处理对象

UML图

在此处输入图片说明

在以下情况下,此模式更有效:

  1. 多个对象可以处理命令
  2. 该处理程序事先未知
  3. 处理程序应自动确定
  4. 希望将请求发送给一组对象,而无需明确指定其接收者
  5. 必须以动态方式指定可以处理命令的对象组

有用的链接:

维基百科的责任链模式

oodesign的责任链模式

来源链的责任链

真实示例:在公司中,指定角色对处理购买请求有特定的限制。如果具有指定角色的人员没有足够的权力来批准购买账单,则他会将命令/请求转发给拥有更多权限的继任者。该链将继续,直到处理命令为止。



1

在阅读了“四人帮”的定义之后,我不相信有真正的区别。(为方便起见包括)

  • 装饰器:允许动态包装对象,以修改其现有职责和行为
  • 责任链:通过将接收对象链接在一起,使多个对象有机会处理请求

Wikipedia充实了它们,但其中有些是任意的。

  • 装饰器通常实现为链接列表。但是我认为这太低了,不能被认为是模式的“一部分”。
  • 责任链链接仅在其负责的情况下处理数据。但是确定责任和处理数据都是行为的一部分。装饰者可以轻松地做到这一点。
  • 装饰器要求您呼叫代表。
  • “纯” CoR链接仅在不处理数据的情况下才应调用委托。

前两个属性并不能真正区分模式。后两者可以,但是通常实现Decorator和CoR的方式并不强制这些属性-设计人员只是希望没有人编写破坏链的Decorator或处理数据后继续链的CoRLink。

要实际实现这些属性,您需要类似以下内容。

强制装饰器:

abstract class Decorated {

public Decorated delegate;

public final Object doIt(Object args) {
    Object returnVal = behavior(arg);
    if(delegate != null) returnVal = delegate.doit(returnVal);
    return returnVal;
}

protected abstract Object behavior(Object args); //base or subclass behavior
}

强制责任链:

abstract class Link {

public Link delegate;

public final Object processIt(Obect args) {
    Object returnVal = args;
    if(isMyResponsibility) returnVal = processingBehavior(returnVal);
    else returnVal = delegate.processIt(returnVal);
    return returnVal;
}

protected abstract Boolean isMyResponsibility(Object args);

protected abstract Object processingBehavior(Object args);
}

(或者,您可以只向javadoc添加一行,如果您想要的只是放任自己,以防万一其他人弄糟了您的设计,但是为什么要碰碰运气呢?)


0

我认为应用这两种模式的情况是不同的。顺便说一下,对于装饰器模式,装饰器应该知道它包装的组件。对于CoR,不同的拦截器之间可能一无所知。


装饰者只需要知道接口,不需要任何实现。
dave1010 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.