
En la programación orientada a objetos , una fábrica es un objeto para crear otros objetos ; formalmente, es una función o método que devuelve objetos de un prototipo o clase variable [ 1 ] a partir de una llamada a un método, que se supone que es nuevo . [ a ] De forma más amplia, una subrutina que devuelve un nuevo objeto puede denominarse fábrica , como en método de fábrica o función de fábrica . El patrón de fábrica es la base de varios patrones de diseño de software relacionados .
Motivo
En la programación basada en clases , una fábrica es una abstracción del constructor de una clase, mientras que en la programación basada en prototipos, una fábrica es una abstracción de un objeto prototipo. Un constructor es concreto porque crea objetos como instancias de una clase, mediante un proceso específico (instanciación de clase), mientras que una fábrica puede crear objetos instanciando varias clases o utilizando otros medios de asignación, como un pool de objetos . Un objeto prototipo es concreto porque se utiliza para crear objetos mediante clonación , mientras que una fábrica puede crear objetos clonando varios prototipos o mediante otros medios de asignación.
Una fábrica puede implementarse de diversas maneras. Lo más frecuente es que se implemente como un método, en cuyo caso se denomina método de fábrica . A veces se implementa como una función, en cuyo caso se denomina función de fábrica . En algunos lenguajes, los constructores son fábricas. Sin embargo, en la mayoría de los lenguajes no lo son, y los constructores se invocan de una manera idiomática del lenguaje, como mediante la palabra clave `const` new, mientras que una fábrica no tiene un estatus especial y se invoca mediante una llamada a método o función ordinaria. En estos lenguajes, una fábrica es una abstracción de un constructor, pero no estrictamente una generalización, ya que los constructores no son fábricas.
Terminología
La terminología difiere en cuanto a si el concepto de fábrica es un patrón de diseño: en Patrones de Diseño no hay un patrón de fábrica , sino dos patrones ( patrón de método de fábrica y patrón de fábrica abstracta ) que utilizan fábricas. Algunas fuentes se refieren al concepto como el patrón de fábrica , [ 2 ] [ 3 ] mientras que otras lo consideran un modismo de programación , [ 4 ] reservando el término patrón de fábrica o patrones de fábrica para patrones más complejos que utilizan fábricas, con mayor frecuencia el patrón de método de fábrica; en este contexto, el concepto de fábrica puede denominarse fábrica simple . [ 4 ] En otros contextos, particularmente en el lenguaje Python , se utiliza el término fábrica , como en este artículo. [ 5 ] De manera más amplia, fábrica puede aplicarse no solo a un objeto que devuelve objetos de alguna llamada a método, sino a una subrutina que devuelve objetos, como en una función de fábrica (incluso si las funciones no son objetos) o método de fábrica . [ 6 ] Debido a que en muchos lenguajes las fábricas se invocan llamando a un método, el concepto general de una fábrica a menudo se confunde con el patrón de diseño específico del patrón de método de fábrica .
Usar
La POO proporciona polimorfismo en el uso de objetos mediante el despacho de métodos , formalmente polimorfismo de subtipos a través de un único despacho determinado por el tipo del objeto sobre el que se llama al método. Sin embargo, esto no funciona para los constructores, ya que los constructores crean un objeto de algún tipo, en lugar de usar un objeto existente. Más concretamente, cuando se llama a un constructor, aún no hay ningún objeto sobre el que realizar el despacho. [ b ]
El uso de fábricas en lugar de constructores o prototipos permite emplear el polimorfismo para la creación de objetos, no solo para su uso. En concreto, las fábricas proporcionan encapsulación , lo que significa que el código no está vinculado a clases u objetos específicos. Por lo tanto, la jerarquía de clases o los prototipos pueden modificarse o refactorizarse sin necesidad de cambiar el código que los utiliza, ya que abstraen la jerarquía de clases o los prototipos.
Más técnicamente, en lenguajes donde las fábricas generalizan los constructores, las fábricas generalmente se pueden usar donde se pueden usar los constructores, [ c ] lo que significa que las interfaces que aceptan un constructor también pueden, en general, aceptar una fábrica; por lo general, solo se necesita algo que cree un objeto, en lugar de tener que especificar una clase y una instanciación.
Por ejemplo, en Python, la collections.defaultdictclase [ 7 ] tiene un constructor que crea un objeto de tipo defaultdict[ d ] cuyos valores predeterminados se generan al invocar una fábrica. La fábrica se pasa como argumento al constructor y puede ser un constructor o cualquier cosa que se comporte como un constructor: un objeto invocable que devuelve un objeto, es decir, una fábrica. Por ejemplo, usando el listconstructor para listas:
from collections import defaultdict# colecciones.defaultdict([default_factory[, ...]]) d : defaultdict [ str , list [ int ]] = defaultdict ( list )Creación de objetos
Los objetos de fábrica se utilizan en situaciones donde obtener un objeto de un tipo específico es un proceso más complejo que simplemente crear uno nuevo, especialmente si se requiere una asignación o inicialización compleja. Algunos de los procesos necesarios para la creación de un objeto incluyen determinar qué objeto crear, gestionar su ciclo de vida y gestionar aspectos específicos de su creación y eliminación. El objeto de fábrica puede decidir crear la clase del objeto (si corresponde) dinámicamente, devolverla desde un pool de objetos , realizar una configuración compleja en el objeto, entre otras cosas. De manera similar, según esta definición, un singleton implementado por el patrón Singleton es una fábrica formal: devuelve un objeto, pero no crea nuevos objetos más allá de la instancia existente.
Por ejemplo, en C++ , las clases de punteros inteligentesstd::unique_ptr<T> y std::shared_ptr<T>tienen funciones de fábrica std::make_unique<T>()y std::make_shared<T>(), que pasan los parámetros a la construcción.
Ejemplos
El ejemplo más sencillo de una fábrica es una función de fábrica simple, que simplemente invoca un constructor y devuelve el resultado. En Python , una función de fábrica fque instancia una clase Ase puede implementar de la siguiente manera:
def f () -> A : return A ()Una función de fábrica simple que implementa el patrón singleton es:
def f () -> A : if f . obj is None : f . obj = A () return f . objf . obj = NingunoEsto creará un objeto la primera vez que se llame, y siempre devolverá el mismo objeto a partir de entonces.
Sintaxis
Las fábricas pueden invocarse de diversas maneras, generalmente mediante una llamada a un método (un método de fábrica ), y a veces llamándolas como una función si la fábrica es un objeto invocable (una función de fábrica ). En algunos lenguajes, los constructores y las fábricas tienen una sintaxis idéntica, mientras que en otros , los constructores tienen una sintaxis especial. En lenguajes donde los constructores y las fábricas tienen una sintaxis idéntica, como Python, Perl , Ruby , Object Pascal y F # , los constructores pueden reemplazarse de forma transparente por fábricas. En lenguajes donde difieren, es necesario distinguirlos en las interfaces, y cambiar entre constructores y fábricas requiere modificar las llamadas.
Semántica
En lenguajes donde los objetos se asignan dinámicamente , como en Java o Python, las fábricas son semánticamente equivalentes a los constructores. Sin embargo, en lenguajes como C++ que permiten la asignación estática de algunos objetos , las fábricas difieren de los constructores para las clases con asignación estática, ya que la asignación de memoria de estos últimos se determina en tiempo de compilación , mientras que la asignación de los valores de retorno de las fábricas debe determinarse en tiempo de ejecución. Si un constructor puede pasarse como argumento a una función, entonces la invocación del constructor y la asignación del valor de retorno deben realizarse dinámicamente en tiempo de ejecución, por lo que tienen una semántica similar o idéntica a la de invocar una fábrica.
Patrones de diseño
Las fábricas se utilizan en diversos patrones de diseño , especialmente en patrones de creación como la biblioteca de objetos de patrones de diseño (DOML) . Se han desarrollado métodos específicos para implementarlas en muchos lenguajes. Por ejemplo, varios patrones de GoF , como el patrón de método de fábrica , el patrón constructor o incluso el patrón singleton , son implementaciones de este concepto. El patrón de fábrica abstracta, en cambio, es un método para construir colecciones de fábricas.
En algunos patrones de diseño, un objeto de fábrica tiene un método para cada tipo de objeto que puede crear. Estos métodos aceptan opcionalmente parámetros que definen cómo se crea el objeto y, a continuación, devuelven el objeto creado.
Aplicaciones
Los objetos de fábrica son comunes en los kits de herramientas de widgets y en los marcos de software donde el código de la biblioteca necesita crear objetos de tipos que pueden ser subclaseados por las aplicaciones que utilizan el marco. También se utilizan en el desarrollo guiado por pruebas para permitir que las clases se sometan a pruebas. [ 8 ]
Las fábricas determinan el tipo concreto del objeto que se va a crear, y es aquí donde se crea dicho objeto. Dado que la fábrica solo devuelve una interfaz abstracta del objeto, el código cliente desconoce, y no tiene que preocuparse por, el tipo concreto del objeto que acaba de crear. Sin embargo, la fábrica abstracta sí conoce el tipo de un objeto concreto. En concreto, esto significa:
- El código del cliente desconoce por completo el tipo de dato concreto , por lo que no necesita incluir archivos de cabecera ni declaraciones de clase relacionadas con dicho tipo. El código del cliente solo trabaja con el tipo abstracto. Si bien la fábrica crea objetos de un tipo concreto, el código del cliente accede a ellos únicamente a través de su interfaz abstracta .
- Agregar nuevos tipos concretos se realiza modificando el código del cliente para usar una fábrica diferente, una modificación que generalmente consiste en una sola línea en un solo archivo. Esto es mucho más sencillo que modificar el código del cliente para instanciar un nuevo tipo, lo que requeriría cambiar cada ubicación del código donde se crea un nuevo objeto.
Aplicabilidad
Las fábricas pueden utilizarse cuando:
- La creación de un objeto hace imposible la reutilización sin una duplicación significativa del código.
- La creación de un objeto requiere acceso a información o recursos que no deberían estar contenidos dentro de la clase que lo compone.
- La gestión del ciclo de vida de los objetos generados debe estar centralizada para garantizar un comportamiento coherente dentro de una aplicación.
Las fábricas, específicamente los métodos de fábrica, son comunes en los kits de herramientas de widgets y los marcos de software , donde el código de la biblioteca necesita crear objetos de tipos que pueden ser subclaseados por aplicaciones que utilizan el marco.
Las jerarquías de clases paralelas a menudo requieren que los objetos de una jerarquía puedan crear los objetos apropiados de otra.
Los métodos de fábrica se utilizan en el desarrollo guiado por pruebas para permitir que las clases se sometan a pruebas. [ 9 ] Si una clase Foocrea otro objeto Dangerousque no se puede someter a pruebas unitarias automatizadas (quizás se comunica con una base de datos de producción que no siempre está disponible), entonces la creación de Dangerousobjetos se coloca en el método de fábrica virtualcreateDangerous en la clase Foo. Para las pruebas, se crea TestFoo(una subclase de ), con el método de fábrica virtual sobrescrito para crear y devolver , un objeto ficticio . Las pruebas unitarias utilizan para probar la funcionalidad de sin incurrir en el efecto secundario de usar un objeto real.FoocreateDangerousFakeDangerousTestFooFooDangerous
Beneficios y variantes
Además de su uso en patrones de diseño, las fábricas, especialmente los métodos de producción, ofrecen diversas ventajas y variaciones.
Nombres descriptivos
Un método de fábrica tiene un nombre distintivo. En muchos lenguajes orientados a objetos, los constructores deben tener el mismo nombre que la clase a la que pertenecen, lo que puede generar ambigüedad si hay más de una forma de crear un objeto (véase sobrecarga ). Los métodos de fábrica no tienen esta restricción y pueden tener nombres descriptivos; a veces se les conoce como constructores alternativos . Por ejemplo, cuando se crean números complejos a partir de dos números reales, estos últimos pueden interpretarse como coordenadas cartesianas o polares, pero al usar métodos de fábrica, el significado es claro, como se ilustra en el siguiente ejemplo en C# .
espacio de nombres Wikipedia.Ejemplos ;usando el sistema ;public class Complex { public double Real { get ; set ; } public double Imaginary { get ; set ; }privado Complejo ( doble real , doble imaginario ) { este . Real = real ; este . Imaginario = imaginario ; }public static Complex FromCartesian ( double real , double imaginary ) { return new Complex ( real , imaginary ); }public static Complex FromPolar ( double modulus , double angle ) { return new Complex ( modulus * Math . Cos ( angle ), modulus * Math . Sin ( angle )); } }Producto complejo = Complejo.FromPolar ( 1 , Math.PI ) ;Cuando se utilizan métodos de fábrica para la desambiguación de este tipo, los constructores originales a menudo se hacen privados para obligar a los clientes a utilizar los métodos de fábrica.
Encapsulación
Los métodos de fábrica encapsulan la creación de objetos. Esto puede ser útil si el proceso de creación es muy complejo; por ejemplo, si depende de la configuración en archivos de configuración o de la entrada del usuario.
Consideremos como ejemplo un programa que lee archivos de imagen . El programa admite diferentes formatos de imagen, representados por una clase lectora para cada formato.
Cada vez que el programa lee una imagen, necesita crear un lector del tipo adecuado según la información contenida en el archivo. Esta lógica se puede encapsular en un método de fábrica. Este enfoque también se conoce como Fábrica Simple.
Java
paquete org.wikipedia.imageio ;import javax.imageio.ImageReader ;public class ImageReaderFactory { public static ImageReader createImageReader ( ImageInputStreamProcessor iisp ) { if ( iisp . isGIF ()) { return new GifReader ( iisp . getInputStream ()); } else if ( iisp . isJPEG ()) { return new JpegReader ( iisp . getInputStream ()); } throw new IllegalArgumentException ( "Tipo de imagen desconocido." ); } }PHP
clase Factory { función estática pública build ( cadena $type ) : FormatInterface { $class = "Formato" . $type ; return new $class ; } }interfaz FormatInterface {}La clase FormatString implementa FormatInterface {} La clase FormatNumber implementa FormatInterface {}try { $string = Factory :: build ( "String" ); } catch ( Error $e ) { echo $e -> getMessage (); }try { $number = Factory :: build ( "Number" ); } catch ( Error $e ) { echo $e -> getMessage (); }Límites
Existen tres limitaciones asociadas al uso del método de fábrica. La primera implica la refactorización del código existente; las otras dos implican la extensión de una clase.
- La primera limitación es que refactorizar una clase existente para usar fábricas rompe los clientes existentes. Por ejemplo, si la clase Complex fuera una clase estándar, podría tener muchos clientes con código como el que se da cuenta de que se necesitan dos fábricas diferentes, la clase se modifica (al código mostrado anteriormente ). Pero como el constructor ahora es privado, el código del cliente existente ya no compila.
Complexc=newComplex(-1,0); - La segunda limitación es que, dado que el patrón se basa en el uso de un constructor privado, la clase no puede extenderse. Cualquier subclase debe invocar el constructor heredado, pero esto no es posible si dicho constructor es privado.
- El tercer límite es que, si se extendiera la clase (por ejemplo, protegiendo el constructor —lo cual es arriesgado pero factible—), la subclase debe proporcionar su propia reimplementación de todos los métodos de fábrica con firmas idénticas. Por ejemplo, si la clase
StrangeComplexextiendeComplex, a menos queStrangeComplexproporcione su propia versión de todos los métodos de fábrica, la llamada devolverá una instancia de (la superclase) en lugar de la instancia esperada de la subclase. Las características de programación reflexiva (reflexión) de algunos lenguajes pueden evitar este problema.StrangeComplex.FromPolar(1,Math.Pi);Complex
Los tres problemas podrían mitigarse modificando el lenguaje de programación subyacente para que las fábricas sean miembros de primera clase (véase también Clase virtual ). [ 10 ]
Notas
- ↑ En cuanto a la interfaz, cualquier objeto que devuelva un objeto puede usarse como una fábrica, pero semánticamente una fábrica devuelve un objeto recién creado, como una instancia de clase o una copia de un prototipo, o un objeto que parece nuevo, como un objeto reinicializado de un grupo de objetos.
- ↑ En los lenguajes donde los constructores son métodos de un objeto de clase ( métodos de clase ), existe un objeto preexistente, y los constructores son casos especiales de métodos de fábrica, siendo la creación polimórfica un caso especial de despacho de métodos polimórficos. En otros lenguajes, existe una clara distinción entre constructores y métodos.
- ↑ Los constructores se pueden usar en cualquier lugar donde se puedan usar fábricas, ya que son un caso especial.
- ↑ Esta clase es una subclase de
dict, la implementación integrada de Python para asignaciones o diccionarios. - ↑ Si se omitela palabra clave opcional
new
Referencias
- ↑ Gamma, Erich (1994). Patrones de diseño . Addison-Wesley. págs. 18–19 . ISBN 9780321700698.
- ↑ Patrón de fábrica , OODesign.com
- ↑ Patrón de fábrica , WikiWikiWeb
- 1 2 Capítulo 4. El patrón de fábrica: Horneando con las bondades de OO Archivado el 11/03/2017 en Wayback Machine : La fábrica simple definida Archivado el 19/02/2014 en Wayback Machine
- ↑ 30.8 Las clases son objetos: Fábricas de objetos genéricos , Aprendiendo Python, por Mark Lutz, 4.ª edición, O'Reilly Media, Inc., ISBN 978-0-596-15806-4
- ↑ Método de fábrica , WikiWikiWeb
- ↑ objetos defaultdict
- ↑ Feathers, Michael (octubre de 2004). Working Effectively with Legacy Code . Upper Saddle River, Nueva Jersey: Prentice Hall Professional Technical Reference. ISBN 978-0-13-117705-5.
- ↑ Feathers, Michael (octubre de 2004). Trabajar eficazmente con código heredado . Upper Saddle River, NJ: Prentice Hall Professional Technical Reference. ISBN 978-0-13-117705-5.
- ↑ Agerbo, Ellen; Cornils, Aino (1998). «Cómo preservar los beneficios de los patrones de diseño». Conferencia sobre sistemas, lenguajes y aplicaciones de programación orientada a objetos . Vancouver, Columbia Británica, Canadá: ACM: 134–143 . ISBN 1-58113-005-8.
- Eric, Freeman; Robson, Elisabeth; Bates, Bert; Sierra, Kathy (2009) [2004]. Patrones de diseño Head First . Head First . O'Reilly. ISBN 978-0-596-55656-3.
- Programación orientada a objetos
- Comparación de lenguajes de programación
