El hecho de que un sistema sea complejo no significa que tenga que hacerlo complicado . Si tienes 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? Apostaría si escribiera lo que MyAwesomeClass
hace en una tarjeta CRC que no encajaría en una tarjeta de índice o tiene que escribir en letras ilegibles muy pequeñas.
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. Podría argumentar que las dependencias son objetos de dominio (lo que ocurre); sin embargo, tener una clase que maneje 20 objetos de dominio al mismo tiempo lo está estirando demasiado.
TDD le proporcionará un buen indicador de cuánto hace una clase. Poner bruscamente Si un método de prueba tiene un código de configuración que tarda mucho tiempo en escribir (incluso si refactoriza las pruebas), es probable que su MyAwesomeClass
tenga demasiadas cosas que hacer.
Entonces, ¿cómo resuelves este enigma? Mueve 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 realiza su clase con sus dependencias.
- Agrupe las acciones según dependencias estrechamente relacionadas.
-
Redelegate! I.e. refactorice cada una de las acciones identificadas a clases nuevas o, lo que es más importante, a otras.
Un ejemplo abstracto sobre las responsabilidades de refactorización
Deje que C
sea una clase que tenga varias dependencias D1
, D2
, D3
, D4
que necesita refactorizar para usar menos. Cuando identificamos qué métodos llama C
a las dependencias, podemos hacer una lista simple de ellos:
-
D1
- performA(D2)
, performB()
-
D2
- performD(D1)
-
D3
- performE()
-
D4
- performF(D3)
Mirando la lista, podemos ver que D1
y D2
están relacionados entre sí ya que la clase los necesita juntos de alguna manera. También podemos ver que D4
necesita D3
. Así que tenemos dos agrupaciones:
-
Group 1
- D1
< - > %código%
-
D2
- Group 2
- > %código%
Las agrupaciones son un indicador de que la clase ahora tiene dos responsabilidades.
-
D4
: uno para manejar los objetos que llaman dos que se necesitan entre sí. Quizás pueda dejar que su clase D3
elimine la necesidad de manejar ambas dependencias y que una de ellas maneje esas llamadas en su lugar. En esta agrupación, es obvio que Group 1
podría tener una referencia a C
.
-
D1
- La otra responsabilidad necesita un objeto para llamar a otro. ¿No puede D2
manejar Group 2
en lugar de tu clase? Entonces, probablemente podamos eliminar D4
de la clase D3
permitiendo que D3
haga las llamadas en su lugar.
No tomes mi respuesta como está escrita en piedra, 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 podrían 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 suena como si su equipo supiera qué es SRP. Si tiene una clase que solo hace una cosa, ¿cómo tiene 20 dependencias?", creo que si tienes una clase de Cliente, 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 configurar en su constructor, y los parámetros generalmente son tipos simples como cadenas. Sin embargo, en el ejemplo de una clase C
, aún puede agrupar sus propiedades dentro de otras clases para simplificar las cosas. Como tener una clase D4
con calles y una clase Customer
que contiene el código postal y manejará la lógica de negocios como la validación de datos también:
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 detalladamente 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 utilizar un patrón de generador de fluidos .