Principios SOLID: Single Responsibility Principle

SOLID es el acrónimo de cinco principios básicos de la programación orientada a objetos. La aplicación de este conjunto de principios de programación conlleva al desarrollo de sistemas más simples, fáciles de mantener y ampliables a lo largo del tiempo. Si bien debemos. entender los principios como directrices y no como leyes.

Se dice que un desarrollador junior debería aplicarlos siempre. Con el paso del tiempo y la adquisición de experiencia e interiorización de estos y otros conceptos de desarrollo de software, podrá decidir acertadamente en qué grado debe aplicar cada uno de ellos en función de sus necesidades, moldeando la arquitectura del sistema a su conveniencia.

Single Responsibility Principle

Primer principio de SOLID, abreviado como SRP y conocido como principio de responsabilidad única en español.

Este principio busca fomentar la cohesión en el código enunciando que si cada clase de nuestro sistema tiene una única responsabilidad (finalidad u objetivo) esta será más sencilla de mantener.

Vamos a verlo con un ejemplo:

Supongamos que queremos mostrar un mensaje de felicitación de cumpleaños en el panel de control de un usuario de nuestra aplicación.

En un primer momento, una clase UserPanel consulta la fecha de nacimiento del usuario y comprueba si el día y mes coinciden con la fecha actual para felicitarle.

Como podemos ver en el ejemplo anterior, la clase User está exponiendo la implementación de su atributo birthdate a través de un método accesor getter, aunque el propio atributo esté declarado como privado.

La responsabilidad mal establecida está rompiendo con el encapsulamiento y por ello aumentando el acoplamiento entre las dos clases y reduciendo la cohesión. Para que esto no suceda, se ha de mover dicha responsabilidad para que sea el objeto de la propia clase User quien diga si es o no su cumpleaños.

El código resultante sigue siendo mejorable, pero al menos se ha avanzado un paso más con respecto al original.

Cohesión, acoplamiento y encapsulamiento

Cohesión

En el diseño orientado a objetos, se entiende por cohesión (cohesion en inglés) al grado en el que los distintos elementos de un módulo se mantienen unidos a dicho módulo. Es decir, aquellos elementos que pueden entenderse como parte de un concepto que los engloba, deberían estar dentro de este y no dispersos en otros módulos.

Los elementos convergen hacia su propio módulo y son conocidos y usados sólo por este

Acoplamiento

Por otra parte, el acoplamiento (coupling en inglés) se refiere al grado en el que los distintos módulos de un sistema son independientes unos de otros. Cuando un módulo accede directamente a un elemento de otro módulo para definir su comportamiento, se dice que ambos módulos están acoplados.

Los elementos tienden hacia otros módulos que los utilizan en vez de sólo por el módulo que los contiene

Ambos, cohesión y acoplamiento, pueden entenderse como fuerzas gravitatorias opuestas, dado que la necesidad de relacionarse entre módulos dará como resultado la aparición de estos.

Así, cuanta mayor cohesión tenga un sistema, menor será su acoplamiento y viceversa.

Encapsulamiento

El encapsulamiento (encapsulation en inglés) es una de las guías del diseño orientado a objetos que enuncia que el estado (conjunto de propiedades) de un objeto nunca debe ser expuesto, de forma que este solo cambie por su propio comportamiento (sus métodos).

Podemos darnos cuenta de que este concepto propicia la cohesión frente al acoplamiento. Esto es algo que debemos buscar en el diseño de nuestros sistemas. Desde el nivel más básico, el de clase, como a los más abstractos, la interacción entre grupos de clases (entendidos como packages en algunos lenguajes como Java) o incluso en la relación de nuestro sistema con otros.

Cuaderno: Refactoring. Malos olores entre clases: Clases librería

Introducción

Una aplicación moderna utilizará clases librería. En ocasiones estas nos ponen en un dilema. Queremos que la librería sea diferente, pero no queremos cambiarla. Incluso cuando es posible cambiar de librería, conlleva riesgos: Afecta a otros clientes y ello implica rehacer nuestros cambios para futuras versiones de la librería.

