代理服务器和修饰模式服务器的区别

你能解释一下 代理室内设计师的区别吗?

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

问题是: 通过聚合创建的 代理仍然是 代理还是 室内设计师?是否允许(根据 GoF 模式中的定义)使用聚合创建 代理

54521 次浏览

Decorator Pattern 专注于动态地向对象添加函数,而 代理 Pattern 专注于控制对对象的访问。

编辑:-

代理和实际主题之间的关系通常在编译时设置,代理以某种方式实例化它,而 室内设计师在运行时被分配给主题,只知道主题的接口。

Decorator 获取修饰对象的引用(通常通过构造函数) ,而 代理负责自己完成。

代理 可能根本不会实例化包装对象(如果没有使用对象字段/getter,则使用 ORM 来防止对 DB 的不必要访问) ,而 室内设计师始终保持到实际包装实例的链接。

框架通常使用 Proxy 来添加安全性或缓存/延迟,并由框架(而不是普通开发人员本身)构造。

Decorator 通常用于开发人员自己基于接口(而非实际类)向旧的或遗留的类添加新行为(因此它可以在广泛的接口实例上工作,代理围绕具体类)。

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

室内设计师一直都是传递给它的委托。 代理 也许吧自己创建它,或者 也许吧注入它。

但是 代理 一直都是知道(更多)委托人的特定类型。换句话说,代理及其委托方将具有相同的基类型,但是 代理指向某个派生类型。室内设计师指向它自己的基类型。因此,区别在于关于委托类型的编译时信息。

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

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

主要区别:

  1. Proxy 提供相同的接口。
  2. 装饰符 代理有不同的用途,但结构相似。两者都描述了如何为另一个对象提供一定程度的间接性,并且实现保留了对它们转发请求的对象的引用。
  3. Decorator 可以看作是只有一个组件的简并 Composite。但是,Decorator 添加了额外的职责-< em > 它不是用于对象聚合的。
  4. Decorator 支持递归 < em > 组合
  5. 室内设计师类声明与 LCD (最低类分母)接口的 作文关系,并且该数据成员在其构造函数中初始化。
  6. 使用 代理惰性初始模式,通过缓存对象和 控制对客户端/调用方的访问来提高性能

这篇文章以极好的方式引用了两者的相似点和不同点。

相关的社会工作服务问题/连结:

何时使用修饰模式?

Adapter 和 Proxy 模式之间的确切区别是什么?

代理和装饰器的用途以及它们关注内部实现的地方不同。代理用于将远程对象、跨进程对象或跨网络对象当作本地对象使用。Decorator 用于向原始接口添加新行为。

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

花了一段时间才弄清楚 这个的答案和它的真正含义。举几个例子就可以更清楚地说明这一点。

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的实例。

  • 代理将包含基本接口(Authorization)的一些具体实现(DBAuthorization)。换句话说,代理知道 没错代理了什么。

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;
}
}

注意我是怎么说 还有另一个求职者的没有 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;
}
}

这肯定是一个选项,并且突出了这些模式有多接近; 它们仍然适用于不同的场景,正如其他答案中所解释的那样。

Proxy 为包装对象提供相同的接口,室内设计师为其提供增强的接口,而 Proxy 通常自己管理其服务对象的生命周期,而 Decorators 的组合总是由客户机控制。

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

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

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

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

根据 GoF,Proxy 和 Decorator 的区别在于 Proxy 限制是客户端。室内设计师没有。代理可以通过控制对功能的访问来限制什么是客户端 是的; 也可以通过执行客户端不可见和未知的操作来限制什么是客户端 知道。Decorator 的作用相反: 它以客户端可见的方式增强其委托的作用。

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

在将 Proxy 与 Decorator 进行对比时,包装器和委托之间的组合关系是不应该关注的,因为组合是这两种模式的共同特征。包装器和客户机之间的关系是这两种模式的区别所在。

  • Decorator 通知其客户端并赋予其权限。
  • 代理限制和剥夺其客户端的权力。

让我先解释一下这些模式,然后再回答你们的问题。

从类图和含义来看,它们非常相似:

  1. 两者都具有与其委托方相同的接口。
  2. 两者都添加/增强其受委托方的行为。
  3. 两者都要求受委托方执行操作(不应使用空受委托方)。

