我在看代理模式,对我而言,它似乎很像装饰器,适配器和桥模式。 我误会了什么吗? 有什么不同? 为什么我会使用Proxy模式而不是其他模式? 你过去在现实世界的项目中如何使用它们?
#1楼
所有这四种模式都涉及用内部对象包装内部对象/类,因此它们在结构上非常相似。 我会按目的概述差异:
- 代理封装了从外到内的访问。
- 装饰器修改或扩展内部与外部的行为。
- 适配器将接口从内部转换为外部。
- Bridge将行为(外部)的不变部分与变量或平台相关部分(内部)分开。
并通过内部和外部对象之间的接口变化:
- 在代理接口中是相同的。
- 在Decorator接口中是相同的。
- 在适配器接口正式不同,但实现相同的目的。
- 在Bridge接口中概念上是不同的。
#2楼
我对这个问题的看法。
所有四种模式都有很多共同点,所有这四种模式有时被非正式地称为包装器或包装器模式。 所有使用组合,包装主题并在某个时刻将执行委托给主题,将一个方法调用映射到另一个方法调用。 它们使客户无需必须构建不同的对象并复制所有相关数据。 如果使用得当,它们可以节省内存和处理器。
通过促进松散耦合,他们可以使稳定的代码更少地暴露于不可避免的变化,并且对于其他开发人
适配器
适配器使主体(适配器)适应不同的接口。 这样我们就可以将对象添加到名义上不同类型的集合中。
适配器仅向客户端公开相关方法,可以限制所有其他方法,揭示特定上下文的使用意图,如调整外部库,使其看起来不那么通用,更专注于我们的应用程序需求。 适配器提高了代码的可读性和自我描述。
适配器使一个团队免受其他团队的易变代码的攻击; 处理离岸团队时的救生员工具;-)
较少提及的目的是防止主题类过多的注释。 有了这么多基于注释的框架,这就变得越来越重要了。
适配器有助于克服仅限单一继承的Java限制。 它可以将几个适应者组合在一个信封下,给人以多重继承的印象。
代码方面,适配器是“瘦”。 除了简单地调用adaptee方法和进行此类调用所必需的偶尔数据转换之外,它不应该向adaptee类添加太多代码。
JDK或基本库中没有很多好的适配器示例。 应用程序开发人员创建适配器,以使库适应应用程序特定的接口
装饰
Decorator不仅委托,不仅将一个方法映射到另一个方法,它们做得更多,它们修改了一些主题方法的行为,它可以决定不调用主题方法,委托给不同的对象,一个辅助对象。
装饰器通常将(透明)功能添加到包装对象,如记录,加密,格式化或压缩到主题。 这个新功能可能会带来很多新代码。 因此,装饰器通常比适配器“更胖”。
装饰器必须是主题界面的子类。 它们可以透明地使用而不是它的主题。 请参阅BufferedOutputStream,它仍然是OutputStream,可以这样使用。 这是Adapters的主要技术差异。
JDK中的整个装饰器系列的教科书示例很容易 - Java IO。 BufferedOutputStream , FilterOutputStream和ObjectOutputStream等所有类都是OutputStream的装饰器。 它们可以是洋葱层,再一次装饰一个装饰,增加更多功能。
代理
代理不是典型的包装器。 在创建代理时,包装对象(代理主题)可能尚不存在。 代理通常在内部创建它。 它可能是按需创建的繁重对象,也可能是不同JVM或不同网络节点中的远程对象,甚至是非Java对象(本机代码中的组件)。 它根本没有必要包装或委托给另一个对象。
最典型的例子是远程代理,重对象初始化器和访问代理。
远程代理 - 主题在远程服务器,不同的JVM甚至非Java系统上。 代理将方法调用转换为RMI / REST / SOAP调用或任何需要的内容,从而防止客户端暴露于底层技术。
延迟加载代理 - 仅对第一次使用或第一次密集使用完全初始化对象。
访问代理 - 控制对主题的访问。
正面
立面与最小知识的设计原则(德米特定律)密切相关。 Facade与Adapter非常相似。 它们都包装,它们都将一个对象映射到另一个对象,但它们的意图不同。 Facade展平了主题的复杂结构,复杂的对象图,简化了对复杂结构的访问。
Facade包裹着一个复杂的结构,为它提供了一个平面界面。 这可以防止客户端对象暴露于主题结构中的内部关系,从而促进松耦合。
桥
适配器模式的更复杂的变体,其中不仅实现变化而且抽象。 它为代表团增加了一个间接性。 额外的授权是桥梁。 它甚至可以从适配接口中解耦适配器。 它比任何其他包装模式更增加复杂性,因此请小心使用。
构造函数的差异
在查看构造函数时,模式差异也很明显。
代理不包装现有对象。 构造函数中没有主题。
装饰器和适配器确实包装已经存在的对象,通常是这样
在构造函数中提供。Facade构造函数获取整个对象图的根元素,否则它看起来与Adapter相同。
现实生活中的例子 - JAXB编组适配器 。 此适配器的用途是将简单的平面类映射到外部所需的更复杂的结构,并防止使用过多的注释“污染”主题类。
#3楼
设计模式不是数学,它是艺术与软件工程的结合。 没有什么比这个要求你必须使用代理,桥接等。创建设计模式来解决问题。 如果您预计到设计问题,请使用它。 根据经验,您将了解具体问题,使用哪种模式。 如果你擅长坚实的设计原则,你就可以实现设计模式而不需要知道模式。 常见的例子是statergy和工厂模式
因此,更多地关注固体设计原则,清洁编码原则和ttd
#4楼
这是Head First Design Patterns的引用
定义属于书。 例子属于我。
装饰器 - 不改变界面,但增加了责任。 假设你有一个汽车界面,当你为汽车的不同型号(s,sv,sl)实现这个时,你可能需要为某些型号增加更多的责任 。 喜欢天窗,安全气囊等。
适配器 - 将一个接口转换为另一个接口。 你有一个汽车界面,你希望它像吉普车一样。 所以你拿车,修改它,变成吉普车。 因为它不是真正的吉普车。 但就像吉普车一样。
Facade - 使界面更简单。 假设您有汽车,飞机,船舶接口。 实际上,你所需要的只是一个将人们从一个地方送到另一个地方的班级。 您希望外观决定使用哪种车辆。 然后你收集所有这些接口引用 ,并让它决定/委托以保持简单。
首先:“一个外观不仅简化了界面,它将客户端与组件子系统分离。外观和适配器可以包装多个类,但外观的意图是简化,而适配器是将接口转换为不同的东西。 “
#5楼
我想在Bill Karwing的回答中添加一些例子(这很棒。)我还添加了一些实现的关键差异,我觉得很遗憾
引用的部分来自[ https://stackoverflow.com/a/350471/1984346](Bill Karwing)的回答
代理,装饰器,适配器和桥都是“包装”类的变体。 但他们的用途不同。
- 当您想要延迟实例化对象时,可以使用代理 ,或者隐藏您正在调用远程服务或控制对象访问的事实。
代理的ProxyClass和ObjectClass应该实现相同的接口,因此它们是可互换的
示例 - 代理昂贵的对象
class ProxyHumanGenome implements GenomeInterface {
private $humanGenome = NULL;
// humanGenome class is not instantiated at construct time
function __construct() {
}
function getGenomeCount() {
if (NULL == $this->humanGenome) {
$this->instantiateGenomeClass();
}
return $this->humanGenome->getGenomeCount();
}
}
class HumanGenome implement GenomeInterface { ... }
- Decorator也被称为“智能代理”。 当您想要向对象添加功能时使用此功能,但不能通过扩展该对象的类型来使用。 这允许您在运行时执行此操作。
DecoratorClass应该(可以)实现ObjectClass的扩展接口。 所以ObjectClass可以被DecoratorClass替换,但反之亦然。
示例 - 添加附加功能
class DecoratorHumanGenome implements CheckGenomeInterface {
// ... same code as previous example
// added functionality
public function isComplete() {
$this->humanGenome->getCount >= 21000
}
}
interface CheckGenomeInterface extends GenomeInterface {
public function isComplete();
}
class HumanGenome implement GenomeInterface { ... }
- 当您具有抽象接口时,将使用适配器 ,并且您希望将该接口映射到具有类似功能角色但具有不同接口的另一个对象。
实现差异代理,装饰器,适配器
适配器为其主题提供不同的界面。 代理提供相同的接口。 Decorator提供增强的界面。
Bridge与Adapter非常相似,但是当您定义抽象接口和底层实现时,我们将其称为Bridge。 即,您不适应某些遗留代码或第三方代码,您是所有代码的设计者,但您需要能够交换不同的实现。
Facade是一个更高级别(读取:更简单)的接口,用于一个或多个类的子系统。 假设您有一个复杂的概念,需要多个对象来表示。 对这组对象进行更改会让人感到困惑,因为您并不总是知道哪个对象具有您需要调用的方法。 现在是编写Facade的时候了,它可以为您可以对对象集合执行的所有复杂操作提供高级方法。 示例:学校部分的域模型,使用
countStudents()
,reportAttendance()
,assignSubstituteTeacher()
等方法。
本答案中的大部分信息来自https://sourcemaking.com/design_patterns ,我建议将其作为设计模式的优秀资源 。
来源:oschina
链接:https://my.oschina.net/u/3797416/blog/3166021