How do the Proxy, Decorator, Adapter, and Bridge Patterns differ?

天大地大妈咪最大 提交于 2019-12-16 22:19:31

问题


I was looking at the Proxy Pattern, and to me it seems an awful lot like the Decorator, Adapter, and Bridge patterns. Am I misunderstanding something? What's the difference? Why would I use the Proxy pattern versus the others? How have you used them in the past in real world projects?


回答1:


Proxy, Decorator, Adapter, and Bridge are all variations on "wrapping" a class. But their uses are different.

  • Proxy could be used when you want to lazy-instantiate an object, or hide the fact that you're calling a remote service, or control access to the object.

  • Decorator is also called "Smart Proxy." This is used when you want to add functionality to an object, but not by extending that object's type. This allows you to do so at runtime.

  • Adapter is used when you have an abstract interface, and you want to map that interface to another object which has similar functional role, but a different interface.

  • Bridge is very similar to Adapter, but we call it Bridge when you define both the abstract interface and the underlying implementation. I.e. you're not adapting to some legacy or third-party code, you're the designer of all the code but you need to be able to swap out different implementations.

  • Facade is a higher-level (read: simpler) interface to a subsystem of one or more classes. Suppose you have a complex concept that requires multiple objects to represent. Making changes to that set of objects is confusing, because you don't always know which object has the method you need to call. That's the time to write a Facade that provides high-level methods for all the complex operations you can do to the collection of objects. Example: a Domain Model for a school section, with methods like countStudents(), reportAttendance(), assignSubstituteTeacher(), and so on.




回答2:


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

So are their structures.

  • Proxy and Decorator both have the same interface as their wrapped types, but the proxy creates an instance under the hood, whereas the decorator takes an instance in the constructor.

  • Adapter and Facade both have a different interface than what they wrap. But the adapter derives from an existing interface, whereas the facade creates a new interface.

  • Bridge and Adapter both point at an existing type. But the bridge will point at an abstract type, and the adapter might point to a concrete type. The bridge will allow you to pair the implementation at runtime, whereas the adapter usually won't.




回答3:


My take on the subject.

All four patterns have a lot in common, all four are sometimes informally called wrappers, or wrapper patterns. All use composition, wrapping subject and delegating the execution to the subject at some point, do mapping one method call to another one. They spare client the necessity of having to construct a different object and copy over all relevant data. If used wisely, they save memory and processor.

By promoting loose coupling they make once stable code less exposed to inevitable changes and better readable for fellow developers.

Adapter

Adapter adapts subject (adaptee) to a different interface. This way we can add object be placed to a collection of nominally different types.

Adapter expose only relevant methods to client, can restrict all others, revealing usage intents for particular contexts, like adapting external library, make it appear less general and more focused on our application needs. Adapters increase readability and self description of our code.

Adapters shields one team from volatile code from other teams; a life savior tool when dealing with offshore teams ;-)

Less mentioned purpose it to prevent the subject class from excess of annotations. With so many frameworks based on annotations this becomes more important usage then ever.

Adapter helps to get around Java limitation of only single inheritance. It can combine several adaptees under one envelope giving impression of multiple inheritance.

Code wise, Adapter is “thin”. It should not add much code to the adaptee class, besides simply calling the adaptee method and occasional data conversions necessary to make such calls.

There are not many good adapter examples in JDK or basic libraries. Application developers create Adapters, to adapt libraries to application specific interfaces.

Decorator

Decorator not only delegate, not only maps one method to another, they do more, they modify behaviour of some subject methods, it can decide not call subject method at all, delegate to a different object, a helper object.

Decorators typically add (transparently) functionality to wrapped object like logging, encryption, formatting, or compression to subject. This New functionality may bring a lot of new code. Hence, decorators are usually much “fatter” then Adapters.

Decorator must be a sub-class of subject's interface. They can be used transparently instead of its subjects. See BufferedOutputStream, it is still OutputStream and can be used as such. That is a major technical difference from Adapters.

Text book examples of whole decorators family is readily in JDK - the Java IO. All classes like BufferedOutputStream, FilterOutputStream and ObjectOutputStream are decorators of OutputStream. They can be onion layered, where one one decorator is decorated again, adding more functionality.

Proxy

Proxy is not a typical wrapper. The wrapped object, the proxy subject, may not yet exist at the time of proxy creation. Proxy often creates it internally. It may be a heavy object created on demand, or it is remote object in different JVM or different network node and even a non-Java object, a component in native code. It does not have to necessary wrap or delegate to another object at all.

