2010-11-30 16 views
24

Recientemente me encontré con el patrón de diseño de Builder. Parece que diferentes autores usan el "patrón de construcción" para referirse a diferentes sabores, así que permítanme describir el patrón sobre el que estoy preguntando.Patrón de diseño del generador: ¿por qué necesitamos un Director?

Tenemos un algoritmo para crear productos, es decir, objetos de diferentes tipos. A un nivel suficientemente alto de abstracción, el algoritmo es el mismo para todos los tipos de productos, pero cada tipo de producto requiere una implementación diferente de cada uno de los pasos abstractos del algoritmo. Por ejemplo, podríamos tener el siguiente algoritmo pastelera:

1. Add liquids. 
2. Mix well. 
3. Add dry ingredients. 
4. Mix well. 
5. Pour batter into baking pan. 
6. Bake. 
7. Return baked cake. 

Diferentes tortas requerirían diferentes implementaciones de estos pasos, es decir, lo que los líquidos/ingredientes secos a utilizar, qué velocidad para mezclar en, ¿cuánto tiempo para hornear , etc.

El patrón dice que lo haga así. Para cada producto creamos una clase de generador de hormigón con una implementación para cada uno de los pasos anteriores. Todas estas clases se derivan de una clase base del generador abstracto, que es esencialmente una interfaz. Así, por ejemplo, vamos a tener una clase base abstracta CakeBaker con métodos virtuales puros AddLiquid(), MixLiquids(), etc. Los panaderos de la torta de hormigón será subclases concretas, por ejemplo,

class ChocolateCakeBaker : public CakeBaker { 
public: 
    virtual void AddLiquids() 
    { 
     // Add three eggs and 1 cup of cream 
    } 

    virtual void AddDryIngredients() 
    { 
     // Add 2 cups flour, 1 cup sugar, 3 tbsp cocoa powder, 
     // 2 bars ground chocolate, 2 tsp baking powder 
    } 
     ... 
     ... 
}; 

El LemonCitrusCakeBaker también sería una subclase de CakeBaker , pero usaría diferentes ingredientes y cantidades en sus métodos.

Los diferentes tipos de tortas serán, de manera similar, subclases de una clase base abstracta Cake.

Finalmente, tenemos una clase para implementar el algoritmo abstracto. Este es el director . En el ejemplo de panadería podríamos llamarlo ExecutiveBaker. Esta clase aceptaría (del cliente) un objeto concreto de construcción y usaría sus métodos para crear y devolver el producto deseado.

Aquí está mi pregunta. ¿Por qué necesitamos que el director esté separado del constructor abstracto? ¿Por qué no incluirlos en una única clase base abstracta del constructor, protegiendo los métodos públicos del creador abstracto original (y las subclases concretas los reemplazan como antes).

Respuesta

15

La parte central del patrón Builder se refiere al Abstract Builder y sus subclases (constructores de hormigón). De acuerdo con GoF's Design Patterns, el director simplemente "notifica al constructor cada vez que se debe construir una parte del producto", lo cual puede hacer perfectamente el cliente.

La clase StringBuilder en la API de Java es un ejemplo de un generador sin el director respectivo; normalmente la clase de cliente lo "dirige".

También, en Effective Java y Creating and Destroying Java Objects, Joshua Bloch sugiere el uso del patrón de generador y no incluye un director.

+0

¿Eso significa que en una aplicación MVC, un controlador (en realidad un método del controlador, en mi caso específico) podría ser el "director"? –

0

Estoy de acuerdo con usted. Creo que el otro enfoque es que CakeBaker debería tener un método GetCake() que devuelve un método cake (Cake class) y MakeCake() donde se ejecutará el algoritmo. Está bien, pero por otro lado hay una separación responsable allí. Considere el constructor abstracto y los constructores específicos como constructores de partes de un pastel solamente y el Director como gerente o diseñador cuya responsabilidad es ensamblar y producir un pastel.

+0

La separación de la responsabilidad no me convenció. En algunos casos, podría tener sentido separarse; en otros no. No veo esto como parte del patrón sino como una decisión ortogonal. – Ari

+0

Learning DPs Intento que sus principios no los copien literalmente. Además, en la práctica, a menudo hago una combinación de ellos. Por lo tanto, no es necesario codificar la misma cantidad de clases o interfaces que en el documento, sino que debe hacer que su código sea más flexible y modificable. – Arseny

8

Si se separa en Director y Constructor tiene documentada la diferente responsabilidad de ensamblar un producto a partir de un conjunto de piezas (director) y la responsabilidad de crear la pieza (constructor).

  • En el constructor puede cambiar cómo se construye una pieza. En su caso, si un AddLiquid() debe agregar crema o leche.
  • En el director puede cambiar cómo ensamblar las piezas. En su caso, usando AddChocolate() en lugar de AddFruits(), obtiene un pastel diferente.

Si desea que esta flexibilidad adicional, me gustaría cambiar el nombre a (ya que el uso de panadero en el constructor sugiere, que era el trabajo de los constructores de ensamblaje de las partes)

