我正在查看代理模式,对我来说,它看起来非常像装饰器、适配器和桥接模式。我是不是误解了什么?有什么不同?为什么我要使用代理模式而不是其他模式?在过去的实际项目中,您是如何使用它们的?


当前回答

我对这个问题的看法。

这四种模式有很多共同之处,这四种模式有时都被非正式地称为包装器或包装模式。所有这些都使用组合,包装主题,并在某些时候将执行委托给主题,将一个方法调用映射到另一个方法调用。它们使客户不必构造一个不同的对象并复制所有相关数据。如果使用得当,它们可以节省内存和处理器。

通过促进松耦合,它们使曾经稳定的代码更少地暴露在不可避免的更改中,对其他开发人员来说可读性更好。

适配器

适配器使主题(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倍。

我强烈推荐!