你能否解释代理和装饰之间有什么区别?
我看到的主要区别是,当我们假设代理使用组合而 Decorator 使用聚合时似乎很清楚,通过使用多个(一个或多个)装饰器,你可以修改/添加功能到预先存在的实例(装饰),而代理有自己的内部实例代理类和委托给它添加一些额外的功能(代理行为)。
问题是 - 使用聚合创建的代理是代理还是 Decorator ?是否允许(根据GoF模式的定义)使用聚合创建代理?
答案 0 :(得分:88)
接受的答案不太正确。真正的区别不是所有权(构成与聚合),而是类型信息。
装饰器 总是通过其委托人。 代理 可能自己创建,或者可能将其注入。
但代理 始终知道受委托人的(更多)特定类型。换句话说,代理及其代理人将具有相同的基本类型,但代理指向某些派生类型。 装饰器指向其自己的基本类型。因此,不同之处在于有关被委派者类型的编译时信息。
在动态语言中,如果被委托者被注入并且恰好具有相同的界面,则没有区别。
您的问题的答案是"是"。
答案 1 :(得分:69)
装饰器模式侧重于动态向对象添加功能,而代理模式则侧重于控制对象的访问。
修改: - 强>
代理与真实主题之间的关系通常在编译时设置,代理以某种方式实例化,而 Decorator 则分配在运行时,只知道主题的界面。
答案 2 :(得分:39)
装饰器获取装饰对象的引用(通常通过构造函数),而代理负责自己执行此操作。
代理可能根本不实例化包装对象(如果没有使用对象字段/ getter,可以使用ORM来防止对DB进行不必要的访问),而装饰器始终保持链接实际包装的实例。
代理通常由框架用于添加安全性或缓存/ lazing并由框架构建(而不是由常规开发人员自己构建)。
装饰器通常用于由开发人员自己基于接口而不是实际类向旧版或旧版添加新行为(因此它适用于各种接口实例,代理是具体的课程。)
答案 3 :(得分:37)
这是GoF的直接引号(第216页)。
尽管装饰器可以具有与代理类似的实现,但装饰器具有不同的用途。装饰器将一个或多个职责添加到对象,而代理控制对对象的访问。
代理的实现程度不同,如装饰器。一种 保护代理可能完全像装饰器一样实现。在另一 远程代理不会直接引用其真实主题,而仅包含 间接引用,例如“主机ID和主机上的本地地址”。虚拟代理 将以间接引用(例如文件名)开始,但最终会 获取并使用直接参考。
受欢迎的答案表明,代理知道代理的具体类型。从这句话中我们可以看出并非总是如此。
根据GoF,代理服务器和装饰器之间的区别是代理服务器限制了客户端。装饰器没有。代理可以通过控制对功能的访问来限制客户端的行为。或通过执行客户端不可见和未知的操作来限制客户端知道的内容。装饰器的作用相反:装饰器以客户端可见的方式增强其代理的功能。
我们可以说代理是一个黑盒子,而装饰器是一个白盒子。
在将Proxy与Decorator进行对比时,包装器和委托之间的组合关系是错误的关系,因为组合是这两种模式的共同特征。包装程序和客户端之间的关系是区分这两种模式的原因。
答案 4 :(得分:16)
主要差异:
Sourcemaking文章以极好的方式引用了相同点和不同点。
相关的SE问题/链接:
When to Use the Decorator Pattern?
What is the exact difference between Adapter and Proxy patterns?
答案 5 :(得分:1)
花点时间找出this的答案及其真正含义。一些例子应该使它更清楚。
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
的实例。 LoggingDBAuthorization
和DBAuthorization
实现 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
。 Decorator
并不确切地知道装饰的是什么,它只知道装饰实例的合同(知道JobSeeker
)。请注意,这与Proxy
不同;相反,它确切地知道它正在装饰什么。
在这种情况下,您可能会问两种设计模式之间是否存在实际差异?如果我们尝试将Decorator
写为Proxy
怎么办?
public class TwoYearsInTheIndustry implements JobSeeker {
private final Newbie newbie = new Newbie();
@Override
public int interviewScore() {
return newbie.interviewScore() + 20;
}
}
这绝对是一个选项,突出显示这些模式有多紧密;如其他答案所述,它们仍然适用于不同的场景。
答案 6 :(得分:0)
代理和装饰器的目的不同,他们专注于内部实现。代理用于使用远程,跨进程或跨网络对象,就像它是本地对象一样。 Decorator用于向原始界面添加新行为。
虽然两种模式在结构上相似,但Proxy的大部分复杂性在于确保与源对象的正确通信。另一方面,Decorator专注于添加行为的实现。
答案 7 :(得分:0)
代理为包装的对象提供相同的接口,装饰器为其提供增强的接口,并且代理通常自行管理其服务对象的生命周期,而装饰器的组成始终由客户控制。