ChatGPT解决这个技术问题 Extra ChatGPT

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

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?

There are often patterns that look very similar, but differ in their intent (the strategy and state patterns come to mind). I think this is often due to the fact that design patterns are based on common solid design principles.
Well, these four patterns have the exact same implementation details. State verses Strategy can at least be summed up as state-full verses stateless (for the most part). Often, the Strategy is just method injection, where the state pattern uses an interface to do more then abstract away a method call. The strategy, also, at the end of the day, is a hack to allow functional programming in the OO world.

B
Bill Karwin

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.


Good answer. May be worth adding some examples of where you see it in the wild? e.g Proxy classes in Web Services. +1 from me.
@Rob: thanks, but I'd rather keep this answer short and sweet. I encourage you to write another answer with examples in the wild!
@RobertDailey Decorator is also good to avoid out of control type hierarchies. For example, say you have a window in a GUI and you want to have optional scroll bars. You could have Window, VScrollWindow, HScrollWindow, and VHScrollWindow classes or you could make VScroll and HScroll decorators on Window.
@RobertDailey, Decorator is composition.
And what if you want to duplicate the interface of the wrapped object 1:1 but then add a few additional methods? Is this a decorator or an adapter?
C
Community

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.


Your answer combined with Bill's wraps up 5 chapters of Design Patterns very nicely. One could call them a higher-level (read: simpler) interface to the book.
I would say that the Adapter usually depends on one interface (needed to be adapted to another - like what you say about deriving), but it may still create (introduce) a new interface (adapted from the dependency interface). The difference between Adapter and Facade is about the number of dependencies, Facade usually consumes a lot of miscellaneous interfaces (not just usually one like Adapter) and combines them to spit out a common interface which serves some purpose.
A
Afshin Moazami

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.


D
Dinah

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!


R
Ravindra babu

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

I will decorate key points.

Decorator:

Add behaviour to object at run time. Inheritance is the key to achieve this functionality, which is both advantage and disadvantage of this pattern. 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:

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. Unlike Decorator, which allows chaining of objects, Proxy does not allow chaining.

e.g.: java.rmi package classes.

Adapter:

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

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

Bridge:

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

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

Key notes:

Adapter provides a different interface to its subject. Proxy provides the same interface. Decorator provides an enhanced interface. Adapter changes an object's interface, Decorator enhances an object's responsibilities. Decorator and Proxy have different purposes but similar structures Adapter makes things work after they're designed; Bridge makes them work before they are. Bridge is designed up-front to let the abstraction and the implementation vary independently. Adapter is retrofitted to make unrelated classes work together 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


Sorry did not get what you meant. Regarding decorator you say, 'Inheritance is the key to achieve this functionality, which is both advantage and disadvantage of this pattern'. And at the same time 'Decorator is designed to let you add responsibilities to objects without subclassing'. These two contradict each other in my opinion
C
Cristian Ciupitu

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.


T
Teoman shipahi

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."


A
Alexey

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.


h
hmcclungiii

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).


Wouldn't that just be the Adaptor Pattern?
A web service is consumed by a Proxy, whereas the Adapter Pattern is used more for the conversion or translation of data from one form to another.
b
bnguyen82

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.


C
Community

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.


T
Tunaki

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);
                }

            }
        }
    }

}

M
Mahantesh

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


Agree, though it's not replying the question.