但它们有一些不同之处:

  1. 不同的目的: 代理使用与其受委托方截然不同的领域知识来增强受委托方(传递的对象)的行为。例如,安全代理添加了对委托方的安全控制。发送远程消息的代理需要序列化/反序列化数据,并具有网络接口方面的知识,但与如何准备源数据无关。 Decorator 在受委托人处理的相同问题域上提供帮助。例如,BufferedInputStreaman (一个 IO 修饰器)处理输入,这个问题域(IO)与它的委托者相同,但是没有提供 IO 数据的委托者它就无法执行。

  2. 依赖性是否强烈: Decorator 依赖于委托来完成行为,如果没有委托(Strong) ,它就不能完成行为。因此我们总是用加法来代替合成。 代理可以执行伪造的行为,即使它不需要委托(弱)。单元测试框架可以通过它的接口来模仿/监视一个行为。因此,我们使用合成来表示对实际对象没有强烈的依赖性。

  3. 多次增强(如上所述) : 代理: 我们可以利用代理来包装实际对象一次,而不是几次。 装饰器: 装饰器可以多次包装实际对象,也可以包装已经被装饰器包装的对象(可以是不同的装饰器,也可以是同一个装饰器)。例如,对于一个订单系统,你可以对装饰者进行折扣。 PercentageDisCountDecorator 减去50% ,如果金额大于10 $() ,则直接减去5 $。那么, 当你想减去50% 并扣除5美元时,你可以这样做: new DeductionAmountDiscount 装饰器(new PercentageDiscount 装饰器(委托人)) 当你想扣除10 $时,你可以使用新的 DeductionAmountDiscount 装饰器(new DeductionAmountDiscount 装饰器(委托人))。

这个问题的答案与 Proxy 和 Decorator 之间的区别无关。为什么?

  1. 设计模式只是为那些不擅长面向对象技能的人提供的模式,以便他们能够利用面向对象解决方案。如果您熟悉 OO,就不需要知道那里有多少设计模式(在设计模式发明之前,有相同问题的熟练人员可以找到相同的解决方案)。
  2. 没有两片叶子是完全一样的,所以你遇到的问题。人们总是会发现他们的问题与设计模式给出的问题不同。

如果您指定的问题确实与 Proxy 和 Decorator 处理的问题不同,并且确实需要聚合,那么为什么不使用呢?我认为将 OO 应用于您的问题比您将其标记为代理或装饰器要重要得多。

Decorator向对象添加额外的责任,而代理控制对对象的访问,它们都使用复合。如果您的包装类干扰了主题,那么它显然是一个代理。让我用 PHP 中的一个代码示例来解释:

代码示例

给定的是以下 CarRepository:

interface CarRepositoryInterface
{
public function getById(int $id) : Car
}


class CarRepository implements CarRepositoryInterface
{
public function getById(int $id) : Car
{
sleep(3); //... fake some heavy db call
$car = new Car;
$car->setId($id);
$car->setName("Mercedes Benz");
return $car;
}
}

CarRepository-Proxy

Proxy通常用作延迟加载或缓存代理:

class CarRepositoryCacheProxy implements CarRepositoryInterface
{
private $carRepository;


private function getSubject() : CarRepositoryInterface
{
if($this->carRepository == null) {
$this->carRepository = new CarRepository();
}
return $this->carRepository;
}
    

/**
* This method controls the access to the subject
* based on if there is cache available
*/
public function getById(int $id) : Car
{
if($this->hasCache(__METHOD__)) {
return unserialize($this->getCache(__METHOD__));
}
$response = $this->getSubject()->getById($id);
$this->writeCache(__METHOD__, serialize($response));
return $response;
}
    

private function hasCache(string $key) : bool
{
//... implementation
}
    

private function getCache(string $key) : string
{
//... implementation
}
    

private function writeCache(string $key, string $result) : string
{
//... implementation
}
}

CarRepository-Decorator

只要所添加的行为不“控制”主体,就可以使用 Decorator:

class CarRepositoryEventManagerDecorator implements CarRepositoryInterface
{
private $subject, $eventManager;


/**
* Subjects in decorators are often passed in the constructor,
* where a proxy often takes control over the invocation behavior
* somewhere else
*/
public function __construct(CarRepositoryInterface $subject, EventManager $eventManager)
{
$this->subject = $subject;
$this->eventManager = $eventManager;
}


public function getById(int $id) : Car
{
$this->eventManager->trigger("pre.getById");
//this method takes no control over the subject
$result = $this->subject->getById($id);
$this->eventManager->trigger("post.getById");
return $result;
}
}