¿Cómo difieren los patrones de proxy, decorador, adaptador y puente?


403

Estaba mirando el Patrón Proxy, y para mí parece mucho como los patrones Decorador, Adaptador y Puente. ¿Estoy malinterpretando algo? ¿Cual es la diferencia? ¿Por qué usaría el patrón Proxy en comparación con los demás? ¿Cómo los has usado en el pasado en proyectos del mundo real?


44
A menudo hay patrones que se parecen mucho, pero difieren en su intención (me vienen a la mente los patrones de estrategia y estado). Creo que esto a menudo se debe al hecho de que los patrones de diseño se basan en principios comunes de diseño sólido.
Jason Down

55
Bueno, estos cuatro patrones tienen exactamente los mismos detalles de implementación. Versos de estado La estrategia se puede resumir al menos como versos llenos de estado sin estado (en su mayor parte). A menudo, la Estrategia es solo una inyección de método, donde el patrón de estado usa una interfaz para hacer más que abstraer una llamada de método. La estrategia, también, al final del día, es un truco para permitir la programación funcional en el mundo OO.
Charles Graham el

Respuestas:


648

Proxy, Decorator, Adapter y Bridge son todas variaciones en "envolver" una clase. Pero sus usos son diferentes.

  • El proxy se puede usar cuando desea instanciar un objeto de forma diferida, ocultar el hecho de que está llamando a un servicio remoto o controlar el acceso al objeto.

  • Decorator también se llama "Smart Proxy". Esto se usa cuando desea agregar funcionalidad a un objeto, pero no extendiendo el tipo de ese objeto. Esto le permite hacerlo en tiempo de ejecución.

  • El adaptador se utiliza cuando tiene una interfaz abstracta y desea asignar esa interfaz a otro objeto que tenga una función funcional similar, pero una interfaz diferente.

  • Puente es muy similar a Adapter, pero lo llamamos Bridge cuando define tanto la interfaz abstracta como la implementación subyacente. Es decir, no se está adaptando a algún código heredado o de terceros, es el diseñador de todo el código pero necesita poder intercambiar diferentes implementaciones.

  • Facade es una interfaz de nivel superior (léase: más simple) a un subsistema de una o más clases. Suponga que tiene un concepto complejo que requiere múltiples objetos para representar. Hacer cambios en ese conjunto de objetos es confuso, porque no siempre sabes qué objeto tiene el método al que debes llamar. Es el momento de escribir una Fachada que proporcione métodos de alto nivel para todas las operaciones complejas que puede realizar en la colección de objetos. Ejemplo: un modelo de dominio para una sección de la escuela, con métodos como countStudents(), reportAttendance(), assignSubstituteTeacher(), y así sucesivamente.


77
Buena respuesta. ¿Puede valer la pena agregar algunos ejemplos de dónde lo ves en la naturaleza? Por ejemplo, clases de proxy en servicios web. +1 de mi parte
Rob Cooper

55
@Rob: gracias, pero prefiero que esta respuesta sea breve y dulce. ¡Te animo a que escribas otra respuesta con ejemplos en la naturaleza!
Bill Karwin

8
@RobertDailey Decorator también es bueno para evitar jerarquías de tipo fuera de control. Por ejemplo , supongamos que tiene una ventana en una GUI y desea tener barras de desplazamiento opcionales. Puede tener clases Window, VScrollWindow, HScrollWindow y VHScrollWindow o puede hacer decoradores VScroll y HScroll en Window.
Eva

1
@RobertDailey, Decorator es composición.
Bill Karwin

1
¿Y qué pasa si desea duplicar la interfaz del objeto envuelto 1: 1 pero luego agrega algunos métodos adicionales? ¿Es este un decorador o un adaptador?
donquixote

198

Como dice la respuesta de Bill, sus casos de uso son diferentes .

Así son sus estructuras.

  • Proxy y Decorator tienen la misma interfaz que sus tipos envueltos, pero el proxy crea una instancia bajo el capó, mientras que el decorador toma una instancia en el constructor.

  • Adaptador y Fachada tienen una interfaz diferente de la que envuelven. Pero el adaptador deriva de una interfaz existente, mientras que la fachada crea una nueva interfaz.

  • El puente y el adaptador apuntan a un tipo existente. Pero el puente apuntará a un tipo abstracto, y el adaptador podría apuntar a un tipo concreto. El puente le permitirá emparejar la implementación en tiempo de ejecución, mientras que el adaptador generalmente no lo hará.


