El hecho de que un sistema sea complejo no significa que deba complicarlo . Si tiene una clase que tiene demasiadas dependencias (o Colaboradores) como esta:
public class MyAwesomeClass {
public class MyAwesomeClass(IDependency1 _d1, IDependency2 _d2, ... , IDependency20 _d20) {
// Assign it all
}
}
... entonces se volvió demasiado complicado y realmente no estás siguiendo SRP , ¿verdad? Apuesto a que si anota lo que MyAwesomeClasshace en una tarjeta CRC , no cabría en una tarjeta índice o si tiene que escribir en letras minúsculas ilegibles.
Lo que tienes aquí es que tus muchachos solo siguieron el Principio de segregación de interfaz y pueden haberlo llevado al extremo, pero esa es otra historia. Se podría argumentar que las dependencias son objetos de dominio (lo que sucede), sin embargo, tener una clase que maneje 20 objetos de dominio al mismo tiempo es demasiado extenso.
TDD le proporcionará un buen indicador de cuánto hace una clase. Sin rodeos puesto; Si un método de prueba tiene un código de configuración que tarda una eternidad en escribir (incluso si refactoriza las pruebas), entonces MyAwesomeClassprobablemente tenga demasiadas cosas que hacer.
Entonces, ¿cómo se resuelve este enigma? Mueves las responsabilidades a otras clases. Hay algunos pasos que puede seguir en una clase que tiene este problema:
- Identifique todas las acciones (o responsabilidades) que su clase hace con sus dependencias.
- Agrupe las acciones según dependencias estrechamente relacionadas.
- Redelegar! Es decir, refactorizar cada una de las acciones identificadas a nuevas o (más importante) otras clases.
Un ejemplo abstracto sobre refactorizar responsabilidades
Vamos a Cser una clase que tiene varias dependencias D1, D2, D3, D4que es necesario perfeccionar por usar menos. Cuando identificamos los métodos que Crequieren las dependencias, podemos hacer una lista simple:
D1- performA(D2),performB()
D2 - performD(D1)
D3 - performE()
D4 - performF(D3)
Mirando la lista podemos ver eso D1y D2estamos relacionados entre sí ya que la clase los necesita juntos de alguna manera. También podemos ver que las D4necesidades D3. Entonces tenemos dos agrupaciones:
Group 1- D1<->D2
Group 2- D4->D3
Las agrupaciones son un indicador de que la clase ahora tiene dos responsabilidades.
Group 1- Uno para manejar la llamada de dos objetos que se necesitan mutuamente. Tal vez pueda dejar que su clase Celimine la necesidad de manejar ambas dependencias y dejar que una de ellas maneje esas llamadas. En esta agrupación, es obvio que D1podría tener una referencia D2.
Group 2- La otra responsabilidad necesita un objeto para llamar a otro. No se puede D4manejar en D3lugar de su clase? Entonces, probablemente podemos eliminar D3de la clase Cdejando en su lugar D4hacer las llamadas.
No tome mi respuesta tal como está escrita, ya que el ejemplo es muy abstracto y hace muchas suposiciones. Estoy bastante seguro de que hay más formas de refactorizar esto, pero al menos los pasos pueden ayudarlo a obtener algún tipo de proceso para mover las responsabilidades en lugar de dividir las clases.
Editar:
Entre los comentarios @Emmad Karem dice:
"Si su clase tiene 20 parámetros en el constructor, no parece que su equipo sepa qué es SRP. Si tiene una clase que solo hace una cosa, ¿cómo tiene 20 dependencias?" - Creo que si usted tener una clase Customer, no es extraño tener 20 parámetros en el constructor.
Es cierto que los objetos DAO tienden a tener muchos parámetros, que debe establecer en su constructor, y los parámetros generalmente son tipos simples como cadena. Sin embargo, en el ejemplo de una Customerclase, aún puede agrupar sus propiedades dentro de otras clases para simplificar las cosas. Como tener una Addressclase con calles y una Zipcodeclase que contenga el código postal y que también maneje la lógica comercial, como la validación de datos:
public class Address {
private String street1;
//...
private Zipcode zipcode;
// easy to extend
public bool isValid() {
return zipcode.isValid();
}
}
public class Zipcode {
private string zipcode;
public bool isValid() {
// return regex match that zipcode contains numbers
}
}
Esto se trata más adelante en la publicación del blog "Nunca, nunca, nunca use String en Java (o al menos a menudo)" . Como alternativa al uso de constructores o métodos estáticos para hacer que los subobjetos sean más fáciles de crear, puede usar un patrón de generador de fluidos .