Incomplete Library Class

Síntomas

Estás utilizando una librería y hay una característica que te gustaría que tuviese.

Qué hacer

  • Contactar con el creador para ver si puede incorporar la característica.
  • Si son sólo un par de métodos, usa Introduce Foreign Method en la clase cliente de la librería. Esto genera Future Envy pero es insalvable.
  • Si son bastantes métodos, usa Introduce Local Extension (creando una subclase de la librería para crear nuevas pseudolibrerías). Usa la nueva clase extendida para avanzar.
  • Puedes decidir introducir una capa para envolver la librería.

Recompensas

Reduce la duplicidad (cuando puedes reusar el código de la librería en lugar de implementarlo completamente desde cero).

Contraindicaciones

Si varios proyectos incorporan una librería de formas incompatibles, puede suponer un trabajo extra el adaptarse a futuros cambios de la misma.

Cuaderno: Refactoring. Malos olores entre clases: Alojando el cambio

Introducción

Algunos problemas se vuelven más aparentes cuando intentas cambiar el código. Lo ideal es que una decisión de cambio afecte sólo a un único lugar. Cuando esto no sucede, es una señal de duplicidad de código. Detectar estos problemas suele tener otros beneficios como facilitar la testeabilidad del código.

Los malos olores relativos al cambio simultáneo forzado entre clases son:

Divergent Change

Síntomas

Una misma clase necesita cambiar por diferentes motivos.

Qué hacer

  • Si la clase encuentra un objeto y hace algo con él, deja que el cliente encuentre el objeto y pásaselo o deja que la clase retorne un valor que el cliente use.
  • Usa Extract Class para crear clases separadas para cada decisión.
  • Si varias clases están compartiendo el mismo tipo de decisiones, puedes unificarlas en una nueva clase con Extract Superclass o Extract Subclass. Llegado el caso estas clases pueden formar una capa.

Recompensas

  • Mejora la comunicación (al expresar mejor la intención).
  • Mejora la estructura para futuros cambios.

Shotgun Surgery

Síntomas

Hacer un cambio supone modificar varias clases.

Qué hacer

  • Identifica la clase que debería contener el grupo de cambios. Puede ser una clase existente o que tengas que crearla con Extract Class.
  • Usa Move Field y Move Method para llevar la funcionalidad a la clase elegida. Una vez que las clases restantes sean lo suficientemente simples, puedes usar Inline Class para eliminarlas.

Recompensas

  • Reduce la duplicidad.
  • Mejora la comunicación.
  • Mejora la mantenibilidad (los próximos cambios estarán más localizados).

Parallel Inheritance Hierarchies

Síntomas

  • Crear una nueva subclase en una jerarquía te supone crear una clase relacionada en otra jerarquía.
  • Encuentras dos jerarquías donde las superclases tienen los mismos prefijos (los nombres reflejan el requisito para coordinarlas). Este es un caso especial de Shotgun Surgery.

Qué hacer

Usa Move Field y Move Method para redistribuir las características de una forma en que puedas eliminar una de las jerarquías.

Recompensas

  • Reduce la duplicidad.
  • Mejora la comunicación.
  • Puede reducir el tamaño.

Combinatorial Explosion

Síntomas

  • Introducir una nueva clase supone introducir múltiples clases en varios sitios de una jerarquía.
  • Cada capa de la jerarquía usa un conjunto común de palabras (informa de estilo, mutabilidad, etc.).

