Probablemente lo consideraría un olor a código o incluso un antipatrón tener una clase que implemente 23 interfaces. Si de hecho es un antipatrón, ¿cómo lo llamarías? ¿O simplemente no sigue el principio de responsabilidad única?
object-oriented
anti-patterns
solid
Jonas Elfström
fuente
fuente
Respuestas:
Familia Real: No hacen nada particularmente loco, pero tienen mil millones de títulos y están relacionados con la mayoría de los demás miembros de la realeza de alguna manera.
fuente
somehow
Declaro que este antipatrón se llamará Jack of All Trades, o quizás Too Many Hats.
fuente
Si tuviera que darle un nombre, creo que lo llamaría Hydra :
De particular relevancia es el hecho de que no solo tiene muchas cabezas, sino que sigue creciendo cada vez más y es imposible de matar por eso. Esa ha sido mi experiencia con este tipo de diseños; los desarrolladores siguen interfiriendo cada vez más interfaces hasta que tiene demasiadas para caber en la pantalla, y para entonces está tan arraigado en el diseño y las suposiciones del programa que dividirlo es una perspectiva desesperada (si lo intentas, en realidad a menudo terminan necesitando más interfaces para cerrar la brecha).
Una señal temprana de fatalidad inminente debido a una "Hidra" es la transmisión frecuente de una interfaz a otra, sin mucha comprobación de cordura, y a menudo a un objetivo que no tiene sentido, como en:
Es obvio cuando se saca de contexto que hay algo sospechoso en este código, pero la probabilidad de que esto suceda aumenta a medida que crecen las "cabezas" de un objeto, porque los desarrolladores saben intuitivamente que siempre están tratando con la misma criatura.
Buena suerte nunca hacer cualquier mantenimiento en un objeto que implementa las interfaces 23. Las posibilidades de que no cause daños colaterales en el proceso son escasas o nulas.
fuente
El Objeto de Dios viene a la mente; Un solo objeto que sabe hacer TODO. Esto proviene de la baja adherencia a los requisitos de "cohesión" de las dos principales metodologías de diseño; si tiene un objeto con 23 interfaces, tiene un objeto que sabe cómo ser 23 cosas diferentes para sus usuarios, y esas 23 cosas diferentes probablemente no están en la línea de una sola tarea o área del sistema.
En SOLID, aunque el creador de este objeto obviamente ha intentado seguir el Principio de segregación de interfaz, ha violado una regla anterior; Principio de responsabilidad única. Hay una razón por la que es "SÓLIDO"; S siempre viene primero cuando se piensa en el diseño, y siguen todas las demás reglas.
En GRASP, el creador de dicha clase ha descuidado la regla de "Alta Cohesión"; GRASP, a diferencia de SOLID, enseña que un objeto no TIENE que tener una sola responsabilidad, pero a lo sumo debe tener dos o tres responsabilidades muy relacionadas.
fuente
¡23 es solo un número! En el escenario más probable, es lo suficientemente alto como para alarmar. Sin embargo, si preguntamos, ¿cuál es el mayor número de métodos / interfaces antes de que se pueda llamar una etiqueta como "antipatrón"? ¿Son 5, 10 o 25? Te das cuenta de que ese número realmente no es una respuesta porque si 10 es bueno, 11 también puede serlo, y luego cualquier número entero después de eso.
La verdadera pregunta es sobre la complejidad. Y debemos señalar que el código extenso, el número de métodos o el gran tamaño de la clase por cualquier medida NO son en realidad la definición de complejidad. Sí, un código cada vez más grande (mayor número de métodos) dificulta la lectura y comprensión de un nuevo principiante. También maneja funcionalidades potencialmente variadas, gran número de excepciones y algoritmos bastante evolucionados para varios escenarios. Esto no significa que sea complejo, solo es difícil de digerir.
Por otro lado, el código de tamaño relativamente pequeño que uno puede leer en unas pocas horas dentro y fuera puede ser complejo. Aquí es cuando creo que el código es (innecesariamente) complejo.
Aquí se puede poner toda la sabiduría del diseño orientado a objetos para definir "complejo", pero restringiría aquí para mostrar cuando "tantos métodos" es una indicación de complejidad.
Conocimiento mutuo. (también conocido como acoplamiento) Muchas veces, cuando las cosas se escriben como clases, todos pensamos que es un código orientado a objetos "agradable". Pero la suposición sobre la otra clase esencialmente rompe la encapsulación real necesaria. Cuando tiene métodos que "filtran" detalles profundos sobre el estado interno de los algoritmos, y la aplicación se construye con la suposición clave sobre el estado interno de la clase de servicio.
Demasiadas repeticiones (interrumpiendo) Cuando los métodos que tienen nombres similares pero hacen un trabajo contradictorio, o contradicen nombres con una funcionalidad similar. Muchas veces los códigos evolucionan para admitir interfaces ligeramente diferentes para diferentes aplicaciones.
Demasiados roles Cuando la clase sigue agregando funciones secundarias y se expande más a medida que a la gente le gusta, solo para saber que la clase es ahora una clase dos. Sorprendentemente, todos estos comienzan con genuinoLos requisitos y ninguna otra clase existen para hacer esto. Considere esto, hay una clase Transacción, que le dice detalles de la transacción. Se ve bien hasta ahora, ahora alguien requiere la conversión de formato en el "momento de la transacción" (entre UTC y similares), más tarde, las personas agregan reglas para verificar si ciertas cosas estaban en ciertas fechas para validar las transacciones invalidadas. - No escribiré toda la historia, pero eventualmente, la clase de transacción construye todo el calendario y luego la gente comienza a usar "solo el calendario". Esto es muy complejo (imaginar) ¿por qué crearé una instancia de "clase de transacción" para tener la funcionalidad que me proporcionaría un "calendario"!
(In) consistencia de API Cuando hago book_a_ticket () - ¡Reservo un ticket! Eso es muy simple, independientemente de cuántas comprobaciones y procesos se realicen para que esto suceda. Ahora se vuelve complejo cuando el flujo de tiempo comienza a afectar esto. Por lo general, uno permitiría la "búsqueda" y el posible estado disponible / no disponible, luego, para reducir el retroceso, comience a guardar algunos punteros de contexto dentro del ticket y luego consulte eso para reservar el ticket. La búsqueda no es la única funcionalidad: las cosas empeoran después de muchas de estas "funciones secundarias". ¡En el proceso, el significado de book_a_ticket () implica book_that_ticket ()! y eso puede ser inimaginablemente complejo.
Probablemente hay muchas situaciones de este tipo que vería en un código muy evolucionado y estoy seguro de que muchas personas pueden agregar escenarios, donde "tantos métodos" simplemente no tienen sentido o no hacen lo que obviamente pensarían. Este es el antipatrón.
Mi experiencia personal es que cuando los proyectos que comienzan razonablemente de abajo hacia arriba, muchas cosas para las que debería haber clases genuinas por sí mismas, permanecen enterradas o, lo que es peor, aún se dividen entre diferentes clases y aumenta el acoplamiento. Muy a menudo, lo que habría merecido 10 clases, pero tiene solo 4, es probable que muchas de ellas tengan métodos confusos y una gran cantidad de métodos. Llámalo DIOS y DRAGÓN, esto es lo que es MALO.
PERO te encuentras con clases MUY GRANDES que son perfectamente consistentes, tienen 30 métodos y aún son muy LIMPIOS. Pueden ser buenos.
fuente
Las clases deben tener exactamente el número correcto de interfaces; ni mas ni menos.
Decir "demasiados" sería imposible sin mirar si todas esas interfaces tienen o no un propósito útil en esa clase. Declarar que un objeto implementa una interfaz significa que debe implementar sus métodos si espera que la clase se compile. (Supongo que la clase que está viendo lo hace). Si todo en la interfaz está implementado y esas implementaciones hacen algo relativo a las entrañas de la clase, sería difícil decir que la implementación no debería estar allí. El único caso que se me ocurre es que una interfaz que cumple con esos criterios no es externa: cuando nadie la usa.
Algunos lenguajes permiten que las interfaces se "subclasifiquen" utilizando un mecanismo como la
extends
palabra clave de Java , y quien lo escribió puede no haberlo sabido. También podría ser que los 23 estén lo suficientemente lejos como para que agregarlos no tenga sentido.fuente
Parece que tienen un par "getter" / "setter" para cada propiedad, como es normal para un "bean" típico y promovieron todos estos métodos a la interfaz. Entonces, ¿qué hay de llamarlo un " haba tiene ". O la "flatulencia" más rabelaisiana después de los efectos bien conocidos de demasiados frijoles.
fuente
El número de interfaces a menudo crece cuando se usa un objeto genérico en diferentes entornos. En C #, las variantes de IComparable, IEqualityComparer e IComparer permiten ordenar en configuraciones distintas, por lo que puede terminar implementando todas ellas, algunas de ellas tal vez más de una vez, ya que puede implementar las versiones genéricas fuertemente tipadas, así como las versiones no genéricas , además, puede implementar más de uno de los genéricos.
Tomemos un escenario de ejemplo, digamos una tienda web donde puede comprar créditos con los que puede comprar otra cosa (los sitios de fotos de archivo a menudo usan este esquema). Es posible que tenga una clase "Valuta" y una clase "Créditos" que heredan de la misma base. Valuta tiene algunas ingeniosas sobrecargas de operadores y rutinas de comparación que le permiten hacer cálculos sin preocuparse por la propiedad "Moneda" (agregar libras a dólares, por ejemplo). Los créditos son mucho más simples pero tienen algún otro comportamiento distinto. Si desea poder compararlos entre sí, podría terminar implementando IComparable, así como IComparable y las otras variantes de interfaces de comparación en ambos (a pesar de que utilizan una implementación común, ya sea en la clase base o en otro lugar).
Al implementar la serialización, ISerializable, se implementan IDeserializationCallback. Luego implementando pilas de deshacer-rehacer: se agrega IClonable Funcionalidad IsDirty: IObservable, INotifyPropertyChanged. Permitir a los usuarios editar los valores utilizando cadenas: IConvertable ... La lista puede seguir y seguir ...
En los idiomas modernos, vemos una tendencia diferente que ayuda a separar estos aspectos y colocarlos en sus propias clases, fuera de la clase principal. La clase o aspecto externo se asocia con la clase de destino mediante el uso de anotaciones (atributos). A menudo es posible hacer que las clases de aspecto externo sean más o menos genéricas.
El uso de atributos (anotación) está sujeto a reflexión. Un inconveniente es la pérdida de rendimiento menor (inicial). Un inconveniente (a menudo emocional) es que los principios como la encapsulación deben relajarse.
Siempre hay otras soluciones, pero para cada solución ingeniosa hay una compensación o una trampa. Por ejemplo, el uso de una solución ORM puede exigir que todas las propiedades se declaren virtuales. Las soluciones de serialización pueden exigir constructores predeterminados en sus clases. Si está utilizando la inyección de dependencia, puede terminar implementando 23 interfaces en una sola clase.
En mi opinión, 23 interfaces no tienen que ser malas por definición. Puede haber un esquema bien pensado detrás de él, o alguna determinación principal, como evitar el uso de la reflexión o las creencias de encapsulación extrema.
Cada vez que cambia un trabajo o tiene que construir sobre una arquitectura existente. Mi consejo es que primero se familiarice por completo, no intente refactorizar todo demasiado rápido. Escuche al desarrollador original (si todavía está allí) e intente descubrir los pensamientos e ideas detrás de lo que ve. Cuando haga preguntas, no lo haga solo por analizarlo, sino para aprender ... Sí, todos tienen sus propios martillos dorados, pero cuantos más martillos pueda recolectar, más fácil será llevarse bien con sus colegas.
fuente
"Demasiado" es subjetivo: ¿estilo de programación? ¿actuación? conformidad con las normas? precedentes? simplemente sentido de comodidad / confianza?
Siempre que su código se comporte correctamente y no presente problemas de mantenimiento, 23 podría incluso ser la nueva norma. Algún día podría decir: "Puedes hacer un excelente trabajo con 23 interfaces, ver: Jonas Elfström".
fuente
Yo diría que el límite debería ser un número menor que 23, más como 5 o 7,
sin embargo, esto no incluye ningún número de interfaces que esas interfaces hereden o cualquier número de interfaces implementadas por las clases base.
(En total, N + cualquier número de interfaces heredadas, donde N <= 7.)
Si su clase implementa demasiadas interfaces, probablemente sea una clase divina .
fuente