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 MyAwesomeClass
hace 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 MyAwesomeClass
probablemente 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 C
ser una clase que tiene varias dependencias D1
, D2
, D3
, D4
que es necesario perfeccionar por usar menos. Cuando identificamos los métodos que C
requieren 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 D1
y D2
estamos relacionados entre sí ya que la clase los necesita juntos de alguna manera. También podemos ver que las D4
necesidades 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 C
elimine la necesidad de manejar ambas dependencias y dejar que una de ellas maneje esas llamadas. En esta agrupación, es obvio que D1
podría tener una referencia D2
.
Group 2
- La otra responsabilidad necesita un objeto para llamar a otro. No se puede D4
manejar en D3
lugar de su clase? Entonces, probablemente podemos eliminar D3
de la clase C
dejando en su lugar D4
hacer 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 Customer
clase, aún puede agrupar sus propiedades dentro de otras clases para simplificar las cosas. Como tener una Address
clase con calles y una Zipcode
clase 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 .