Most typical examples are remote proxies, heavy object initializers and access proxies.

  • Remote Proxy – subject is on remote server, different JVM or even non Java system. Proxy translates method calls to RMI/REST/SOAP calls or whatever is needed, shielding client from exposure to underlying technology.

  • Lazy Load Proxy – fully initialize object only the first usage or first intensive usage.

  • Access Proxy – control access to subject.

Facade

Facade is closely associated with design Principle of Least Knowledge (Law of Demeter). Facade is very similar to Adapter. They both wrap, they both map one object to another, but they differ in the intent. Facade flattens complex structure of a subject, complex object graph, simplifying access to a complex structure.

Facade wraps a complex structure, providing a flat interface to it. This prevents client object from being exposed to inner relations in subject structure hence promoting loose coupling.

Bridge

More complex variant of Adapter pattern where not only implementation varies but also abstraction. It adds one more indirection to the delegation. The extra delegation is the bridge. It decouples Adapter even from adapting interface. It increases complexity more than any other of the other wrapping patterns, so apply with care.

Differences in constructors

Pattern differences are also obvious when looking at their constructors.

  • Proxy is not wrapping an existing object. There is no subject in constructor.

  • Decorator and Adapter does wrap already existing object, and such is typically
    provided in the constructor.

  • Facade constructor takes root element of a whole object graph, otherwise it looks same as Adapter.

Real life example – JAXB Marshalling Adapter. Purpose of this adapter is mapping of a simple flat class to more complex structure required externally and to prevent "polluting" subject class with excessive annotations.




回答4:


There's a great deal of overlap in many of the GoF patterns. They're all built on the power of polymorphism and sometimes only really differ in intent. (strategy vs. state)

My understanding of patterns increased 100 fold after reading Head First Design Patterns.

I highly recommend it!




回答5:


All good answers from experts have already explained what does each pattern stands for.

I will decorate key points.

Decorator:

  1. Add behaviour to object at run time. Inheritance is the key to achieve this functionality, which is both advantage and disadvantage of this pattern.
  2. It modifies the behaviour of interface.

e.g. (with chaining ) : java.io package classes related to InputStream & OutputStream interfaces

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

Proxy:

  1. Use it for lazy initialization, performance improvement by caching the object and controlling access to the client/caller. It may provide alternative behaviour or call real object. During this process, it may create new Object.
  2. Unlike Decorator, which allows chaining of objects, Proxy does not allow chaining.

e.g.: java.rmi package classes.

Adapter:

  1. It allows two unrelated interfaces to work together through the different objects, possibly playing same role.
  2. It modifies original interface.

e.g. java.io.InputStreamReader (InputStream returns a Reader)

Bridge:

  1. It allows both abstractions and implementations to vary independently.
  2. It uses composition over inheritance.

e.g. Collection classes in java.util. List implemented by ArrayList.

Key notes:

  1. Adapter provides a different interface to its subject. Proxy provides the same interface. Decorator provides an enhanced interface.
  2. Adapter changes an object's interface, Decorator enhances an object's responsibilities.
  3. Decorator and Proxy have different purposes but similar structures
  4. Adapter makes things work after they're designed; Bridge makes them work before they are.
  5. Bridge is designed up-front to let the abstraction and the implementation vary independently. Adapter is retrofitted to make unrelated classes work together
  6. Decorator is designed to let you add responsibilities to objects without subclassing.

Have a look at great SE questions/articles regarding examples of various design patterns

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:


They are quite similar, and the lines between them are quite gray. I suggest you read the Proxy Pattern and Decorator Pattern entries in the c2 wiki.

The entries and discussions there are quite extensive, and they also link to other relevant articles. By the way, the c2 wiki is excellent when wondering about the nuances between different patterns.

To sum the c2 entries up, I would say a decorator adds/changes behavior, but a proxy has more to do with access control (lazy instantiation, remote access, security etc). But like I said, the lines between them are gray, and I see references to proxies that could easily be viewed as decorators and vice versa.




回答7:


This is quote from Head First Design Patterns

Definitions belongs to book. Examples belongs to me.

Decorator - Doesn’t alter the interface, but adds responsibility. Assume you have a car interface, when you implement this for different model of the car (s, sv, sl) you may need to add more responsibility for some models. Like has sunroof, airbag etc..

Adapter - Converts one interface to another. You have a car interface and you would like it to act like jeep. So you take the car, modify it and turn into a jeep. Since it is not a real jeep. But acts like a jeep.

