代码之家  ›  专栏  ›  技术社区  ›  Charles Graham

代理、装饰器、适配器和网桥模式有何区别?

  •  363
  • Charles Graham  · 技术社区  · 17 年前

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

    13 回复  |  直到 7 年前
        1
  •  695
  •   Bill Karwin    11 年前

    代理、装饰器、适配器和桥都是“包装”类的变体。但它们的用途不同。

    • 代理

    • 室内装修设计师 也被称为“智能代理”。当您希望向对象添加功能时,可使用此选项,但不能扩展该对象的类型。这允许您在运行时这样做。

    • 适配器 当您有一个抽象接口,并且希望将该接口映射到另一个具有类似功能角色但不同接口的对象时,使用。

    • 非常类似于适配器,但在定义抽象接口和底层实现时,我们称之为桥接。也就是说,您没有适应某些遗留或第三方代码,您是所有代码的设计者,但您需要能够交换不同的实现。

    • 外观 countStudents() reportAttendance() assignSubstituteTeacher() 等等

        2
  •  218
  •   Community Mohan Dere    9 年前

    As Bill's answer says, their use cases are different

    • 室内装修设计师 两者都具有与其包装类型相同的接口,但代理在引擎盖下创建实例,而装饰器在构造函数中获取实例。

    • 外观

        3
  •  64
  •   Afshin Moazami Darxis    11 年前

    我对这个问题的看法。

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

    通过促进松耦合,它们使曾经稳定的代码更少地暴露于不可避免的更改中,并且使其他开发人员更易于阅读。

    适配器

    适配器将主题(适配器)适配到不同的接口。通过这种方式,我们可以将对象添加到名义上不同类型的集合中。

    适配器只向客户机公开相关的方法,可以限制所有其他方法,揭示特定上下文的使用意图,如调整外部库,使其看起来不那么通用,更关注我们的应用程序需求。适配器增加了代码的可读性和自我描述。

    适配器保护一个团队不受来自其他团队的易失性代码的影响;与海上团队打交道时的救世主工具;-)

    适配器有助于绕过Java仅支持单一继承的限制。它可以在一个信封下组合多个适应项,给人多重继承的印象。

    代码方面,适配器很薄。它不应该向adaptee类添加太多代码,除了简单地调用adaptee方法和进行此类调用所需的偶尔数据转换之外。

    室内装修设计师

    装饰者通常(透明地)向包装对象添加功能,如日志记录、加密、格式化或主题压缩。这个新功能可能会带来很多新代码。因此,装饰师通常比适配器胖得多。

    装饰器必须是主题接口的子类。它们可以透明地使用,而不是其主题。请参阅BufferedOutputStream,它仍然是OutputStream,可以这样使用。这是与适配器的主要技术区别。

    整个decorators家族的教科书示例都可以在JDK-JavaIO中找到。所有班级都喜欢 BufferedOutputStream , FilterOutputStream ObjectOutputStream 你是建筑的装饰师吗 OutputStream . 它们可以是洋葱状分层的,其中一个装饰者被再次装饰,增加了更多的功能。

    代理

    代理不是典型的包装器。在创建代理时,包装的对象(代理主体)可能还不存在。代理通常在内部创建它。它可能是按需创建的重对象,也可能是不同JVM或不同网络节点中的远程对象,甚至是非Java对象,本机代码中的组件。它根本不需要包装或委托给另一个对象。

    最典型的例子是远程代理、重对象初始化器和访问代理。

    • Java系统。代理将方法调用转换为RMI/REST/SOAP调用或 无论需要什么,保护客户免受潜在风险的影响

    • 首先是密集使用。

    • 访问代理控制对主题的访问。

    外观

    适配器模式的更复杂变体,其中不仅实现不同,而且抽象也不同。它又为委托添加了一个间接操作。额外的代表团是桥梁。它甚至可以使适配器与适配接口分离。它比任何其他包装图案都更复杂,所以请小心使用。

    构造器的差异

    • 代理 未包装现有对象。构造函数中没有主题。

    • 室内装修设计师 适配器
      在构造函数中提供。

    • 外观 构造函数获取整个对象图的根元素,否则

    JAXB Marshalling Adapter . 此适配器的目的是将简单的平面类映射到外部所需的更复杂的结构,并防止使用过多的注释“污染”主题类。

        4
  •  29
  •   Dinah SLaks    13 年前

    阅读后,我对模式的理解提高了100倍 Head First Design Patterns

    我强烈推荐它!

        5
  •  16
  •   Ravindra babu    8 年前

    所有来自专家的好答案都已经解释了每种模式代表什么。

    我会的 装饰 关键点。

    装饰师:

    1. 在运行时向对象添加行为
    2. 行为

    e、 g.(带链接): java.io InputStream & OutputStream

    FileOutputStream fos1 = new FileOutputStream("data1.txt");  
    ObjectOutputStream out1 = new ObjectOutputStream(fos1);
    

    1. 使用它进行延迟初始化,通过缓存对象和控制对客户端/调用方的访问来提高性能 . 它可以提供替代行为或调用真实对象。在此过程中,它可能会创建新对象。
    2. 不像 室内装修设计师 ,它允许对象链接,代理

    例如。: java.rmi 包类。

    适配器:

    1. 它允许两个不相关的接口通过不同的对象一起工作 ,可能扮演同样的角色。
    2. 它修改了原始接口 .

    例如 java.io.InputStreamReader 输入流 Reader

    桥:

    1. 它允许抽象和实现独立变化 .
    2. 它使用 组合重于继承

    e、 g.收集课程 java.util . List 实施人 ArrayList .

    主要注意事项:

    1. 为其主题提供不同的界面。 代理 提供相同的接口。
    2. 适配器 更改对象的接口, 增强对象的责任。
    3. 目的不同但结构相似
    4. 适配器 使事物在设计完成后工作; 让他们在工作之前就开始工作。
    5. 适配器
    6. 室内装修设计师 旨在允许您向对象添加职责,而无需子类化。

    看看关于各种设计模式示例的优秀SE问题/文章

    When to Use the Decorator Pattern?

    When do you use the Bridge Pattern? How is it different from Adapter pattern?

    Differences between Proxy and Decorator Pattern

        6
  •  8
  •   Cristian Ciupitu    11 年前

    Proxy Pattern Decorator Pattern

    那里的条目和讨论相当广泛,它们还链接到其他相关文章。顺便说一下,c2 wiki在了解不同模式之间的细微差别时非常出色。

    总结c2条目,我想说装饰器添加/更改行为,但代理与访问控制(延迟实例化、远程访问、安全性等)有更多关系。但正如我所说,它们之间的线条是灰色的,我看到的代理很容易被视为装饰者,反之亦然。

        7
  •  6
  •   Teoman shipahi    11 年前

    这是引自 Head First Design Patterns

    定义属于书本。例子属于我。

    -不改变接口,但增加了责任。假设你有一个汽车接口, 当您为不同车型(s、sv、sl)实施此功能时,您可能需要 增加更多的责任

    适配器 -将一个接口转换为另一个接口。你有一个汽车接口,你希望它像吉普一样。所以你把车改装成吉普车。 因为它不是一辆真正的吉普车。但却像一辆吉普车。

    外观 -使界面更简单。假设你有汽车、飞机、轮船的接口。实际上,您所需要的只是一个类,它将人们从一个位置发送到另一个位置。您希望facade决定使用哪种车辆。然后你收集所有这些

    首先:“facade不仅简化了接口,还将客户机与子系统分离 Facades和适配器可以封装多个类,但Facades的目的是简化,而 适配器用于将接口转换为不同的内容。”

        8
  •  5
  •   Alexey    13 年前

    这四种模式都涉及到用外部对象/类包装内部对象/类,因此它们在结构上非常相似。我将根据目的概述不同之处:

    • 代理
    • 用外部修改或扩展内部的行为。
    • 适配器
    • 将行为的不变部分(外部)与变量或平台相关部分(内部)分开。

    通过内部和外部对象之间的界面变化:

    • 在里面 代理
    • 在里面 室内装修设计师
    • 在里面 适配器
    • 在里面 接口在概念上是不同的。
        9
  •  1
  •   hmcclungiii    17 年前

    我在使用web服务时经常使用它。代理模式可能应该改名为更实用的模式,如“包装模式”。我还有一个库,它是MS Excel的代理。它使Excel自动化变得非常容易,而不必担心后台细节,例如安装了什么版本(如果有的话)。

        10
  •  1
  •   bnguyen82    14 年前

    谈到具体的实现,我发现代理和装饰器、适配器、Facade。。。在这些模式的常见实现中,有一个由封闭对象包装的目标对象。客户端使用封闭对象而不是目标对象。而目标对象实际上在一些封闭对象的方法中起着重要的作用。

    然而,在代理的情况下,封闭对象可以自己扮演一些方法,当客户端调用一些需要目标对象参与的方法时,它只是初始化目标对象。这是延迟初始化。对于其他模式,封闭对象实际上基于目标对象。所以目标对象总是和构造函数/设置器中的封闭对象一起初始化。

    另一件事,代理做的正是目标做的,而其他模式为目标添加了更多功能。

        11
  •  1
  •   Community Mohan Dere    9 年前

    我想为Bill Karwing的答案添加一些例子(顺便说一句,这很好。) 我还添加了一些关键的实现差异,我觉得这些差异是缺失的

    https://stackoverflow.com/a/350471/1984346] (比尔·卡温)

    代理、装饰器、适配器和桥都是“包装”类的变体。

    • 代理 可以在您想要延迟实例化对象时使用,或者 对该对象。

    代理的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的扩展接口。因此,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提供了一个增强的接口。

    • 定义抽象接口和底层实现。 也就是说,您没有适应某些遗留或第三方代码,而是 不同的实现。

    • 外观 一个或多个类。假设您有一个复杂的概念,需要 是令人困惑的,因为您并不总是知道哪个对象具有 您需要调用的方法。是时候写一个门面了 为您可以执行的所有复杂操作提供高级方法 节,使用以下方法 countStudents() , reportAttendance() , assignSubstituteTeacher()

    此答案中的大部分信息来自 https://sourcemaking.com/design_patterns ,我建议作为 用于设计模式。

        12
  •  1
  •   Tunaki    10 年前

    我相信代码会给出一个清晰的想法(以补充其他答案)。请参见下文(重点关注类实现和包装的类型)

    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);
                    }
    
                }
            }
        }
    
    }
    
        13
  •  -3
  •   Mahantesh    12 年前

    设计模式不是数学,它是艺术和软件工程的结合。对于这个要求,没有什么比得上使用代理、桥接等。创建设计模式是为了解决这些问题。如果你预计到一个设计问题,那么就使用它。根据经验,您将了解具体问题,使用哪种模式。如果您擅长扎实的设计原则,那么您可能已经实现了设计模式,而不知道它是一种模式。常见的例子是statergy和factory模式

    因此,更多地关注固体设计原则、清洁编码原则和ttd

    推荐文章