30
Su respuesta combinada con Bill resume muy bien 5 capítulos de Patrones de diseño. Uno podría llamarlos una interfaz de nivel superior (léase: más simple) para el libro.
Jonas Eicher

54

Mi opinión sobre el tema.

Los cuatro patrones tienen mucho en común, los cuatro a veces se denominan informalmente envoltorios o patrones de envoltorio. Todos usan la composición, ajustan el tema y delegan la ejecución al tema en algún momento, mapean una llamada de método a otra. Le ahorran al cliente la necesidad de tener que construir un objeto diferente y copiar todos los datos relevantes. Si se usan sabiamente, ahorran memoria y procesador.

Al promover un acoplamiento flexible, hacen que el código una vez estable esté menos expuesto a cambios inevitables y sea mejor legible para otros desarrolladores.

Adaptador

El adaptador adapta el sujeto (adaptado) a una interfaz diferente. De esta manera, podemos agregar objetos a una colección de tipos nominalmente diferentes.

El adaptador expone solo los métodos relevantes al cliente, puede restringir todos los demás, revelando intentos de uso para contextos particulares, como adaptar la biblioteca externa, hacer que parezca menos general y más centrado en las necesidades de nuestra aplicación. Los adaptadores aumentan la legibilidad y la autodescripción de nuestro código.

Los adaptadores protegen a un equipo del código volátil de otros equipos; una herramienta de salvamento cuando se trata de equipos offshore ;-)

El propósito menos mencionado es evitar que la clase de asignaturas exceda las anotaciones. Con tantos marcos basados ​​en anotaciones, este uso se vuelve más importante que nunca.

El adaptador ayuda a evitar la limitación de Java de una sola herencia. Puede combinar varios adaptadores en un sobre dando la impresión de herencia múltiple.

Código sabio, el adaptador es "delgado". No debe agregar mucho código a la clase adaptee, además de simplemente llamar al método adaptee y las conversiones de datos ocasionales necesarias para realizar tales llamadas.

No hay muchos buenos ejemplos de adaptadores en JDK o bibliotecas básicas. Los desarrolladores de aplicaciones crean adaptadores para adaptar bibliotecas a interfaces específicas de aplicaciones.

Decorador

Decorator no solo delega, no solo asigna un método a otro, hacen más, modifican el comportamiento de algunos métodos de sujeto, puede decidir no llamar al método de sujeto en absoluto, delegar a un objeto diferente, un objeto auxiliar.

Los decoradores suelen agregar funcionalidad (transparente) al objeto envuelto, como el registro, el cifrado, el formateo o la compresión del sujeto. Esta nueva funcionalidad puede traer mucho código nuevo. Por lo tanto, los decoradores suelen ser mucho más "gordos" que los adaptadores.

El decorador debe ser una subclase de la interfaz del sujeto. Se pueden usar de forma transparente en lugar de sus sujetos. Vea BufferedOutputStream, todavía es OutputStream y puede usarse como tal. Esa es una gran diferencia técnica de los adaptadores.

Los ejemplos de libros de texto de toda la familia de decoradores se encuentran fácilmente en JDK: Java IO. Todas las clases como BufferedOutputStream , FilterOutputStream y ObjectOutputStream son decoradores de OutputStream . Pueden ser capas de cebolla, donde un decorador se decora nuevamente, agregando más funcionalidad.

Apoderado

El proxy no es un contenedor típico. Es posible que el objeto envuelto, el sujeto proxy, aún no exista en el momento de la creación del proxy. Proxy a menudo lo crea internamente. Puede ser un objeto pesado creado a pedido, o es un objeto remoto en una JVM diferente o un nodo de red diferente e incluso un objeto no Java, un componente en el código nativo. No tiene que envolver o delegar necesariamente a otro objeto en absoluto.