Facade - Makes an interface simpler. Assume you have car, airplane, ship interfaces. Actually all you need is a class which sends people from one location to another. You want facade to decide what vehicle to use. Then you collect all those interface references under 1 umbrella and let it decide/delegate to keep it simple.

Head First: "A facade not only simplifies an interface, it decouples a client from a subsystem of components. Facades and adapters may wrap multiple classes, but a facade’s intent is to simplify, while an adapter’s is to convert the interface to something different."




回答8:


All of the four patterns involve wrapping inner object/class with outer one, so they are very similar structurally. I would outline difference by the purpose:

  • Proxy encapsulates access in outer to inner.
  • Decorator modifies or extends behaviour of inner with outer.
  • Adaptor converts interface from inner to outer.
  • Bridge separates invariable part of behaviour (outer) from variable or platform-dependent part (inner).

And by interface variation between inner and outer objects:

  • in Proxy interfaces are the same.
  • in Decorator interfaces are the same.
  • in Adaptor interfaces are different formally, but fulfil the same purpose.
  • in Bridge interfaces are different conceptually.



回答9:


I use it quite often when consuming web services. The Proxy Pattern should probably be renamed to something more pragmatic, like 'Wrapper Pattern". I also have a library that is a Proxy to MS Excel. It makes it very easy to automate Excel, without having to worry about background details such as what version is installed (if any).




回答10:


Speaking detail implementation, I find a difference between Proxy and Decorator, Adapter, Facade ... In common implementation of these patterns there's a target object wrapped by a enclosing object. Client uses enclosing object instead of target object. And the target object actually play an important part inside some of methods of enclosing object.

However, in case of Proxy, enclosing object can play some methods by itself, it just initialize target object when client calls some methods that it needs target object take part in. This is lazy initialization. In case of other patterns, enclosing object is virtually based on target object. So target object is always initialized along with enclosing object in constructors/setters.

Another thing, a proxy does exactly what a target does whereas other patterns add more functionality to target.




回答11:


I would like to add examples to Bill Karwing answer (which is great btw.) I add also some key differences of implementation, that I feel are missing

Quoted parts are from answer of [https://stackoverflow.com/a/350471/1984346] (Bill Karwing)

Proxy, Decorator, Adapter, and Bridge are all variations on "wrapping" a class. But their uses are different.

  • Proxy could be used when you want to lazy-instantiate an object, or hide the fact that you're calling a remote service, or control access to the object.

ProxyClass and ObjectClass that is proxied, should implement same interface, so they are interchangable

Example - proxy expensive object

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 is also called "Smart Proxy." This is used when you want to add functionality to an object, but not by extending that object's type. This allows you to do so at runtime.

DecoratorClass should(could) implement extended interface of ObjectClass. So the ObjectClass could be replaced by DecoratorClass, but not vice versa.

Example - adding addition functionality

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 { ... }
  • Adapter is used when you have an abstract interface, and you want to map that interface to another object which has similar functional role, but a different interface.

Implentation differences Proxy, Decorator, Adapter

Adapter provides a different interface to its subject. Proxy provides the same interface. Decorator provides an enhanced interface.

  • Bridge is very similar to Adapter, but we call it Bridge when you define both the abstract interface and the underlying implementation. I.e. you're not adapting to some legacy or third-party code, you're the designer of all the code but you need to be able to swap out different implementations.

  • Facade is a higher-level (read: simpler) interface to a subsystem of one or more classes. Suppose you have a complex concept that requires multiple objects to represent. Making changes to that set of objects is confusing, because you don't always know which object has the method you need to call. That's the time to write a Facade that provides high-level methods for all the complex operations you can do to the collection of objects. Example: a Domain Model for a school section, with methods like countStudents(), reportAttendance(), assignSubstituteTeacher(), and so on.

Most of the information in this answer is from https://sourcemaking.com/design_patterns, which I recommend as an excellent resource for design patterns.




回答12:


I believe code will give a clear ideas (to complement others answers as well). Please see below, (Focus the types that a class implements and wraps)

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:


Design pattern is not mathematics, it is combination of art and software engineering. There is nothing like for this requirment you have to use proxy, bridge etc. Design patterns are created to solve the problems. If you anticipate a design problem, then use it. Based on experience, you will come to know for specific problem, which pattern to use. If you are good in solid design principles, you would have implemented design pattern without knowing it is pattern. Common example is statergy and factory patterns

Hence concentrate more on solid desighn principles, clean coding principles and ttd



来源:https://stackoverflow.com/questions/350404/how-do-the-proxy-decorator-adapter-and-bridge-patterns-differ

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!