¿El patrón de fábrica abstracta escala?

8

Todavía estoy tratando de entender los patrones de diseño aquí, después de aprender el patrón abstracto de fábrica, me di cuenta de que este patrón no escalará bien. Echa un vistazo al diagrama uml del patrón abstracto de fábrica

diagrama uml

Si tengo que crear un nuevo 'AbstractProductC', tendré que agregar un método abstracto 'CreateProductC' en 'AbstractFactory' que afecta la implementación de ConcreateFactory1, ConcreateFactory2.

Mi pregunta aquí es, ¿el patrón de Abstract Factory se escala en absoluto (o) estoy pensando en la dirección incorrecta aquí?

gracias por adelantado

Jitendar
fuente
Creo que el diagrama que presentó, que parece relativamente estándar, se ha simplificado para ilustrar el patrón en cuestión. Sin embargo, si necesita algo más complejo, siempre puede hacer que AbstractFactory defina solo un método: CreateProduct (productType) y luego tener una fábrica dentro de una fábrica que observe el tipo y cree una instancia de clase concreta en función de eso.
DXM
@DXM Eso no soluciona el problema. Aún necesitaría cambiar la implementación de fábricas concretas cada vez que agregue un nuevo producto.
Eufórico el
Bueno, lo único que podría escalar es una interfaz abstracta real, pero sí, si tienes N productos y M formas de crearlos, no importa cómo lo cortes, necesitarás implementaciones N x M, a menos que vayas con una estrategia completamente diferente .
DXM
@Eufórico: no necesariamente. Podría simplemente agregar una clase de fábrica y registrarla en la fábrica abstracta. Puede parecer un poco quisquilloso, pero para escalar y SRP encuentro que agregar un poco menos problemático que cambiar.
Marjan Venema
@MarjanVenema Ahora la pregunta es si realmente es una fábrica y no un patrón diferente.
Eufórico el

Respuestas:

5

Abstract Factory escala muy bien.

Hay un principio básico que establece que una clase debe hacer una cosa bien. Su problema aquí es que está tratando de hacer que muchas clases hagan muchas cosas.

No tiene que atenerse a una fábrica abstracta. Puede (y debe tener) varios:

AbstractProductAFactorydefine la interfaz para producir ProductA. Sus implementaciones concretas (ConcreteProductAFactory1, ConcreteProductAFactory2) lo ampliarían.

AbstractProductBFactorydefine la interfaz para producir ProductB. Sus implementaciones concretas ( ConcreteProductBFactory1, ConcreteProductBFactory2) lo ampliarían.

Si necesita un ProductC, cree uno nuevo AbstractProductCFactory. No es necesario cambiar ninguna de sus otras fábricas de esta manera.

ACTUALIZACIÓN Idealmente, ProductA debería representar una clase de producto, es decir, todos los productos que comparten una interfaz que está llamando ProductA. En los comentarios sugiero que esto es algo así como una pizza:

interface AbstractPizzaFactory {
    public Pizza buildPizza(List<Topping> toppings);
}


class ThinCrustPizzaFactory implements AbstractPizzaFactory {
    public Pizza buildPizza(List<Topping> toppings){

        ...

    }
}

class DeepDishPizzaFactory implements AbstractPizzaFactory {
    public Pizza buildPizza(List<Topping> toppings){

        ...

    }
}

Y así. Agregar una PanPizzaFactory no afectará a ninguna de las otras clases: es solo una nueva implementación concreta de PizzaFactory. Si tiene productos que no son pizzas, por ejemplo, sándwiches, allí es donde crea otra fábrica abstracta (por ejemplo, AbstractSandwichFactory).

El punto real es que no querrás tener una fábrica abstracta que construya dos tipos muy diferentes de productos con dos métodos diferentes de "compilación". Agrúpelos lo más lógicamente posible para que compartan una interfaz, y luego cree una fábrica abstracta que defina cómo las fábricas concretas deben construir implementaciones de esa interfaz.