class LightBakingSteps : public BakingSteps { 
public: 
    virtual void AddLiquids() 
    { 
     // Add milk instead of cream 
    } 

    virtual void AddDryIngredients() 
    { 
     // Add light sugar 
    } 

    ... 
}; 

class ChoclateCakeBaker : public CakeBaker { 
public: 
    Cake Bake(BakingSteps& steps) 
    { 
     steps.AddLiquieds(); 
     steps.AddChocolate();  // chocolate instead of fruits 
     return builder.getCake(); 
    } 
} 
+1

Pero, ¿cuál es la ventaja de esta separación? Si deseo diferentes protocolos de ensamblaje, tendré que tener diferentes directores, cada uno adaptado a un constructor específico (abstracto). Estos directores no comparten ninguna funcionalidad común que pueda abstraerse, excepto, tal vez, MakeSomething(), que no es útil. Siempre puede dividir cualquier tarea en subtareas y responsabilidades cada vez más pequeñas, y siempre puede agregar otro nivel de "gerente" o "director" a cualquier patrón. Estoy pensando que los autores de este patrón probablemente pensaban según las líneas que usted ha delineado, pero no puedo ver el valor que tiene. – Ari

+0

@Ari y no puede ver el valor porque el valor de los patrones de programación está intrínsecamente encadenado con el dominio de su programa. ¿Por qué debería haber un panadero particular para cada pastel que hay? quitar dominio: todo es solo un ruido, caracteres aleatorios en la pantalla. eso es lo que quise decir en mi respuesta. –

-1

lado oscuro de los patrones es que contaminan nuestra comprensión del dominio comercial con términos técnicos y desenfoca nuestro enfoque.

Como yo lo veo, hay demasiada relación entre la torta y el conocimiento de cómo hacerlo. Esos pueden desacoplarse introduciendo una idea de pastel con una receta en nuestro código (más como tomar prestado del mundo real, diseñando nuestro modelo por dominio comercial). La receta tendría ingredientes y pasos para hornear (solo un nombre de paso, no una implementación real porque las recetas no hornean pasteles) sobre cómo hacer pastel lo que la receta describe. Nuestro panadero tendría un método BakeCake (receta), y un montón de métodos más pequeños de acuerdo con los pasos de cocción como mezclar, agregar ingrediente, etc.

Tenga en cuenta que si necesita modelar chef en general, no solo pastelero, también necesitarás desacoplar el conocimiento de hornear pasteles del panadero también. Podría hacerse presentando la idea de que el chef tiene una habilidad.

+0

Esto se parece más a una alternativa al patrón que a una explicación de ello. ¿Lo entendí mal? – Ari

+0

@Ari de hecho lo es. técnicamente - No estoy respondiendo pregunta. solo quería recordar que los patrones en sí mismos no son tan importantes. es como con el lenguaje hablado: debes saber al menos lo básico, pero al final es lo que quieres decir lo que importa. –

2

Digamos que quiere hacer una torta sin los ingredientes secos. Lo que vas a hacer es simplemente agregar un nuevo método al Director o crear otro Director. Esto lo protegerá de la complejidad de la herencia y también hará que su código sea más flexible.

4

La variación GoF del patrón de generador NO tiene el constructor SIN director. Hay un punto diferente a esto, pero lo explicaré más adelante.

El punto del patrón Builder es darle varias formas de crear el mismo objeto. El creador solo debe tener métodos que construyan diferentes partes de un objeto, pero el algoritmo, la forma en que se ejecutan estas funciones, debe ser la preocupación del Director. Sin Director, cada cliente tendría la necesidad de saber EXACTAMENTE cómo funciona el edificio. Pero con el Director todo lo que el Cliente necesita saber es qué Constructor usar en un caso específico.

Por lo tanto, lo que tenemos aquí son dos partes:

  1. Builder, que crea partes del objeto uno por uno. Lo importante a tener en cuenta es que para esto guarda el estado del objeto creado.
  2. Director, que controla la forma en que se ejecuta el generador.

Ahora al punto al que me refería anteriormente. La parte del patrón de Builder es útil en otros casos y ha sido utilizada por diferentes proveedores SIN el Director para diferentes propósitos. Un ejemplo concreto de tal uso sería el Doctrine Query Builder.El inconveniente de este enfoque es que cuando el Constructor comienza a construir un objeto se convierte en estado y si el Cliente no reinicia el Constructor después de que se creó el objeto: otro Cliente o el mismo Cliente que se ha usado más de una vez. podría obtener las partes del objeto que se creó anteriormente. Por esta razón, Doctrine usa un patrón Factory para crear cada instancia del Builder.

Espero que esto ayude a los que buscan en Google.

0

Builder sabe cómo hacer pasos específicos. El director sabe cómo ensamblar todo con los pasos del generador.

Funcionan juntos.

La única fragilidad que puedo ver con este patrón es que el cliente es capaz de llamar a los métodos Builder directamente sin director - que puede traer algunos problemas e incoherencias (por ejemplo, no llamar al método Init que es parte del algoritmo de conjunto)

Cuestiones relacionadas