Los ejemplos más típicos son proxies remotos, inicializadores de objetos pesados ​​y proxies de acceso.

  • Proxy remoto: el sujeto está en un servidor remoto, una JVM diferente o incluso un sistema que no es Java. Proxy traduce llamadas de método a llamadas RMI / REST / SOAP o lo que sea necesario, protegiendo al cliente de la exposición a la tecnología subyacente.

  • Proxy de carga diferida: inicialice completamente el objeto solo el primer uso o el primer uso intensivo.

  • Proxy de acceso: controla el acceso al sujeto.

Fachada

La fachada está estrechamente asociada con el Principio de diseño del conocimiento mínimo (Ley de Demeter). Facade es muy similar al adaptador. Ambos se envuelven, ambos asignan un objeto a otro, pero difieren en la intención. Fachada aplana la estructura compleja de un sujeto, gráfico de objetos complejos, simplificando el acceso a una estructura compleja.

Fachada envuelve una estructura compleja, proporcionándole una interfaz plana. Esto evita que el objeto del cliente se exponga a relaciones internas en la estructura del sujeto, promoviendo así un acoplamiento flojo.

Puente

Variante más compleja del patrón Adaptador donde no solo la implementación varía sino también la abstracción. Agrega una indirección más a la delegación. La delegación extra es el puente. Desacopla el adaptador incluso de la interfaz de adaptación. Aumenta la complejidad más que cualquier otro de los otros patrones de envoltura, así que aplique con cuidado.

Diferencias en constructores

Las diferencias de patrones también son obvias cuando se observan sus constructores.

  • Proxy no está envolviendo un objeto existente. No hay asignatura en constructor.

  • Decorator and Adapter sí envuelve el objeto ya existente, y normalmente se
    proporciona en el constructor.

  • El constructor de fachadas toma el elemento raíz de un gráfico de objeto completo, de lo contrario, se ve igual que Adaptador.

Ejemplo de la vida real: JAXB Marshalling Adapter . El propósito de este adaptador es el mapeo de una clase plana simple a una estructura más compleja requerida externamente y para evitar la clase de sujeto "contaminante" con anotaciones excesivas.


30

Hay una gran superposición en muchos de los patrones de GoF. Todos están construidos sobre el poder del polimorfismo y, a veces, solo difieren en su intención. (estrategia versus estado)

Mi comprensión de los patrones aumentó 100 veces después de leer Head First Design Patterns .

¡Lo recomiendo altamente!


9

Todas las buenas respuestas de los expertos ya han explicado qué significa cada patrón.

Voy a decorar los puntos clave.

Decorador:

  1. Agregar comportamiento al objeto en tiempo de ejecución . La herencia es la clave para lograr esta funcionalidad, que es tanto una ventaja como una desventaja de este patrón.
  2. Modifica el comportamiento de la interfaz.

por ejemplo (con encadenamiento): java.ioclases de paquetes relacionados con InputStream& OutputStreaminterfaces

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

Apoderado:

  1. Úselo para la inicialización diferida, la mejora del rendimiento al almacenar en caché el objeto y controlar el acceso al cliente / llamante . Puede proporcionar un comportamiento alternativo o llamar a un objeto real. Durante este proceso, puede crear un nuevo Objeto.
  2. A diferencia de Decorator , que permite el encadenamiento de objetos, Proxy no permite el encadenamiento.

Por ejemplo: java.rmiclases de paquetes.

Adaptador:

  1. Permite que dos interfaces no relacionadas trabajen juntas a través de los diferentes objetos. , posiblemente jugando el mismo rol.
  2. Modifica la interfaz original .

por ejemplo java.io.InputStreamReader( InputStreamdevuelve a Reader)

Puente:

  1. Permite que tanto las abstracciones como las implementaciones varíen independientemente .
  2. Utiliza la composición sobre la herencia .

por ejemplo, clases de colección en java.util. Listimplementado por ArrayList.

Notas clave:

  1. El adaptador proporciona una interfaz diferente a su tema. Proxy proporciona la misma interfaz. Decorator proporciona una interfaz mejorada.
  2. El adaptador cambia la interfaz de un objeto, Decorator mejora las responsabilidades de un objeto.
  3. Decorador y Proxy tienen diferentes propósitos pero estructuras similares.
  4. El adaptador hace que las cosas funcionen después de su diseño El puente los hace trabajar antes que ellos.
  5. Bridge está diseñado por adelantado para permitir que la abstracción y la implementación varíen independientemente. El adaptador se actualiza para que las clases no relacionadas funcionen juntas
  6. Decorator está diseñado para permitirle agregar responsabilidades a los objetos sin subclasificar.

