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

Łuk*_*ski 108 oop design-patterns decorator proxy-pattern

你能解释一下ProxyDecorator之间的区别吗?

我看到的主要区别在于,当我们假设Proxy使用合成Decorator使用聚合时,似乎很清楚通过使用多个(一个或多个)装饰器,您可以修改/添加功能到预先存在的实例(装饰),而代理有自己的代理类的内部实例,并委托给它添加一些额外的功能(代理行为).

是个问题-是否代理与聚集创造仍然是代理还是相当装饰?是否允许(通过GoF模式中的定义)创建具有聚合的代理

cdu*_*001 95

接受的答案并不完全正确.真正的区别不是所有权(构成与聚合),而是类型信息.

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

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

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

你的问题的答案是"是".

  • 我在亚马逊上从一位了解他的东西的客座讲师那里上课.使用"代理"可执行文件(例如,使用Web服务)和代理设计模式之间存在差异.代理模式和装饰模式的UML可以不同.但没有什么可以阻止代理与其代理人拥有相同的API.Decorator是Proxy的严格子集,但是Decorator可能仍然被称为代理,具体取决于底层API是否*保证*是相同的. (3认同)
  • "但代理人总是知道受委托人的(更具体)类型."我认为这不是真的.想象一下远程代理.代理机制不需要知道远程对象的任何细节.远程系统使用指定的接口注册对象.并且本地代理公开相同的接口. (2认同)

Rah*_*thi 74

Decorator Pattern专注于向对象动态添加函数,而Proxy Pattern则专注于控制对对象的访问.

编辑:-

Proxy和真实主题之间的关系通常在编译时设置,Proxy以某种方式实例化,而Decorator在运行时分配给主题,只知道主题的接口.

  • 代理仍可用于添加功能.想想AOP代理. (5认同)
  • 完全同意先生.换句话说,我会将其转换为代理模式,代理类可以隐藏对象的详细信息.因此,在使用代理模式时,我们通常会在代理类中创建一个abject实例.当使用Decorator Pattern时,我们通常将原始对象作为参数传递给装饰器的构造函数. (4认同)
  • Proxy和真实主题之间的关系通常在编译时设置,Proxy以某种方式实例化,而Decorator或Adapter在运行时分配给主题,只知道主题的接口.希望有道理!:) (4认同)

jac*_*646 54

这是来自 GoF 的直接引述(第 216 页)。

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

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

流行的答案表明代理知道其委托的具体类型。从这句话我们可以看出,这并不总是正确的。

GoF 中Proxy 和Decorator 的区别在于Proxy限制了客户端。装饰器没有。代理可以通过控制对功能的访问来限制客户端的行为;或者它可以通过执行客户端不可见和不知道的操作来限制客户端知道的内容。装饰器做相反的事情:它以客户端可见的方式增强其委托的功能。

我们可以说 Proxy 是一个黑盒,而 Decorator 是一个白盒。

在对比代理和装饰器时,包装器和委托之间的组合关系是错误的关注关系,因为组合是这两种模式的共同特征。包装器和客户端之间的关系是区分这两种模式的原因。

  • 装饰者通知并授权其客户。
  • 代理限制和剥夺其客户的权力。


gav*_*koa 43

装饰器获取装饰对象的引用(通常通过构造函数),而Proxy负责自己做.

代理可能根本不实例化包装对象(如果没有使用对象字段/ getter,这样做是为了防止对DB进行不必要的访问),而Decorator总是保存到实际包装实例的链接.

框架通常使用代理来添加安全性或缓存/ lazing并由框架构建(而不是由常规开发人员自己构建).

Decorator通常用于通过开发人员本身基于接口而不是实际类向旧类或旧类添加新行为(因此它适用于各种接口实例,Proxy是围绕具体类).


Rav*_*abu 19

主要差异:

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

Sourcemaking文章以优秀的方式引用了相同点和不同点.

相关的SE问题/链接:

何时使用装饰器模式?

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


Eug*_*ene 6

花了一段时间才弄清楚这个答案及其真正含义。几个例子应该可以更清楚地说明这一点。

Proxy第一的:

public interface Authorization {
    String getToken();
} 
Run Code Online (Sandbox Code Playgroud)

和 :

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

有一个调用者Authorization,一个非常愚蠢的调用者:

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

到目前为止没有什么不寻常的,对吧?从某个服务获取令牌,并使用该令牌。现在又提出了一个要求,添加日志记录:意思是每次都记录令牌。对于这种情况很简单,只需创建一个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;
    }
}
Run Code Online (Sandbox Code Playgroud)

我们将如何使用它?

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

    Caller caller = new Caller();
    caller.authenticatedUserAction(loggingDBAuthorization);
}
Run Code Online (Sandbox Code Playgroud)

请注意,它LoggingDBAuthorization 包含一个 的实例DBAuthorization。两者LoggingDBAuthorization兼而有之。DBAuthorization Authorization

  • 代理将保存DBAuthorization基本接口 () 的一些具体实现 () Authorization。换句话说,代理确切地知道正在代理的内容。

Decorator:

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

public interface JobSeeker {
    int interviewScore();
}
Run Code Online (Sandbox Code Playgroud)

及其实现:

class Newbie implements JobSeeker  {
    @Override
    public int interviewScore() {
        return 10;
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我们想添加一位更有经验的候选人,将其面试分数加上另一位候选人的面试分数JobSeeker

@RequiredArgsConstructor 
public class TwoYearsInTheIndustry implements JobSeeker {

    private final JobSeeker jobSeeker;

    @Override
    public int interviewScore() {
        return jobSeeker.interviewScore() + 20;
    } 
}
Run Code Online (Sandbox Code Playgroud)

请注意我是如何说的,再加上另一位求职者的说法而不是 Newbie。ADecorator并不确切地知道它正在装饰什么,它只知道该装饰实例的契约(它知道JobSeeker)。请注意,这与 ; 不同Proxy。相比之下,它确切地知道它正在装饰什么。

您可能会问,在这种情况下,两种设计模式实际上是否有任何区别?如果我们尝试将 the 写成Decoratora 会怎么样Proxy

public class TwoYearsInTheIndustry implements JobSeeker {

    private final Newbie newbie = new Newbie();

    @Override
    public int interviewScore() {
        return newbie.interviewScore() + 20;
    }
}
Run Code Online (Sandbox Code Playgroud)

这绝对是一个选择,并强调了这些模式的接近程度;正如其他答案中所解释的,它们仍然适用于不同的场景。


Jam*_*Lin 5

代理和装饰器的目的不同,它们专注于内部实现的地方。代理用于像使用本地对象一样使用远程、跨进程或跨网络对象。装饰器用于向原始界面添加新行为。

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

  • 我不知道是否全部都在那里。读完前面的答案后,我只是感到有插话的冲动。 (2认同)