2009-12-17 10 views
6

Parte de nuestro producto principal es un sitio web CMS que hace uso de varios widgets de página. Estos widgets son responsables de mostrar el contenido, enumerar productos, gestionar el registro de eventos, etc. Cada widget está representado por una clase que se deriva de la clase de widgets base. Al renderizar una página, el servidor toma el widget de la página de la base de datos y luego crea una instancia de la clase correcta. El método de fábrica ¿no?¿Estoy exagerando con mi método de fábrica?

Private Function WidgetFactory(typeId) 
    Dim oWidget 
    Select Case typeId 
     Case widgetType.ContentBlock 
      Set oWidget = New ContentWidget 
     Case widgetType.Registration 
      Set oWidget = New RegistrationWidget 
     Case widgetType.DocumentList 
      Set oWidget = New DocumentListWidget 
     Case widgetType.DocumentDisplay 
    End Select 
    Set WidgetFactory = oWidget 
End Function 

De todas formas, todo esto está bien, pero como el tiempo ha pasado el número de tipos de widgets ha aumentado a alrededor de 50 significa que el método de fábrica es bastante larga. Cada vez que creo un nuevo tipo de widget voy a agregar otro par de líneas al método y una pequeña alarma suena en mi cabeza que tal vez esta no sea la mejor manera de hacer las cosas. Tiendo a ignorar esa alarma, pero cada vez es más fuerte.

Entonces, ¿lo estoy haciendo mal? ¿Hay una mejor manera de manejar este escenario?

Respuesta

15

Creo que la pregunta que debe hacerse es: ¿Por qué estoy usando un método de Fábrica aquí?

Si la respuesta es "debido a una", y Un es una buena razón, y luego continuar haciéndolo, incluso si esto significa algo de código adicional. Si la respuesta es "no lo sé, porque he oído que se supone que debes hacerlo de esta manera?" entonces deberías reconsiderarlo.

Repasemos las razones estándar para usar fábricas. Esto es lo que Wikipedia says sobre el patrón método de fábrica:

[...], se trata el problema de la creación de objetos (productos), sin especificar la clase exacta del objeto que se va a crear. El patrón de diseño de método de fábrica maneja este problema al definir un método separado para crear los objetos, cuyas subclases pueden anular para especificar el tipo de producto derivado que se creará.

Desde su WidgetFactory es Private, esto obviamente no es la razón por la que utiliza este patrón. ¿Qué pasa con el "Patrón de fábrica" ​​en sí mismo (independientemente de si lo implementa utilizando un método de fábrica o una clase abstracta)? Una vez más, Wikipedia says:

Uso del patrón de la fábrica cuando:

  • La creación del objeto impide la reutilización sin duplicar significativamente código.
  • La creación del objeto requiere acceso a información o recursos no apropiados para contener dentro del objeto de composición.
  • La gestión de vida de los objetos creados debe centralizarse para garantizar un comportamiento coherente.

Utilizando su código de ejemplo, que no se parece a nada de esto se adapte a su necesidad.Entonces, la pregunta (que solo usted puede responder) es: (1) ¿Qué tan probable es que necesite las características de una fábrica centralizada para sus widgets en el futuro y (2) qué tan costoso sea cambiar todo de nuevo? a un enfoque de fábrica si lo necesita en el futuro? Si ambos son bajos, puede dejar el método Factory de forma segura por el momento.


EDIT: Voy a volver a su caso especial después de esta elaboración genérica: Por lo general, es a = new XyzWidget() vs a = WidgetFactory.Create(WidgetType.Xyz). En su caso, sin embargo, tiene algunos (¿numéricos?) typeId de una base de datos. Como Mark escribió correctamente, debe tener este typeId -> className mapa en algún lugar.

Por lo tanto, en ese caso, la buena razón para utilizar un método de fábrica podría ser: "Necesito una especie de enorme ConvertWidgetTypeIdToClassName caso sentencia de selección de todos modos, así que usar un método de fábrica lleva ningún código adicional más Proporciona las ventajas del método de fábrica de forma gratuita, si alguna vez las necesito ".