Qué hacer

  • Si las cosas no han ido demasiado lejos, puedes usar Replace Inheritance with Delegation (al crear una misma interfaz para las variaciones, puedes usar el patrón Decorator.
  • Si la situación se ha vuelto demasiado compleja, se requieren grandes refactorizaciones como Tease Apart Inheritance.

Recompensas

  • Reduce la duplicidad.
  • Reduce el tamaño.

Cuaderno: Refactoring. Malos olores entre clases: Responsabilidad

Introducción

El equilibrio en la responsabilidad entre objetos es difícil de conseguir. Una de las virtudes de la refactorización es que nos permite experimentar con diferentes ideas de una forma segura y nos permite cambiar de idea.

Hay herramientas que nos ayudan a decidir como trabajan los objetos entre sí, como los patrones de diseño o las cartas CRC.

Las refactorizaciones suelen ser reversibles y pueden compensar dos opciones.

Los malos olores que pueden aparecer por una mala separación de las responsabilidades son:

Feature Envy

Síntomas

Un método parece más enfocado a manipular los datos de otra clase más que los suyos. Esto suele generar duplicidad ya que varios clientes realizan las mismas acciones sobre los datos de otro objeto o este es tocado varias veces en una misma fila.

Qué hacer

Utiliza Move Method para poner las acciones en la clase correcta. Es probable que tengas que utilizar primero Extract Method para aislar la parte de código que te interesa mover.

Recompensas

  • Reduce la duplicidad.
  • Suele mejorar la comunicación.
  • Puede exponer posteriores oportunidades de refactorización.

Contraindicaciones

En ocasiones el comportamiento está puesto intencionadamente en la clase «incorrecta». Por ejemplo, algunos patrones de diseño, como el Strategy o Visitor ponen el comportamiento en una clase separada y así poder realizar cambios de forma independiente. Si utilizas Move Method para ponerlo de vuelta, puedes acabar uniendo cosas que deberían cambiar de forma independiente.

Innapropiate Intimacy (General Form)

Síntomas

Una clase accede a las partes internas de otra clase independiente.

Qué hacer

  • Si dos clases independientes están enrredadas, usa Move Method y Move Field para poner cada cosa en su lugar.
  • Si las partes enrredadas parecen ser una clase perdida, utiliza Extract Class y Hide Delegate para introducir la nueva clase.
  • Si ambas clases se apuntan mutuamente, usa Change Bidirectional Reference to Unidirectional para crear una dependencia unidireccional.
  • Si una subclase está demasiado acoplada a su generalización:
    • Si está accediendo a los campos de la generalización de forma incontrolada, usa Self Encapsulate Field.
    • Si la generalización puede definir un algoritmo general que pueda ser usado por la subclase, usa Form Template Method.
    • Si la generalización y la subclase necesitan estar aún más desacopladas, usa Replace Inheritance with Delegation.

Recompensas

  • Reduce la duplicidad.
  • Suele mejorar la comunicación.
  • Puede reducir el tamaño.

Message Chains

Qué hacer

Si las manipulaciones pertenecen al objeto que las recibe, utiliza Extract Method y Move Method para llevarlas a él.

Usa Hide Delegate para hacer que el método dependa de un único objeto. Esto puede implicar que se repita la delegación a lo largo de los objetos relacionados.

Recompensas

Puede reducir o exponer duplicidad.

Contraindicaciones

Esta refactorización es un equilibrio. Si aplicas demasiado Hide Delegate, puedes acabar encontrando en que todas las clases están tan ocupadas delegando que ninguna parece estar realizando el trabajo. A veces es menos confuso mantener una cadena de mensajes.

Middle Man

Qué hacer

  • En general, aplica Remove Middle Man haciendo que el cliente llame a la clase delegada directamente.
  • Si la clase delegada es contenida por el Middle Man o es inmutable, el Middle Man no tiene comportamiento a añadir y puede ser visto como un ejemplo de la delegación, usa Replace Delegation with Inheritance.

Recompensas

  • Reduce el tamaño.
  • Puede mejorar la comunicación.

Contraindicaciones

  • Algunos patrones (por ejemplo Proxy o Decorador) crean delegados intencionadamente. No elimines un Middle Man que existe por un motivo.
  • Middle Man y Message Chain se equilibran entre sí.
  • Los delegados proveen una especie de fachada, dejando al cliente permanecer ignorante a los detalles de los mensajes y las estructuras. Eliminar un Middle Man puede exponer a los clientes demasiada información.

Cuaderno: Refactoring. Malos olores entre clases: Herencia

Introducción

La relación entre una clase y su subclase suele comenzar siendo simple pero se va volviendo más complicada con el paso del tiempo. Una subclase a menudo depende de su generalización más estrechamente que una clase ajena, pero esto puede ser demasiado.

La clave es decidir entre lo que una clase es y lo que una clase tiene. La estructura de una clase suele comenzar con herencia y con el paso del tiempo se mueve más hacia la composición.

Los malos olores emergentes por un mal uso de la herencia son:

Refused Bequest

Síntomas

  • Una clase hereda de una generalización, pero lanza una excepción en lugar de dar soporte a un método (honest refusal).
  • Una clase hereda de una generalización, pero un método heredado no funciona cuando es llamado en dicha clase (implicit refusal).
  • Los clientes tienden a acceder a la clase más que manejar la generalización.
  • La herencia no tiene sentido. La subclase no es un ejemplo de la generalización.

Qué hacer

  • Dejarlo tal y como está, si no es confuso.
  • Si no hay motivos para compartir una relación de clases, utiliza Replace Inheritance with Delegation.
  • Si la relación generalización-subclase no tiene sentido, puedes crear una nueva subclase con Extract Subclass, Push Down Field y Push Down Method. Deja que esta nueva clase tenga el comportamiento no rechazado y cambia los clientes de la generalización que sean clientes de esta nueva clase. Con ello, la generalización no necesita mencionar dicho comportamiento y podrás eliminarlo de ella y de la subclase inicial. La generalización inicial pasa a ser una subclase de la nueva clase.

Recompensas

  • Mejora la comunicación.
  • Mejora la testeabilidad.

Contraindicaciones

A veces un caso de Refused Bequest es usado para prevenir una explosión de nuevos tipos.

Inappropiate Intimacy (Subclass Form)

Síntomas

Una clase accede a partes internas que deberían ser privadas de su generalización. Si esto ocurre entre clases separadas, se conoce como General Form.

Qué hacer

Si la subclase está accediendo a campos de la generalización de una forma incontrolada, utiliza Self Encapsulate Field.

Si la generalización puede definir un algoritmo general que pueda ser introducido por la subclase, entonces utiliza Form Template Method.

Si la generalización y la subclase necesitan estar aún más desacopladas, entonces utiliza Replace Inheritance with Delegation.

Recompensas

Reduce la duplicidad.

Suele mejorar la comunicación.

Puede reducir el tamaño.

Lazy Class

Síntomas

Una clase apenas tiene comportamiento. Sus generalizaciones, subclases o clientes realizan todo el trabajo aparentemente asociado y no hay suficiente comportamiento en la clase que justifique su existencia.

Qué hacer

  • Si sus generalizaciones o subclases parecen el lugar adecuado para albergar el comportamiento de dicha clase, almacénalo dentro de una de ellas con Colapse Hierarchy.
  • En caso contrario, encapsula su comportamiento dentro de su cliente con Inline Class.

Recompensas

  • Reduce el tamaño.
  • Mejora la comunicación.
  • Mejora la simplicidad.

Contraindicaciones

A veces una Lazy Class existe para comunicar una intención. Debes buscar el equilibrio entre comunicación y simplicidad.

Cuaderno: Refactoring. Malos olores entre clases: Datos

Introducción

Los DTOs son una oportunidad. Si los datos forman un buen conjunto, normalmente podemos encontrar un comportamiento que pertenezca a la clase.

Los malos olores resultantes de un mal uso de las estructuras de datos son:

Primitive Obsession

Síntomas

  • Uso de primitivos o casi primitivos (int, float, String, etc.).
  • Constantes y enumeraciones representando pequeños enteros.
  • Constantes de tipo String representando nombres de campos.

Qué hacer

Para Missing Class:

  • Revisar Data Clump, porque el primitivo suele ser encapsulado para localizar el problema.
  • Replace Data Value with Object para crear valores de datos de primera clase.

Para Simulated Types:

  • Si no hay comportamiento que sea condicional, entonces es más un enumerador, así que utiliza Replace Type Code with Class.
  • Si cambia o la clase ya tiene subclase, utiliza Replace Type Code with State/Strategy.

Para Simulated Field Accessors:

  • Si el primitivo es usado para tratar ciertos elementos array, Replace Array with Object.

Recompensas

  • Mejora la comunicación.
  • Puede exponer duplicidad.
  • Mejora la flexibilidad.
  • Suele exponer la necesidad de otras refactorizaciones.

Contraindicaciones

  • Suele haber problemas de rendimiento o dependencia que te detengan en localizarlo.
  • En ocasiones un Map suele ser utilizado en lugar de un objeto con campos fijos, utilizando los nombres de los campos como índices. Esto puede reducir el acoplamiento de la estructura de un objeto simple, pero con un coste en el rendimiento, la seguridad del tipado y la claridad.

Data Class

Síntomas

La clase consiste únicamente en datos públicos, o getters y setters. Esto hace al cliente depender de la mutabilidad y representación de la clase.

Qué hacer

  1. Encapsulate Field al bloque para sólo permitir el acceso a los campos mediante getters y setters.
  2. Remove Setting Methods para todos los métodos que puedas.
  3. Encapsulate Collection para eliminar el acceso directo a alguno de los campos de tipo colección.
  4. Mira en cada cliente del objeto. Probablemente encontrarás que los clientes acceden a los campos y manipulan los resultados, cuando lo debería estar haciendo la propia clase.
  5. Tras analizarlo verás que tienes muchos métodos similares en la clase. Utiliza entonces refactorizaciones como Rename Method, Extract Method, Add Parameter o Remove Parameter para armonizar las firmas y eliminar la duplicidad.
  6. No deberían de necesitarse más accesos a los campos porque los métodos movidos cubren su uso real. Utiliza Hide Method para eliminar el acceso a los getters y setters.

Recompensas

  • Mejora la comunicación.
  • Puede exponer duplicidad.

Contraindicaciones

  • En ocasiones el encapsulamiento de campos puede tener un coste en el rendimiento.
  • Algunos mecanismos de persistencia se basan en los métodos getters/setters para definir cuales serán los campos de los que obtendrán los datos a cargar o guardar. (Ver Mementos, Gamma’s Design Patterns). También se puede encapsular esta clase en otra contenedora.

Data Clump

Síntomas

  • Los mismos dos o tres elementos aparecen juntos frecuentemente en clases y listas de parámetros.
  • El código declara grupos de campos y métodos juntos dentro de la clase.
  • Los grupos o nombres de campos empiezan o terminan de forma similar.

Qué hacer

  • Si los elementos son campos en una clase, utiliza Extract Class para llevarlos a una nueva clase.
  • Si los valores están juntos en la firma de un método, utiliza Introduce Parameter Object para extraer el nuevo objeto.
  • Mira las llamadas que pasan los elementos desde el nuevo objeto para ver si puedes aplicarles Preserve Whole Object.
  • Mira los usos de los elementos. Suelen haber oportunidades de utilizar Move Method y otras refactorizaciones para mover esos usos al nuevo objeto, identificando Data Class.

Recompensas

  • Mejora la comunicación.
  • Puede exponer duplicidad.
  • Suele reducir el tamaño.

Contraindicaciones

  • Ocasionalmente, pasar un objeto completo puede introducir una dependencia. En este caso pasa únicamente las piezas que necesites.
  • Puede repercutir negativamente en el rendimiento.

Cuaderno: Refactoring. Malos olores dentro de una clase: Lógica condicional

Introducción

  • Es difícil de razonar ya que tenemos que considerar múltiples caminos a través del código.
  • Es tentador añadir casos de uso especiales en lugar de desarrollar un caso de uso general.
  • A veces es usada como un mal sustituto de mecanismos orientados a objetos.

Los malos olores derivados de un mal uso de la lógica condicional son:

Null Check

Qué hacer

  • Si hay un valor por defecto razonable, utilizalo.
  • Sino, introduce un Null Object para crear un valor por defecto que puedas usar.

Recompensas

  • Reduce la duplicación.
  • Reduce los errores lógicos y excepciones.

Contraindicaciones

  • Si el null ocurre en un único lugar, no vale la pena aislarlo en un Null Object.
  • Los Null Objects necesitan tener un comportamiento seguro para los métodos que aportan. Si no puedes asegurarlo, no los utilices.
  • Ten cuidado con los casos donde null significa más de una cosa en contextos diferentes. Puedes querer reemplazarlos por más de un tipo de Null Object.

Complicated Boolean Expression

Qué hacer

Aplica la ley de DeMorgan.

Recompensas

Mejora la comunicación.

Contraindicaciones

Puede que encuentres otras formas de simplificar la expresión o que al reescribirla comuniques más con menos código.

Special Case

Síntomas

  • Declaraciones complejas de if.
  • Comprobación de valores particulares antes de hacer algo (especialmente comparaciones con constantes o enumeraciones).

Qué hacer

  • Si los condicionales están reemplazando un polimorfismo, utiliza Replace Conditional with Polymorphism.
  • Si el if y el then son similares, quizá quieras reescribirlos para que el mismo fragmento de código pueda generar los resultados adecuados para cada caso y eliminar el condicional.

Recompensas

  • Mejora la comunicación.
  • Puede exponer duplicidad.

Contraindicaciones

  • No puedes eliminar el caso base de una expresión recursiva.
  • A veces un if es simplemente la mejor forma de hacer algo.

Simulated Inheritance (Switch Statement)

Síntomas

  • El código utiliza un switch (especialmente en un type field).
  • El código tiene una serie de if en una línea (especialmente si comparan contra el mismo valor).
  • El código usa instanceof (o equivalente) para decidir con qué tipo está trabajando.

Qué hacer

No simules herencia. Utiliza mecanismos nativos del propio lenguaje.

Si un switch para la misma condición aparece en diferentes sitios, normalmente está utilizando un type code. Reemplázalo con polimorfismo:

  1. Usa Extract Method. Saca fuera el código para cada rama.
  2. Move Method. Mueve el código a la clase correcta.
  3. Replace Type Code with Subclass o Replace Type Code with State/Strategy. Configura la estructura de herencia.
  4. Replace Conditional with Polymorphism. Elimina los condicionales.

Si las condiciones ocurren dentro de una única clase, puedes reemplazar la lógica condicional con Replace Parameter with Explicit Method o Introduce Null Object.

Recompensas

  • Mejora la comunicación.
  • Puede exponer duplicidad.

Contraindicaciones

  • A veces un switch es la forma más simple de expresar una lógica. Si está haciendo algo simple en un único sitio, no hace falta que lo aisles en una clase a parte. Esto suele ocurrir en puntos del sistema que se comuniquen con partes no orientadas a objetos (DTOs).
  • Un único switch puede ser utilizado en un patrón Factory o Abstract Factory.
  • Un switch puede ser utilizado en varios lugares relacionados para controlar una máquina de estados. Decide entonces si tiene más sentido utilizar el switch o el patrón State.

Cuaderno: Refactoring. Malos olores dentro de una clase: Duplicación

Introducción

La duplicación causa los siguientes problemas:

  • Hay más código que mantener.
  • Las partes que varían están enterradas bajo las partes que se mantienen fijas.
  • Variaciones en el código a menudo esconden similitudes más profundas.
  • Hay tendencia a reparar un bug en un lugar y dejar otros idénticos sin reparar en otro sitio.

La duplicación del código suele ser síntoma de varios malos olores, como son:

Magic Number

Qué hacer

Utiliza Replace Magic Number with Symbolic Constant para el valor específico.

Si los valores son strings, puede que te interese aislarlos en alguna estructura (clase wrapper, maps, etc.)

Recompensas

Reduce la duplicidad.

Mejora la comunicación.

Contraindicaciones

Los tests suelen ser más legibles cuando simplemente utilizan variables.

Duplicated Code

Síntomas

  • Fácil de ver: Dos fragmentos de código son prácticamente idénticos.
  • Difícil de ver: Dos fragmentos de código tienen prácticamente el mismo efecto.

Qué hacer

Si la duplicación se encuentra dentro de uno o dos métodos de la misma clase, utiliza Extract Method para mover la parte común a un método separado.

Si la duplicación se encuentra entre dos clases gemelas, utiliza Extract Method para crear una rutina única y luego usa Pull Up Field y/o Pull Up Method para juntar las partes comunes. Luego podrás utilizar Form Template Method para crear un algoritmo común en el padre y pasos únicos en los hijos.

Si la duplicación se encuentra en dos clases no relacionadas, extrae la parte común en una nueva clase con Extract Class, o identifica si el olor es Feature Envy y por tanto el código duplicado pertenece únicamente a una de las dos clases.

Si en ambos lugares el código no es idéntico pero tiene el mismo efecto, decide cual es mejor y utiliza Substitute Algorithm para quedarte con una única copia.

Recompensas

  • Reduce la duplicación.
  • Reduce el tamaño.
  • Puede llevar a una mejor abstracción y mayor flexibilidad.

Contraindicaciones

  • En raras ocasiones puedes concluir que la duplicación es mejor para comunicar la intención y mantenerla.
  • Puedes tener duplicación que es mera coincidencia y reducirla puede confundir a quien lo lea.

Alternative Classes with Different Interfaces

Síntomas

Dos clases parecen estar haciendo el mismo trabajo pero tienen nombres de métodos distintos.

Qué hacer

  1. Utiliza Rename Method para dejarlos similares.
  2. Utiliza Move Method, Add Parameter y Parameterize Method para hacer los protocolos (firmas y enfoques) similares.
  3. Si ambas clases son similares pero no identicas, utiliza Extract Superclass una vez que ambas estén en concordancia.
  4. Elimina la clase extra si es posible.

Recompensas

  • Reduce la duplicación.
  • Puede reducir el tamaño.
  • Puede mejorar la comunicación.

Contraindicaciones

En ocasiones las dos clases no pueden ser cambiadas (si están en librerías diferentes, por ejemplo). Cada librería puede tener su propia visión para un mismo concepto, pero puedes verte sin una buena forma de unificarlos.

Cuaderno: Refactoring. Malos olores dentro de una clase: Complejidad innecesaria

La complejidad innecesaria del código puede presentarse en los siguientes malos olores:

Introducción

Sigue el principio YAGNI (You Aren’t Gonna Need It).

Dead Code

Qué hacer

Elimina el código no utilizado y los tests asociados.

Recompensas

  • Reduce el tamaño.
  • Mejora la comunicación.
  • Mejora la simplicidad.

Contraindicaciones

No elimines código que pueda ser utilizado para dar soporte a clientes aunque no sea utilizado dentro de tu framework.

Speculative Generality

Qué hacer

  • Para clases innecesarias:
    • Si los padres o hijos de la clase parecen el sitio correcto para el comportamiento, mételo dentro de uno de ellos con Colapse Hierarchy.
    • En otro caso, mételo dentro del caller con Inline Class.
  • Para métodos innecesarios, utiliza Inline Method o Remove Method.
  • Para un campo innecesario, asegúrate de que no hay referencias al mismo y elimínalo.
  • Para un parámetro innecesario, utiliza Remove Parameter.

Recompensas

  • Reduce el tamaño.
  • Mejora la comunicación.
  • Mejora la simplicidad.

Contraindicaciones

  • No elimines código que pueda ser utilizado para dar soporte a clientes aunque no sea utilizado dentro de tu framework.
  • Si algunos elementos son utilizados por los tests y le dan a este información privilegiada sobre la clase, puede ser indicativo de que no estás teniendo en cuenta una abstracción que puedas testear de forma independiente.