我正在查看代理模式,对我而言,它看起来像装饰器,适配器和桥接器模式一样糟糕。我误会了吗?有什么不同?为什么要使用代理模式而不是其他模式?您过去在现实世界的项目中是如何使用它们的?
我正在查看代理模式,对我而言,它看起来像装饰器,适配器和桥接器模式一样糟糕。我误会了吗?有什么不同?为什么要使用代理模式而不是其他模式?您过去在现实世界的项目中是如何使用它们的?
Answers:
代理,装饰器,适配器和桥都是“包装”类的变体。但是它们的用途不同。
当您要延迟实例化对象,或者隐藏您正在调用远程服务或控制对对象的访问的事实时,可以使用代理。
装饰器也称为“智能代理”。当您要向对象添加功能而不是通过扩展该对象的类型时,可以使用此方法。这使您可以在运行时这样做。
当您具有抽象接口,并且想要将该接口映射到另一个具有相似功能角色但接口不同的对象时,将使用适配器。
Bridge与Adapter非常相似,但是当您定义抽象接口和基础实现时,我们将其称为Bridge。也就是说,您不适应某些遗留代码或第三方代码,而是所有代码的设计者,但您需要能够交换出不同的实现。
Facade是到一个或多个类的子系统的更高级别(阅读:更简单)的接口。假设您有一个复杂的概念,需要多个对象来表示。对那组对象进行更改会造成混乱,因为您并不总是知道哪个对象具有需要调用的方法。是时候编写Facade,为您可以对对象集合执行的所有复杂操作提供高级方法了。例如:一个域模型为学校部分,与类似的方法countStudents()
,reportAttendance()
,assignSubstituteTeacher()
,等。
它们的结构也是如此。
代理和装饰器都具有与其包装类型相同的接口,但是代理在幕后创建了一个实例,而装饰器在构造函数中采用了一个实例。
Adapter和Facade都具有与其包装不同的接口。但是适配器是从现有接口派生的,而外观则创建了新接口。
网桥和适配器都指向现有类型。但是网桥将指向抽象类型,而适配器可能指向具体类型。桥接器将允许您在运行时配对实现,而适配器通常不会。
我对这个问题的看法。
这四种模式有很多共同点,有时有时将这四种模式非正式地称为包装器或包装器模式。所有使用组合,包装主题并在某个时候将执行委托给主题,都将一个方法调用映射到另一个方法调用。他们使客户不必构造另一个对象并复制所有相关数据。如果使用得当,它们可以节省内存和处理器。
通过促进松散耦合,它们使曾经稳定的代码减少了不可避免的更改,并让其他开发人员更易读。
适配器
适配器可将主题(适配器)适应不同的界面。这样,我们可以将对象添加到名义上不同类型的集合中。
适配器仅向客户端公开相关方法,可以限制所有其他方法,揭示特定上下文的使用意图,例如适应外部库,使其显得不太通用,并且更加专注于我们的应用程序需求。适配器提高了代码的可读性和自我描述。
适配器使一个团队免受其他团队的易失性代码的攻击;与海上团队打交道时的救生工具;-)
较少提及的目的是为了防止主题类过多的注释。有了这么多基于注释的框架,这比以往任何时候都变得更加重要。
适配器有助于绕开Java单一继承的限制。它可以将多个适配器集成到一个信封中,给人留下多重继承的印象。
在代码方面,适配器是“薄”的。除了简单地调用Adaptee方法和偶尔进行此类调用所需的数据转换外,它不应向Adaptee类添加太多代码。
JDK或基本库中没有很多好的适配器示例。应用程序开发人员创建适配器,以使库适应特定于应用程序的接口。
装饰器
装饰器不仅委托,而且不仅将一个方法映射到另一方法,而且执行更多操作,修改某些主题方法的行为,它可以决定根本不调用主题方法,委托给另一个对象(帮助对象)。
装饰器通常向包装的对象添加(透明)功能,例如记录,加密,格式化或压缩主题。此新功能可能会带来很多新代码。因此,装饰器通常比适配器更“笨拙”。
装饰器必须是主题界面的子类。它们可以透明地代替其主题使用。请参见BufferedOutputStream,它仍然是OutputStream,可以这样使用。这是与适配器的主要技术差异。
JDK-Java IO中提供了整个装饰器系列的教科书示例。像BufferedOutputStream,FilterOutputStream和ObjectOutputStream之类的所有类都是OutputStream的装饰器。它们可以是洋葱分层的,可以再装饰一个装饰器,从而增加了更多功能。
代理
代理不是典型的包装器。被包装的对象(代理主体)在创建代理时可能还不存在。代理通常在内部创建它。它可能是按需创建的繁重对象,也可能是不同JVM或不同网络节点中的远程对象,甚至是非Java对象(本机代码中的组件)。它根本不需要包装或委托给另一个对象。
最典型的示例是远程代理,重对象初始化程序和访问代理。
远程代理–主题位于远程服务器,其他JVM甚至非Java系统上。代理将方法调用转换为RMI / REST / SOAP调用或所需的任何内容,从而使客户端免于暴露于基础技术。
延迟加载代理–仅首次使用或首次密集使用完全初始化对象。
访问代理服务器–控制对主题的访问。
正面
外立面与最小知识的设计原理(得墨meter耳法则)密切相关。外观与适配器非常相似。它们都包装,它们都将一个对象映射到另一个对象,但是它们的意图不同。外立面展平主题,复杂对象图的复杂结构,从而简化了对复杂结构的访问。
外墙包裹了一个复杂的结构,为其提供了一个扁平的界面。这样可以防止客户对象暴露于主体结构中的内部关系,从而促进松散耦合。
桥
适配器模式的更复杂的变体,不仅实现有所不同,而且抽象也不同。它为委托添加了另一种间接方式。额外的授权是桥梁。它将适配器与适配接口解耦。与其他包装方式相比,它增加了更多的复杂性,因此请谨慎使用。
构造函数的差异
模式差异在查看其构造函数时也很明显。
代理不包装现有对象。构造函数中没有主题。
Decorator和Adapter确实包装了已经存在的对象,并且通常
在构造函数中提供此类对象。
Facade构造函数采用整个对象图的根元素,否则它看起来与Adapter相同。
现实生活中的示例– JAXB编组适配器。此适配器的目的是将简单的平面类映射到外部所需的更复杂的结构,并防止使用过多的注释“污染”主题类。
专家们的所有好的答案都已经解释了每种模式代表什么。
我将装饰关键点。
装饰器:
例如(带有链接):java.io
与InputStream
&OutputStream
接口相关的包类
FileOutputStream fos1 = new FileOutputStream("data1.txt");
ObjectOutputStream out1 = new ObjectOutputStream(fos1);
代理:
例如:java.rmi
包类。
适配器:
例如java.io.InputStreamReader
(InputStream
返回a Reader
)
桥:
例如中的Collection类java.util
。List
由实施ArrayList
。
重要说明:
查看有关各种设计模式示例的出色SE问题/文章
它们非常相似,并且它们之间的线条非常灰色。我建议您阅读c2 Wiki中的Proxy Pattern和Decorator Pattern条目。
那里的条目和讨论非常广泛,它们也链接到其他相关文章。顺便说一句,当想知道不同模式之间的细微差别时,c2 Wiki非常出色。
总结一下c2项,我想说一个装饰器添加/更改行为,但是代理与访问控制(延迟实例化,远程访问,安全性等)有更多关系。但是就像我说的那样,它们之间的线条是灰色的,而且我看到对代理的引用很容易被视为装饰者,反之亦然。
定义属于书籍。例子属于我。
装饰器 -不更改界面,但增加责任。假设您有汽车接口,那么当您针对汽车的不同模型(s,sv,sl)实现此接口时,您可能需要为某些模型增加更多责任。如有天窗,安全气囊等。
适配器 -将一个接口转换为另一个接口。您有汽车接口,并且希望它像吉普车一样工作。因此,您乘汽车,对其进行改装并变成一辆吉普车。由于它不是真正的吉普车。但是就像吉普车。
外观 -使界面更简单。假设您有汽车,飞机,轮船界面。实际上,您所需要的只是一门课程,该课程将人们从一个位置转移到另一个位置。您希望立面决定使用哪种车辆。然后,在1个保护伞下收集所有这些接口引用,并让其决定/委托以使其简单。
首先,“ Facade不仅简化了接口,而且使客户端与组件子系统分离。Facade和适配器可以包装多个类,但是Facade的目的是简化,而Adapter的目的是将接口转换为不同的东西。 ”
在使用Web服务时,我经常使用它。Proxy Pattern可能应该重命名为更实用的名称,例如“ Wrapper Pattern”。我还有一个库,它是MS Excel的代理。它使Excel的自动化变得非常容易,而不必担心背景细节(例如什么)。版本已安装(如果有)。
说到细节实现,我发现Proxy和Decorator,Adapter,Facade之间存在区别。在这些模式的常见实现中,有一个目标对象被一个封闭的对象包装。客户端使用封闭对象而不是目标对象。实际上,目标对象在某些封闭对象的方法中起着重要的作用。
但是,在使用代理服务器的情况下,封闭对象可以自己播放一些方法,它只是在客户端调用一些需要目标对象参与的方法时才初始化目标对象。这是延迟的初始化。在其他模式的情况下,包围对象实际上基于目标对象。因此,目标对象总是与构造器/设置器中的封闭对象一起初始化。
另一件事,代理完全可以执行目标的操作,而其他模式则可以为目标添加更多功能。
我想在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 { ... }
- 装饰器也称为“智能代理”。当您要向对象添加功能而不是通过扩展该对象的类型时,可以使用此方法。这使您可以在运行时这样做。
DecoratorClass应该(可以)实现ObjectClass的扩展接口。因此,可以用DecoratorClass代替ObjectClass,反之亦然。
示例-添加附加功能
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 { ... }
- 当您具有抽象接口,并且想要将该接口映射到另一个具有相似功能角色但接口不同的对象时,将使用适配器。
实施差异代理,装饰器,适配器
适配器为其主题提供了不同的接口。代理提供相同的接口。装饰器提供增强的界面。
Bridge与Adapter非常相似,但是当您定义抽象接口和基础实现时,我们将其称为Bridge。也就是说,您不适应某些遗留代码或第三方代码,而是所有代码的设计者,但您需要能够交换出不同的实现。
Facade是到一个或多个类的子系统的更高级别(阅读:更简单)的接口。假设您有一个复杂的概念,需要多个对象来表示。对那组对象进行更改会造成混乱,因为您并不总是知道哪个对象具有需要调用的方法。是时候编写Facade了,它为您可以对对象集合执行的所有复杂操作提供了高级方法。例如:一个域模型为学校部分,与类似的方法
countStudents()
,reportAttendance()
,assignSubstituteTeacher()
,等。
此答案中的大多数信息来自https://sourcemaking.com/design_patterns,我建议将其作为设计模式的绝佳资源。
我相信代码会给出明确的想法(也可以补充其他答案)。请参见下面的内容(关注类实现和包装的类型)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TestConsole
{
class Program
{
static void Main(string[] args)
{
/* Proxy */
Console.WriteLine(Environment.NewLine);
Console.WriteLine("PROXY");
Console.WriteLine(Environment.NewLine);
//instead of creating here create using a factory method, the facory method will return the proxy
IReal realProxy = new RealProxy();
Console.WriteLine("calling do work with the proxy object ");
realProxy.DoWork();
Console.WriteLine(Environment.NewLine);
Console.WriteLine("ADAPTER");
Console.WriteLine(Environment.NewLine);
/*Adapter*/
IInHand objectIHave = new InHand();
Api myApi = new Api();
//myApi.SomeApi(objectIHave); /*I cant do this, use a adapter then */
IActual myAdaptedObject = new ActualAdapterForInHand(objectIHave);
Console.WriteLine("calling api with my adapted obj");
myApi.SomeApi(myAdaptedObject);
Console.WriteLine(Environment.NewLine);
Console.WriteLine("DECORATOR");
Console.WriteLine(Environment.NewLine);
/*Decorator*/
IReady maleReady = new Male();
Console.WriteLine("now male is going to get ready himself");
maleReady.GetReady();
Console.WriteLine(Environment.NewLine);
IReady femaleReady = new Female();
Console.WriteLine("now female is going to get ready her self");
femaleReady.GetReady();
Console.WriteLine(Environment.NewLine);
IReady maleReadyByBeautician = new Beautician(maleReady);
Console.WriteLine("now male is going to get ready by beautician");
maleReadyByBeautician.GetReady();
Console.WriteLine(Environment.NewLine);
IReady femaleReadyByBeautician = new Beautician(femaleReady);
Console.WriteLine("now female is going to get ready by beautician");
femaleReadyByBeautician.GetReady();
Console.WriteLine(Environment.NewLine);
Console.ReadLine();
}
}
/*Proxy*/
public interface IReal
{
void DoWork();
}
public class Real : IReal
{
public void DoWork()
{
Console.WriteLine("real is doing work ");
}
}
public class RealProxy : IReal
{
IReal real = new Real();
public void DoWork()
{
real.DoWork();
}
}
/*Adapter*/
public interface IActual
{
void DoWork();
}
public class Api
{
public void SomeApi(IActual actual)
{
actual.DoWork();
}
}
public interface IInHand
{
void DoWorkDifferently();
}
public class InHand : IInHand
{
public void DoWorkDifferently()
{
Console.WriteLine("doing work slightly different ");
}
}
public class ActualAdapterForInHand : IActual
{
IInHand hand = null;
public ActualAdapterForInHand()
{
hand = new InHand();
}
public ActualAdapterForInHand(IInHand hnd)
{
hand = hnd;
}
public void DoWork()
{
hand.DoWorkDifferently();
}
}
/*Decorator*/
public interface IReady
{
void GetReady();
}
public class Male : IReady
{
public void GetReady()
{
Console.WriteLine("Taking bath.. ");
Console.WriteLine("Dress up....");
}
}
public class Female : IReady
{
public void GetReady()
{
Console.WriteLine("Taking bath.. ");
Console.WriteLine("Dress up....");
Console.WriteLine("Make up....");
}
}
//this is a decorator
public class Beautician : IReady
{
IReady ready = null;
public Beautician(IReady rdy)
{
ready = rdy;
}
public void GetReady()
{
ready.GetReady();
Console.WriteLine("Style hair ");
if (ready is Female)
{
for (int i = 1; i <= 10; i++)
{
Console.WriteLine("doing ready process " + i);
}
}
}
}
}