2009-05-22 7 views
11

En prácticamente todos los proyectos que jamás he trabajado, habrá uno o dos clases con las siguientes propiedades:¿Cómo previene que las clases se conviertan en "imanes de dependencia" y clases de Dios?

  • extremadamente grandes con muchos muchos miembros y métodos.
  • Muchas otras clases que heredan de esta clase.
  • Muchas otras clases de lo contrario dependen de esta clase.

mal diseño, se podría decir. Pero en todos los casos, este no fue el caso en el momento del diseño. Las clases crecieron orgánicamente con el tiempo y se convirtieron en la proverbial 'Clase de Dios'. Esta ha sido una tal invariante de mi experiencia con grandes proyectos de software que tengo que preguntar:

  • ¿Es posible prever probables imanes de dependencia y software de diseño de tal manera que la posibilidad de dichas clases se manifiestan es menor ¿probable? Si es así, específicamente, ¿cómo?
  • ¿O simplemente requiere una refactorización despiadada a medida que pasa el tiempo?
  • O, ¿hay alguna solución técnica o patrón de diseño que pueda mitigar los problemas causados ​​por dichas clases?
  • ¿O una combinación de los tres?

consejos, experiencias e ideas bienvenidas!

Respuesta

8

La refacturación continua ayudará a evitar esto.

Además, este es un lugar donde forzar una cierta cantidad de análisis de código estático puede ser muy beneficioso. A menudo puede encontrar estas clases y marcarlas para refactorizar automáticamente de forma muy sencilla mirando las métricas del código.

Mi mayor sugerencia, sin embargo, es mantener una actitud que diseña necesidad de cambiar, y las cosas tienen que ser descompuesto. A menudo, en mi experiencia, estas clases se forman porque las personas no están dispuestas a considerar cambiar un diseño roto o subóptimo. Mantenerse flexible hace que sea más fácil prevenir esto.

2

Mi experiencia es que el primer diseño que se presenta no suele ser el correcto y con el tiempo debe replantearse el diseño. Cuando una clase comienza a ser demasiado grande, generalmente es porque es starts to violate SRP.

Algunos de los patrones de refactorización de Fowler son útiles. The Extract Class refactorización puede ayudar, como puede Extract Subclass y Extract Superclass. A menudo me encuentro cambiando jerarquías de herencia, quizás usando replace inheritance with delegation.

+0

"Cuando una clase comienza a ser demasiado grande, generalmente es porque comienza a violar SRP". -- No lo sé. Por ejemplo, tengo una clase 'Editor' cuya responsabilidad es editar un DOM. Con el tiempo, hay más y más tipos de ediciones (es decir, cantidad de métodos públicos), y a medida que el DOM que se está editando se vuelve más complicado, cada edición se vuelve más complicada (es decir, los métodos privados que implementan cada edición se enredan más). La clase Editor nunca viola necesariamente a SRP, solo crece hasta que es mejor volver a empaquetar algunas de sus subrutinas como clases o capas por derecho propio. – ChrisW

+0

ChrisW, noté que dije por lo general y no siempre. A veces hay clases grandes que serían contraproducentes para dividir. – RichardOD

0

refactorización Ruthless para mí también, especialmente para pequeños proyectos que duran mucho tiempo (por ejemplo, años) (es decir, no cascada, altamente diseñado).

Comienzo pequeño: un poco de arquitectura, digamos de 2 a 5 componentes. Implemento la funcionalidad agregando código a estos componentes ... y cuando un componente se vuelve demasiado grande (que es subjetivo) entonces lo divido en más de un componente. [Por 'componente' me refiero a 'paquete' o 'DLL' o algo así.]

"Dependency magnets" es un problema ligeramente diferente: para abordar eso, estoy interesado en el software de capas, sin dependencias cíclicas entre capas, y a veces con una fachada que aísla el alto nivel de los componentes de bajo nivel.

7

Mucho de esto, creo, tiende a provenir de la pereza de diseño posterior. La filosofía de "refactorización agresiva" es un antídoto muy bueno para este tipo de problema de diseño. El problema es que, si bien el diseño inicial de las clases de "imán de dependencia" es bueno, los ingenieros posteriores con estrés temporal tienden a simplemente agregar otras funcionalidades a esa clase por la simple razón de que está disponible donde lo necesitan (generalmente).

En gran parte, el problema tiene que ver con el hecho de que un diseño que puede manejar las necesidades de un producto de software maduro es un overdesign para un producto de software menos maduro. Todo el diseño que termina siendo una iteración madura de un producto de software es una exageración masiva para una iteración anterior de ese producto; Fundamentalmente, el diseño siempre debe ser manipulado mientras el desarrollo continúa, por lo tanto. Y aquí es donde la refactorización es necesaria; a medida que las nuevas características y funcionalidades comienzan a implementarse, los problemas de diseño mostrarán sus cabezas; a medida que lo hacen, es sumamente importante asumir la tarea de rediseñar la forma en que las clases interactúan y refactorizar el código para aprovecharlo. Solo de esa manera puede mantener un vencimiento de diseño que se encuentre en cualquier tipo de similitud con el vencimiento del proyecto.

Efectivamente, hay un nivel de madurez de diseño que aumenta junto con la complejidad del proyecto; un diseño masivamente complejo para un proyecto simple es inapropiado; de la misma manera que un diseño masivamente simple para un proyecto complejo es inapropiado. Pero debido a que esa discrepancia existe, el diseño debe evolucionar junto con el proyecto. Refactorizar como un proceso es simplemente un reconocimiento de esa necesidad.

Cuestiones relacionadas