Eche un vistazo a excelentes preguntas / artículos de SE con respecto a ejemplos de varios patrones de diseño

¿Cuándo usar el patrón decorador?

¿Cuándo usas el patrón de puente? ¿Cómo es diferente del patrón del adaptador?

Diferencias entre Proxy y Patrón Decorador


8

Son bastante similares, y las líneas entre ellos son bastante grises. Te sugiero que leas el entradas de Patrón de proxy y Patrón de decorador en la wiki de c2.

Las entradas y discusiones allí son bastante extensas, y también enlazan con otros artículos relevantes. Por cierto, el wiki de c2 es excelente cuando te preguntas sobre los matices entre los diferentes patrones.

Para resumir las entradas de c2, diría que un decorador agrega / cambia el comportamiento, pero un proxy tiene más que ver con el control de acceso (instanciación diferida, acceso remoto, seguridad, etc.). Pero como dije, las líneas entre ellos son grises, y veo referencias a proxies que podrían verse fácilmente como decoradores y viceversa.


4

Los cuatro patrones implican envolver el objeto / clase interno con el externo, por lo que son muy similares estructuralmente. Describiría la diferencia por el propósito:

  • El proxy encapsula el acceso de externo a interno.
  • El decorador modifica o extiende el comportamiento de lo interno con lo externo.
  • El adaptador convierte la interfaz de interna a externa.
  • El puente separa la parte invariable del comportamiento (exterior) de la parte variable o dependiente de la plataforma (interior).

Y por variación de interfaz entre objetos internos y externos:

  • en Proxy las interfaces son iguales.
  • en Decorator las interfaces son iguales.
  • Las interfaces del adaptador son diferentes formalmente, pero cumplen el mismo propósito.
  • En Bridge las interfaces son conceptualmente diferentes.

4

Esta es una cita de Head First Design Patterns

Las definiciones pertenecen al libro. Los ejemplos me pertenecen.

Decorador : no altera la interfaz, pero agrega responsabilidad. Suponga que tiene una interfaz de automóvil, cuando implemente esto para diferentes modelos del automóvil (s, sv, sl), es posible que deba agregar más responsabilidad para algunos modelos. Al igual que tiene techo solar, airbag, etc.

Adaptador : convierte una interfaz a otra. Tiene una interfaz de automóvil y le gustaría que actúe como un jeep. Entonces tomas el auto, lo modificas y te conviertes en un jeep. Ya que no es un verdadero jeep. Pero actúa como un jeep.

Fachada : simplifica la interfaz. Suponga que tiene interfaces para automóviles, aviones y barcos. En realidad, todo lo que necesitas es una clase que envíe personas de un lugar a otro. Desea que la fachada decida qué vehículo usar. Luego recopila todas esas referencias de interfaz bajo 1 paraguas y deja que decida / delegue para que sea simple.

Primero: "Una fachada no solo simplifica una interfaz, sino que desacopla a un cliente de un subsistema de componentes. Las fachadas y los adaptadores pueden envolver varias clases, pero la intención de una fachada es simplificar, mientras que un adaptador es convertir la interfaz en algo diferente. "


1

Lo uso con bastante frecuencia cuando consumo servicios web. El Proxy Pattern probablemente debería renombrarse a algo más pragmático, como 'Wrapper Pattern ". También tengo una biblioteca que es un Proxy para MS Excel. Hace que sea muy fácil automatizar Excel, sin tener que preocuparse por detalles de fondo como La versión está instalada (si la hay).


¿No sería ese el patrón adaptador?
Charles Graham

1
Un servicio web es consumido por un Proxy, mientras que el Patrón Adaptador se usa más para la conversión o traducción de datos de un formulario a otro.
hmcclungiii

1

Hablando de implementación detallada, encuentro una diferencia entre Proxy y Decorador, Adaptador, Fachada ... En la implementación común de estos patrones hay un objeto objetivo envuelto por un objeto envolvente. El cliente utiliza el objeto adjunto en lugar del objeto de destino. Y el objeto de destino realmente juega un papel importante dentro de algunos de los métodos de encerrar el objeto.