Matthew Flynn
fuente
44
Estás bromeando, ¿verdad? ¿Qué pasa si su empresa fabrica 256 productos? O 1024?
Robert Harvey
@RobertHarvey: supongo que ProductA es una clase de productos, supongamos que es una AbstractPizzaFactory. Esto puede tener un ThinCrustPizzaFactory de hormigón, un PanPizzaFactory de hormigón y un DeepDishPizzaFactory de hormigón. Del mismo modo, los sándwiches se construirían por subtipos de AbstractSandwichFactory. Esto es diferente de AgrstractFactory con un método buildPizza y buildSandwich.
Matthew Flynn
@RobertHarvey - Mientras tanto, variaciones sutiles en los productos (como coberturas) pueden ser algo que se puede manejar por separado. Quizás tu método de fábrica sea AbstractPizzaFactory.buildPizza(List<Topping> toppings).
Matthew Flynn
Es posible que desee aclarar esas cosas en su respuesta.
Robert Harvey
@RobertHarvey - actualizado
Matthew Flynn
4

El problema con el escalado es que el código puede escalar de muchas maneras diferentes. El problema que tiene es simplemente causado por la necesidad de escalar en una dirección, ese patrón de diseño no está destinado. En el caso del patrón Abstract Factory, está destinado a escalar para agregar nuevas fábricas de concreto, pero agregar nuevos productos causa grandes cambios estructurales.

Uno de los puntos del diseño y la arquitectura del software es identificar las direcciones más probables que el código escalará y seleccionará patrones basados ​​en esas direcciones probables. Si se identifica, es más probable que agregue un nuevo producto que agregar una nueva fábrica de concreto, entonces usar Abstract Factory no es una buena idea y podría ser mejor repensar completamente el diseño.

Eufórico
fuente
1
su respuesta tiene más sentido que el rígido "¡BÁSCULAS DE FÁBRICA ABSTRACTA!" del otro tipo ...
Turco
@Eufórico, como está sugiriendo, la escala de patrón de fábrica abstracta está en una forma de agregar nuevas fábricas de concreto, pero finalmente la nueva fábrica de concreto tiene que generar productos, lo que se reduce a la creación del producto y, en última instancia, deberíamos hacer cambios estructurales, es que no ¿Correcto?
Jitendar
@Jitendar No entiendo lo que quieres decir. El punto de la fábrica abstracta es que existe una familia relativamente estable de abstracciones relacionadas (ProductoA, ProductoB) y las fábricas concretas crean implementaciones de esas abstracciones, que también están relacionadas de alguna manera. Si la posibilidad de agregar una nueva abstracción es mayor que agregar nuevas implementaciones concretas de esas abstracciones, entonces usar Abstract Factory no es una buena elección.
Eufórico el
3

Sí, tiene razón, la "fábrica abstracta" no escala bien cuando necesita productos abstractos adicionales .

Pero en muchos escenarios del mundo real, tiene una cantidad cambiante de productos, pero solo una cantidad pequeña y fija de productos abstractos para admitir. Por ejemplo, tome el ejemplo de widgets GUI del artículo de Wikipedia sobre fábricas abstractas . La fábrica de GUI abstracta podría tener un método createWidget(en lugar de createButton), donde se Widgetencuentra el "producto abstracto", siendo la clase base más alta para una familia de varias docenas de elementos de GUI. Agregar nuevos widgets GUI de ninguna manera implica un cambio en la interfaz de la fábrica abstracta, por lo que no se debe cambiar ninguna de las interfaces de la fábrica concreta.

Tener muchos productos abstractos significa que tendrá muchas jerarquías de clases diferentes en su código. Y si ese es el caso, puede considerar crear una fábrica individual (o fábrica abstracta) para cada una de las jerarquías.

Doc Brown
fuente
0

Su factor de escala es una simple cuestión de gestión de dependencias. Cuantas más dependencias tenga una clase, más estable y reflexiva debería ser su API.

No hay ningún problema en crear una fábrica que esté definida por la responsabilidad de crear tipos de naturaleza similar o cohesiva. Pero cuantas más clases dependan de esta fábrica, más estricta debería ser esta cohesión.

(Nota: esto se puede hacer de manera gradual, no necesariamente por un BDUF)

Ñame Marcovic
fuente