代理和装饰器模式之间的差异


136

您能否给出任何好的解释,ProxyDecorator有什么区别?

我看到的主要区别是,当我们假设代理使用组合并且装饰器使用聚合时,那么很显然,通过使用多个(一个或多个)装饰器,您可以为预先存在的实例(装饰)修改/添加功能,而代理具有自己的代理类内部实例,并为其添加了一些附加功能(代理行为)的委托。

现在的问题是-不代理创建了聚集仍然是代理还是相当装饰?是否允许(按照GoF模式的定义)创建具有聚合的代理


2
一些链接:代理装饰器
Sotirios Delimanolis

5
您从哪里知道代理使用组合而装饰器使用聚合?
CPerkins

1
@CPerkins看到我对Rahul Tripathi答案的评论。
卢卡斯Rzeszotarski

1
还有装饰器(patternspatterns.cs.up.ac.za/examples/ch2/decorator-theory.cs)-显然是聚合,代理(patterns.cs.up.ac.za/examples/ch2/proxy-theory.cs) -很明显的组成。
hyankov

Answers:


16

这是来自GoF的直接报价(第216页)。

尽管装饰器可以具有与代理类似的实现,但装饰器具有不同的用途。装饰器将一个或多个职责添加到对象,而代理控制对对象的访问。

代理的实现程度不同,如装饰器。保护代理可以完全像装饰器一样实现。另一方面,远程代理将不包含对其真实主题的直接引用,而仅包含间接引用,例如“主机ID和主机上的本地地址”。虚拟代理将以间接引用(例如文件名)开始,但最终将获得并使用直接引用。

流行的答案表明,代理知道代理的具体类型。从这句话中我们可以看出并非总是如此。

根据GoF,Proxy和Decorator之间的区别在于Proxy 限制了客户端。装饰器没有。代理可以通过控制对功能的访问来限制客户端的操作。否则可能会限制客户的了解通过执行看不见和未知的操作来的内容。装饰器的作用相反:装饰器以客户端可见的方式增强其代理的功能。

我们可以说代理是一个黑盒子,而装饰器是一个白盒子。

在将Proxy与Decorator进行对比时,包装器和委托之间的组合关系是错误的关系,因为组合是这两种模式的共同特征。包装器和客户端之间的关系是区分这两种模式的原因。

  • 装饰器通知客户并赋予其权力。
  • 代理限制并取消其客户端的权限。

113

真正的区别不是所有权(组成与聚合),而是类型信息。

一个装饰始终通过其代理方。一个代理 可能会创建它自己,或者他可能有它注入。

但是代理 总是知道委托人的(更多)特定类型。换句话说,代理服务器及其委托人将具有相同的基本类型,但是代理服务器指向某些派生类型。一个装饰指向其自身的基本类型。因此,区别在于有关委托人类型的编译时信息。

在动态语言中,如果委托被注入并且碰巧具有相同的接口,则没有区别。

您的问题的答案是“是”。


2
“但是代理总是知道(更多)特定的委托人类型。”我认为这不是真的。想象一下远程代理。代理机制不需要知道远程对象的任何细节。远程系统使用指定的接口注册对象。并且本地代理公开相同的接口。
阿列克谢

3
我在亚马逊上从一位了解他的知识的访问讲师那里上了一堂课。在使用“代理”可执行文件(例如,与Web服务一起使用)与代理设计模式之间有区别。代理模式和装饰器模式的UML可以不同。但是,没有什么能阻止Proxy具有与其委托人相同的API。Decorator是Proxy的严格子集,但根据基础API是否保证相同,Decorator仍可以称为Proxy 。
cdunn2001

85

装饰器模式专注于向对象动态添加功能,而代理模式专注于控制对对象的访问。

编辑:-

代理与实际主题之间的关系通常在编译时设置,代理以某种方式实例化它,而Decorator在运行时分配给主题,仅知道主题的界面。


5
代理仍然可以用来添加功能。考虑一下AOP代理。
Sotirios Delimanolis

5
主席先生,完全同意。我将换成我所说的“代理模式”,代理类可以从其客户端隐藏对象的详细信息。因此,在使用代理模式时,我们通常在代理类内部创建abject的实例。当使用Decorator Pattern时,我们通常将原始对象作为参数传递给装饰器的构造函数。
Rahul Tripathi

在这种情况下,当实例在代理中被“隐藏”时,区别对我来说很清楚(正如我所写的),但是我认为人们经常将它们称为代理类,它们将作为构造函数参数传递的代理对象。在这种情况下,对我来说,添加新功能或控制的区别非常小。
卢卡斯Rzeszotarski

5
代理与实际主题之间的关系通常在编译时设置,代理以某种方式实例化它,而Decorator或Adapter在运行时分配给主题,仅知道主题的接口。希望有道理!!!:)
Rahul Tripathi

1
您是否可以将此行添加到答案中。
卢卡斯Rzeszotarski

49

装饰器(通常通过构造器)获取装饰对象的引用,而Proxy则由他自己负责。