Sin embargo, en el caso de Proxy, el objeto que encierra puede reproducir algunos métodos por sí mismo, solo inicializa el objeto de destino cuando el cliente llama a algunos métodos en los que necesita que participe el objeto de destino. Esta es una inicialización diferida. En el caso de otros patrones, el objeto envolvente se basa virtualmente en el objeto objetivo. Por lo tanto, el objeto de destino siempre se inicializa junto con el objeto delimitador en constructores / establecedores.

Otra cosa, un proxy hace exactamente lo que hace un objetivo, mientras que otros patrones agregan más funcionalidad al objetivo.


1

Me gustaría agregar ejemplos a la respuesta de Bill Karwing (lo cual es genial por cierto). Agrego también algunas diferencias clave de implementación, que siento que faltan

Las partes citadas son de la respuesta de [ https://stackoverflow.com/a/350471/1984346] (Bill Karwing)

Proxy, Decorator, Adapter y Bridge son todas variaciones en "envolver" una clase. Pero sus usos son diferentes.

  • El proxy se puede usar cuando desea instanciar un objeto de forma diferida, ocultar el hecho de que está llamando a un servicio remoto o controlar el acceso al objeto.

ProxyClass y ObjectClass que son proxy, deben implementar la misma interfaz, por lo que son intercambiables

Ejemplo: objeto costoso proxy

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 también se llama "Smart Proxy". Esto se usa cuando desea agregar funcionalidad a un objeto, pero no extendiendo el tipo de ese objeto. Esto le permite hacerlo en tiempo de ejecución.

DecoratorClass debería (podría) implementar la interfaz extendida de ObjectClass. Entonces ObjectClass podría ser reemplazado por DecoratorClass, pero no al revés.

Ejemplo: agregar funcionalidad adicional

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 { ... }
  • El adaptador se utiliza cuando tiene una interfaz abstracta y desea asignar esa interfaz a otro objeto que tenga una función funcional similar, pero una interfaz diferente.

Diferencias de implementación Proxy, Decorador, Adaptador

El adaptador proporciona una interfaz diferente a su tema. Proxy proporciona la misma interfaz. Decorator proporciona una interfaz mejorada.

  • Bridge es muy similar a Adapter, pero lo llamamos Bridge cuando define tanto la interfaz abstracta como la implementación subyacente. Es decir, no se está adaptando a algún código heredado o de terceros, es el diseñador de todo el código pero necesita poder intercambiar diferentes implementaciones.

  • Facade es una interfaz de nivel superior (léase: más simple) a un subsistema de una o más clases. Suponga que tiene un concepto complejo que requiere múltiples objetos para representar. Hacer cambios en ese conjunto de objetos es confuso, porque no siempre sabes qué objeto tiene el método al que debes llamar. Es el momento de escribir una Fachada que proporcione métodos de alto nivel para todas las operaciones complejas que puede realizar en la colección de objetos. Ejemplo: un modelo de dominio para una sección de la escuela, con métodos como countStudents(), reportAttendance(), assignSubstituteTeacher(), y así sucesivamente.

La mayor parte de la información en esta respuesta proviene de https://sourcemaking.com/design_patterns , que recomiendo como un excelente recurso para patrones de diseño.


0

Creo que el código dará ideas claras (para complementar las respuestas de otros también). Vea a continuación, (Enfoque los tipos que una clase implementa y ajusta)

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

            }
        }
    }

}

-3

El patrón de diseño no es matemático, es una combinación de arte e ingeniería de software. No hay nada como para este requisito que tiene que usar proxy, puente, etc. Se crean patrones de diseño para resolver los problemas. Si anticipa un problema de diseño, úselo. Según la experiencia, conocerá el problema específico, qué patrón utilizar. Si eres bueno en principios de diseño sólidos, habrías implementado un patrón de diseño sin saber que es un patrón. Un ejemplo común son los patrones de fábrica y de fábrica.

Por lo tanto, concéntrese más en principios sólidos de diseño, principios de codificación limpios y ttd


De acuerdo, aunque no responde la pregunta.
Leon
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.