我正在查看代理模式,对我来说,它看起来非常像装饰器、适配器和桥接模式。我是不是误解了什么?有什么不同?为什么我要使用代理模式而不是其他模式?在过去的实际项目中,您是如何使用它们的?
当前回答
我对这个问题的看法。
这四种模式有很多共同之处,这四种模式有时都被非正式地称为包装器或包装模式。所有这些都使用组合,包装主题,并在某些时候将执行委托给主题,将一个方法调用映射到另一个方法调用。它们使客户不必构造一个不同的对象并复制所有相关数据。如果使用得当,它们可以节省内存和处理器。
通过促进松耦合,它们使曾经稳定的代码更少地暴露在不可避免的更改中,对其他开发人员来说可读性更好。
适配器
适配器使主题(adaptee)适应不同的接口。通过这种方式,我们可以将对象添加到名义上不同类型的集合中。
适配器只向客户端公开相关的方法,可以限制所有其他方法,揭示特定上下文的使用意图,比如适应外部库,使其看起来不那么通用,更专注于我们的应用程序需求。适配器增加了代码的可读性和自描述性。
适配器保护一个团队不受来自其他团队的易变代码的影响;在处理离岸团队时的救星工具;-)
较少提及的目的是防止主题类的注释过多。有这么多基于注解的框架,这就变得越来越重要了。
适配器有助于绕过Java只能单一继承的限制。它可以在一个信封下组合多个改编,给人一种多重遗传的印象。
在代码方面,Adapter是“瘦”的。它不应该向adaptee类添加太多代码,除了简单地调用adaptee方法和偶尔进行此类调用所需的数据转换之外。
JDK或基本库中没有很多好的适配器示例。应用程序开发人员创建适配器,使库适应应用程序特定的接口。
装饰
Decorator不仅仅是委托,不仅仅是将一个方法映射到另一个,它们做的更多,它们修改一些subject方法的行为,它可以决定不调用subject方法,委托给一个不同的对象,一个helper对象。
装饰器通常向包装对象添加(透明的)功能,如日志记录、加密、格式化或压缩主题。这个新功能可能会带来很多新代码。因此,装饰器通常比适配器“胖”得多。
Decorator必须是subject接口的子类。它们可以透明地使用,而不是其主题。参见BufferedOutputStream,它仍然是OutputStream,可以这样使用。这是与适配器的主要技术区别。
整个装饰器家族的教科书示例很容易在JDK - Java IO中。所有类,如BufferedOutputStream, FilterOutputStream和ObjectOutputStream都是OutputStream的装饰器。它们可以是洋葱层状的,其中一个装饰器被再次装饰,增加更多的功能。
代理
代理不是典型的包装器。被包装的对象(代理主题)在创建代理时可能还不存在。代理通常在内部创建它。它可能是按需创建的重载对象,也可能是不同JVM或不同网络节点中的远程对象,甚至是非java对象,本机代码中的组件。它根本不需要包装或委托给另一个对象。
最典型的例子是远程代理、重对象初始化器和访问代理。
远程代理-主题在远程服务器上,不同的JVM甚至非 Java系统。代理将方法调用转换为RMI/REST/SOAP调用或 无论需要什么,保护病人不受潜在危险的影响 技术。 惰性加载代理-完全初始化对象只有第一次使用或 第一次密集使用。 访问代理-控制对主题的访问。
外观
立面与设计的最小知识原则(得墨忒耳定律)密切相关。 Facade非常类似于Adapter。它们都是包装,都是将一个对象映射到另一个对象,但它们的意图不同。立面扁平化了复杂结构的主题,复杂的对象图形,简化了对复杂结构的访问。
Facade包装了一个复杂的结构,为其提供了一个平面接口。这可以防止客户端对象暴露于主题结构中的内部关系,从而促进松耦合。
桥
适配器模式的更复杂变体,不仅实现不同,而且抽象也不同。它为委托增加了另一种间接方式。额外的代表团是一座桥梁。它甚至从适配接口中分离了适配器。它比其他任何包装模式都增加了更多的复杂性,因此应用时要小心。
构造函数的差异
在查看它们的构造函数时,模式差异也很明显。
代理没有包装现有对象。构造函数中没有主语。 装饰器和适配器包装已经存在的对象,这是典型的 在构造函数中提供。 外观构造函数取整个对象图的根元素,否则看起来 与Adapter相同。
真实的例子——JAXB编组适配器。该适配器的目的是将简单的平面类映射到外部所需的更复杂的结构,并防止用过多的注释“污染”主题类。
其他回答
所有这四种模式都涉及到用外部对象/类包装内部对象/类,因此它们在结构上非常相似。我将通过目的来概述不同之处:
代理将访问从外部封装到内部。 装饰器用外部修改或扩展内部的行为。 适配器转换接口从内部到外部。 桥将行为的不变部分(外部)与变量或平台依赖部分(内部)分开。
通过内外物体之间的界面变化:
在代理接口中是相同的。 在Decorator接口中是相同的。 在适配器接口形式上不同,但实现相同的目的。 桥接接口在概念上是不同的。
在使用web服务时,我经常使用它。代理模式可能应该重命名为更实用的东西,比如“包装器模式”。我也有一个库,是一个代理到MS Excel。它使自动化Excel变得非常容易,而不必担心背景细节,如安装了什么版本(如果有的话)。
我对这个问题的看法。
这四种模式有很多共同之处,这四种模式有时都被非正式地称为包装器或包装模式。所有这些都使用组合,包装主题,并在某些时候将执行委托给主题,将一个方法调用映射到另一个方法调用。它们使客户不必构造一个不同的对象并复制所有相关数据。如果使用得当,它们可以节省内存和处理器。
通过促进松耦合,它们使曾经稳定的代码更少地暴露在不可避免的更改中,对其他开发人员来说可读性更好。
适配器
适配器使主题(adaptee)适应不同的接口。通过这种方式,我们可以将对象添加到名义上不同类型的集合中。
适配器只向客户端公开相关的方法,可以限制所有其他方法,揭示特定上下文的使用意图,比如适应外部库,使其看起来不那么通用,更专注于我们的应用程序需求。适配器增加了代码的可读性和自描述性。
适配器保护一个团队不受来自其他团队的易变代码的影响;在处理离岸团队时的救星工具;-)
较少提及的目的是防止主题类的注释过多。有这么多基于注解的框架,这就变得越来越重要了。
适配器有助于绕过Java只能单一继承的限制。它可以在一个信封下组合多个改编,给人一种多重遗传的印象。
在代码方面,Adapter是“瘦”的。它不应该向adaptee类添加太多代码,除了简单地调用adaptee方法和偶尔进行此类调用所需的数据转换之外。
JDK或基本库中没有很多好的适配器示例。应用程序开发人员创建适配器,使库适应应用程序特定的接口。
装饰
Decorator不仅仅是委托,不仅仅是将一个方法映射到另一个,它们做的更多,它们修改一些subject方法的行为,它可以决定不调用subject方法,委托给一个不同的对象,一个helper对象。
装饰器通常向包装对象添加(透明的)功能,如日志记录、加密、格式化或压缩主题。这个新功能可能会带来很多新代码。因此,装饰器通常比适配器“胖”得多。
Decorator必须是subject接口的子类。它们可以透明地使用,而不是其主题。参见BufferedOutputStream,它仍然是OutputStream,可以这样使用。这是与适配器的主要技术区别。
整个装饰器家族的教科书示例很容易在JDK - Java IO中。所有类,如BufferedOutputStream, FilterOutputStream和ObjectOutputStream都是OutputStream的装饰器。它们可以是洋葱层状的,其中一个装饰器被再次装饰,增加更多的功能。
代理
代理不是典型的包装器。被包装的对象(代理主题)在创建代理时可能还不存在。代理通常在内部创建它。它可能是按需创建的重载对象,也可能是不同JVM或不同网络节点中的远程对象,甚至是非java对象,本机代码中的组件。它根本不需要包装或委托给另一个对象。
最典型的例子是远程代理、重对象初始化器和访问代理。
远程代理-主题在远程服务器上,不同的JVM甚至非 Java系统。代理将方法调用转换为RMI/REST/SOAP调用或 无论需要什么,保护病人不受潜在危险的影响 技术。 惰性加载代理-完全初始化对象只有第一次使用或 第一次密集使用。 访问代理-控制对主题的访问。
外观
立面与设计的最小知识原则(得墨忒耳定律)密切相关。 Facade非常类似于Adapter。它们都是包装,都是将一个对象映射到另一个对象,但它们的意图不同。立面扁平化了复杂结构的主题,复杂的对象图形,简化了对复杂结构的访问。
Facade包装了一个复杂的结构,为其提供了一个平面接口。这可以防止客户端对象暴露于主题结构中的内部关系,从而促进松耦合。
桥
适配器模式的更复杂变体,不仅实现不同,而且抽象也不同。它为委托增加了另一种间接方式。额外的代表团是一座桥梁。它甚至从适配接口中分离了适配器。它比其他任何包装模式都增加了更多的复杂性,因此应用时要小心。
构造函数的差异
在查看它们的构造函数时,模式差异也很明显。
代理没有包装现有对象。构造函数中没有主语。 装饰器和适配器包装已经存在的对象,这是典型的 在构造函数中提供。 外观构造函数取整个对象图的根元素,否则看起来 与Adapter相同。
真实的例子——JAXB编组适配器。该适配器的目的是将简单的平面类映射到外部所需的更复杂的结构,并防止用过多的注释“污染”主题类。
我相信代码会给出一个清晰的想法(补充其他答案)。请参见下面,(关注类实现和包装的类型)
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);
}
}
}
}
}
在许多GoF模式中有大量的重叠。它们都建立在多态性的力量之上,有时只是意图不同。(战略vs.国家)
在阅读了头部优先设计模式之后,我对模式的理解增加了100倍。
我强烈推荐!
推荐文章
- 如何在Kotlin中实现生成器模式?
- 工厂模式和策略模式之间的区别是什么?
- 如何向一个5岁的孩子解释依赖注入?
- 我怎么知道什么时候创建一个接口?
- 在PHP5中创建单例设计模式
- 什么时候我们应该使用观察者和可观察对象?
- 在类主体中调用类静态方法?
- 在哪里放置AutoMapper.CreateMaps?
- 使用Enum实现单例(Java)
- 由Jon Skeet撰写的《Singleton》澄清
- 为什么c#不提供c++风格的'friend'关键字?
- 如何在python抽象类中创建抽象属性?
- 如何实现typescript装饰器?
- 是否有一个装饰器来缓存函数返回值?
- Redux @connect装饰器中的“@”(at符号)是什么?