Decorator始终保持指向实际包装实例的链接时,代理可能根本不会实例化包装对象(例如,如果未使用对象字段/获取程序,则ORM会执行此操作以防止不必要地访问DB)。

框架通常使用代理来增加安全性或缓存/缓存,并由框架(而非常规开发人员本身)构造。

Decorator通常用于由开发人员自己根据接口而不是实际类向旧的或旧的类添加新的行为(因此它可以在各种接口实例上工作,Proxy围绕具体的类)。


22

主要区别:

  1. 代理提供相同的接口。装饰器提供增强的界面。
  2. 装饰器代理具有不同的用途,但结构相似。两者都描述了如何提供对另一个对象的间接访问级别,并且实现保留了对它们向其转发请求的对象的引用。
  3. 可以将装饰器视为仅具有一个组件的简并复合材料。但是,Decorator会增加其他职责- 并非用于对象聚合。
  4. 装饰器支持递归合成
  5. 装饰类声明了组合物到LCD(最低级分母)接口关系,并且该数据成员在其构造初始化。
  6. 使用代理进行延迟初始化,通过缓存对象并控制对客户端/调用者的访问来提高性能

原始资料制作文章以出色的方式引用了异同。

相关的SE问题/链接:

何时使用装饰图案?

适配器和代理模式之间的确切区别是什么?


3

代理和装饰器的目的不同,并且侧重于内部实现。代理用于使用远程,跨进程或跨网络对象,就像它是本地对象一样。装饰器用于向原始接口添加新行为。

尽管两种模式的结构相似,但是代理的大部分复杂性在于确保与源对象的正确通信。另一方面,装饰器专注于添加行为的实现。


您说的与这里已有的其他4个答案有什么不同?
斯蒂芬·劳奇

我不知道是否全部都在那里。阅读之前的答案后,我只是感到渴望进来。
詹姆士·林

1

花了一些时间弄清楚这个答案及其真正含义。一些例子应该使它更清楚。

Proxy 第一:

public interface Authorization {
    String getToken();
} 

和:

// goes to the DB and gets a token for example
public class DBAuthorization implements Authorization {
    @Override
    public String getToken() {
        return "DB-Token";
    }
}

而且有一个调用者Authorization,一个很愚蠢的人:

class Caller {
    void authenticatedUserAction(Authorization authorization) {
        System.out.println("doing some action with : " + authorization.getToken());
    }
}

到目前为止没有什么不寻常的,对吧?从某个服务获取令牌,然后使用该令牌。现在对图片提出了另一个要求,添加了日志记录:意味着每次都记录令牌。在这种情况下很简单,只需创建一个Proxy

public class LoggingDBAuthorization implements Authorization {

    private final DBAuthorization dbAuthorization = new DBAuthorization();

    @Override
    public String getToken() {
        String token = dbAuthorization.getToken();
        System.out.println("Got token : " + token);
        return token;
    }
}

我们将如何使用它?

public static void main(String[] args) {
    LoggingDBAuthorization loggingDBAuthorization = new LoggingDBAuthorization();

    Caller caller = new Caller();
    caller.authenticatedUserAction(loggingDBAuthorization);
}

请注意,其中LoggingDBAuthorization 包含的实例DBAuthorization。无论LoggingDBAuthorizationDBAuthorization 实施 Authorization

  • 代理将DBAuthorization包含基本接口(Authorization)的一些具体实现()。换句话说,代理服务器确切知道要代理的内容。

Decorator

它的开始与几乎相同Proxy,但具有一个接口:

public interface JobSeeker {
    int interviewScore();
}

及其实现:

class Newbie implements JobSeeker  {
    @Override
    public int interviewScore() {
        return 10;
    }
}

现在我们想添加一个经验更丰富的候选人,那就是面试成绩加上另一个JobSeeker

@RequiredArgsConstructor 
public class TwoYearsInTheIndustry implements JobSeeker {

    private final JobSeeker jobSeeker;

    @Override
    public int interviewScore() {
        return jobSeeker.interviewScore() + 20;
    } 
}

请注意,我怎么说,再加上另一个JobSeeker的那个,不是 Newbie。A Decorator并不确切知道它正在装饰什么,它只知道那个装饰实例的协定(它知道JobSeeker)。请注意,这与Proxy ; 相反,它确切地知道它正在装饰什么。

您可能会问,在这种情况下,两种设计模式之间实际上是否存在差异?如果我们尝试将Decoratoras 写为该Proxy怎么办?

public class TwoYearsInTheIndustry implements JobSeeker {

    private final Newbie newbie = new Newbie();

    @Override
    public int interviewScore() {
        return newbie.interviewScore() + 20;
    }
}

这绝对是一个选择,它突出了这些模式之间的距离。如其他答案所述,它们仍然适用于不同的场景。


1

代理为包装的对象提供相同的接口,装饰器为其提供增强的接口,代理通常自行管理其服务对象的生命周期,而装饰器的组成始终由客户端控制。

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.