Como alternativa, se puede almacenar el nombre de la clase del widget en la base de datos (que probablemente ya tiene algunos WidgetType mesa con clave primaria typeId de todos modos, ¿verdad?) Y crear la clase mediante la reflexión (si su idioma permite este tipo de cosa). Esto tiene muchas ventajas (por ejemplo, puede colocar DLL con nuevos widgets y no tiene que cambiar su código CMS principal) sino también desventajas (por ejemplo, "cadena mágica" en su base de datos que no se comprueba en tiempo de compilación; inyección, dependiendo de quién tenga acceso a esa tabla).

+0

+1 Preciso y al grano. –

+0

Gracias por su respuesta. Creo que está lejos de decir que la respuesta a la primera pregunta puede ser "No sé ...". ¿Cuál podría ser un enfoque alternativo a mi método? Necesito crear una subclase diferente de Widget dependiendo del valor de typeId almacenado en la base de datos. – jammus

+0

@jammus: Edité mi respuesta. – Heinzi

0

prueba las categorías de tus widgets, tal vez en función de su funcionalidad. si algunos de ellos dependen lógicamente el uno del otro, créelos con una sola construcción

4

El método WidgetFactory es realmente una asignación de una enumeración typeId a clases concretas. En general, es mejor si puede evitar las enumeraciones por completo, pero a veces (especialmente en aplicaciones web) debe realizar un viaje de ida y vuelta a un entorno (por ejemplo, el navegador) que no comprenda el polimorfismo y necesite tales medidas.

Refactoring contiene una muy buena explicación de por qué las sentencias de cambio/selección de caso son olores de código, pero eso se dirige principalmente al caso en el que tiene muchos conmutadores similares.

Si su método WidgetFactory es el único lugar donde enciende esa enumeración particular, yo diría que no tiene que preocuparse. Necesitas tener ese mapa en alguna parte.

Como alternativa, podría definir el mapa como un diccionario, pero la cantidad de líneas de código no disminuiría significativamente; podría cortar las líneas de código a la mitad, pero el grado de complejidad sería el equivalente .

2

La forma clásica de implementar una fábrica no es utilizar un conmutador gigante o una escalera if, sino utilizar un mapa que mapea el nombre del tipo de objeto a una función de creación de objeto. Aparte de todo lo demás, esto permite que la fábrica se modifique en tiempo de ejecución.

2

Ya sea que sea apropiado o no, siempre he creído que el momento de usar una fábrica es cuando la decisión de qué tipo de objeto crear se basará en la información que no está disponible hasta el tiempo de ejecución.

Indicó en un comentario de seguimiento que el tipo de widget está almacenado en una base de datos. Como su código no sabe qué objetos se crearán hasta el tiempo de ejecución, creo que este es un uso perfectamente válido del patrón Factory. Al tener la fábrica, permite que su programa difiera la decisión de qué tipo de objeto utilizar hasta el momento en que realmente se puede tomar la decisión.

4

Su aplicación del patrón de fábrica es correcta. Usted tiene información que dicta qué tipo de N se crea. Una fábrica es lo que sabe cómo hacer eso. (Es un poco extraño como método privado. Esperaría que esté en una interfaz IWidgetFactory).

Sin embargo, su implementación relaciona estrechamente la implementación con los tipos concretos. Si en su lugar se asignó typeId -> widgetType, puede usar Activator.CreateInstance(widgetType) para que la fábrica entienda cualquier tipo de widget.

Ahora, puede definir las asignaciones como desee: un diccionario simple, descubrimiento (atributos/reflexión), en el archivo de configuración, etc. Debe conocer todos los tipos en un lugar, pero también tiene el opción para componer múltiples fuentes.

+0

+1 estoy de acuerdo en que el núcleo de lo que está sucediendo aquí es el mapeo, ya que un primer paso en el interruptor mantiene las cosas juntas, y luego lo hace más fácilmente extensible con configuración extra, reflexión, etc., si es un refinamiento agradable. – djna

1

Ha sido mi experiencia que las fábricas crecen, por lo que sus dependencias no tienen por qué crecer. Si ve este mapeo duplicarse en otros lugares, entonces tiene que preocuparse.

Cuestiones relacionadas