Como el principio de segregación de interfaz sugiere que ningún cliente debería verse obligado a depender de métodos que no utiliza, por lo que un cliente no debe implementar un método vacío para sus métodos de interfaz, de lo contrario, este método de interfaz debe colocarse en otra interfaz.
¿Pero qué hay de los métodos concretos? ¿Debo separar los métodos que no todos los clientes usarían? Considere la siguiente clase:
public class Car{
....
public boolean isQualityPass(){
...
}
public int getTax(){
...
}
public int getCost(){
...
}
}
public class CarShop{
...
public int getCarPrice(int carId){
Car car=carList[carId];
int price=car.getTax() + car.getCost()... (some formula);
return price;
}
}
en el código anterior, CarShop no utiliza el método isQualityPass () en Car, si separo isQualityPass () en una nueva clase:
public class CheckCarQualityPass{
public boolean isQualityPass(Car car){
}
}
para reducir el acoplamiento de CarShop? Porque creo que una vez si isQualityPass () necesita dependencia adicional, por ejemplo:
public boolean isQualityPass(){
HttpClient client=...
}
CarShop dependería de HttpClient incluso si nunca usa HttpClient en realidad. Entonces mi pregunta es: de acuerdo con el principio de segregación de interfaz, ¿debería separar métodos concretos que no todos los clientes usarían, de modo que esos métodos dependan del cliente solo cuando el cliente realmente lo use, para reducir el acoplamiento?
fuente
Car
clase que no desea que (todos) los usuarios conozcan, cree (más de una) interfaz que laCar
clase implemente y que declare solo métodos útiles en el contexto de las interfaces.Respuestas:
En su ejemplo,
CarShop
no dependeisQualityPass
y no está obligado a realizar una implementación vacía para un método. Ni siquiera hay una interfaz involucrada. Entonces, el término "ISP" simplemente no coincide aquí. Y siempre que un método comoisQualityPass
sea un método que se ajuste bien alCar
objeto, sin sobrecargarlo con responsabilidades o dependencias adicionales, está bien. No es necesario refactorizar un método público de una clase en otro lugar solo porque existe un cliente que no usa el método.Sin embargo, hacer que una clase de dominio
Car
dependa directamente de algo asíHttpClient
probablemente tampoco sea una buena idea, independientemente de qué clientes usen o no el método. Mover la lógica a una clase separadaCheckCarQualityPass
simplemente no se llama "ISP", esto se llama "separación de preocupaciones" . La preocupación de un objeto de automóvil reutilizable probablemente no debería ser hacer ninguna llamada HTTP externa, al menos no directamente, esto limita la reutilización y, además, la comprobabilidad demasiado.Si
isQualityPass
no se puede mover fácilmente a otra clase, la alternativa sería hacer lasHttp
llamadas a través de una interfaz abstractaIHttpClient
que se inyecta en elCar
momento de la construcción, o inyectando toda la estrategia de verificación "QualityPass" (con la solicitud Http encapsulada) en elCar
objeto . Pero esa es, en mi humilde opinión, la segunda mejor solución, ya que aumenta la complejidad general en lugar de reducirla.fuente
Car
objeto más complejo . No sería mi primera opción para una solución (al menos no en el contexto de este ejemplo artificial). Sin embargo, puede tener más sentido en el código "real", no lo sé.El principio de segregación de interfaz no se trata de impedir el acceso a lo que no necesita. Se trata de no insistir en el acceso a lo que no necesita.
Las interfaces no son propiedad de la clase que las implementa. Son propiedad de los objetos que los usan.
Lo que se usa aquí es
getTax()
ygetCost()
. En lo que se insiste es en todo lo accesibleCar
. El problema es insistir enCar
significa que insiste en el acceso alisQualityPass()
cual no es necesario.Esto se puede arreglar. Usted pregunta si se puede arreglar concretamente. Puede.
Ninguno de ese código sabe si
CarLiability
es una interfaz o una clase concreta. Eso es bueno. No quiere saberlo.Si es una interfaz,
Car
podría implementarlo. Esto no violaría a ISP porque aunqueisQuality()
está adentroCar
CarShop
no insiste en ello. Esto esta bien.Si es concreto, es posible que
isQuality()
no exista o se haya mudado a otro lugar. Esto esta bien.También podría ser que se
CarLiability
trata de una envoltura de concretoCar
que le delega trabajo. MientrasCarLiability
no se exponga,isQuality()
entoncesCarShop
está bien. Por supuesto, esto simplemente patea la lata en el camino yCarLiability
tiene que descubrir cómo seguir al ISP deCar
la misma manera queCarShop
tenía que hacerlo.En resumen,
isQuality()
no es necesario eliminarloCar
debido a ISP. La necesidad implícita deisQuality()
necesidades debe eliminarseCarShop
porqueCarShop
no la necesita, por lo que no debe solicitarla.fuente
Realmente no. Hay diferentes maneras de ocultar
Car.isQualityPass
aCarShop
.1. Modificadores de acceso
Desde el punto de vista de la Ley de Deméter , podríamos considerar
Car
yCardShop
no ser amigos . Nos legitima para hacer lo siguiente.Tenga en cuenta que ambos componentes están en paquetes diferentes. Ahora
CarShop
no tiene visibilidad sobre comportamientosCar
protegidos . (Disculpe de antemano si el ejemplo anterior parece tan simplista).2. Segregación de interfaz
El ISP funciona desde la premisa de que trabajamos con abstracciones, no con clases concretas. Asumiré que ya está familiarizado con la implementación del ISP y con las interfaces de roles .
A pesar de la
Car
implementación real , nada nos impide practicar ISP.Lo que he hecho aquí. He reducido la interacción entre
Car
yaCarShop
través de la interfaz de rol Facturable . Tenga en cuenta el cambio en lagetPrice
firma. He modificado intencionalmente el argumento. Quería hacer obvio queCarShop
solo está "vinculado / vinculado" a una de las interfaces de roles disponibles. Podría haber seguido la implementación real pero no conozco los detalles de la implementación real y me temo que elgetPrice(String carId)
acceso real (visibilidad) sobre la clase concreta. Si es así, todo el trabajo realizado con el ISP se vuelve inútil porque está en manos del desarrollador hacer el casting y trabajar solo con la interfaz Facturable . No importa lo metódicos que seamos, la tentación siempre estará ahí.3. Responsabilidad individual
Me temo que no estoy en condiciones de decir si la dependencia entre
Car
yHttpClient
es adecuada, pero estoy de acuerdo con @DocBrown, plantea algunas advertencias que merecen una revisión de diseño. Ni la Ley de Demeter ni el ISP harán que su diseño sea "mejor" en este momento. Simplemente enmascararán el problema, no lo solucionarán.He sugerido DocBrown el Patrón de estrategia como una posible solución. Estuve de acuerdo con él en que el patrón agrega complejidad, pero también creo que cualquier rediseño lo hará. Es una compensación, cuanto más desacoplamiento queremos, más partes móviles tenemos (generalmente). De todos modos, creo que ambos están de acuerdo con que un nuevo diseño es muy recomendable.
Resumiendo
No, no necesita mover métodos concretos a clases externas para no hacerlos accesibles. Podría haber innumerables consumidores. ¿Moverías todos los métodos concretos a clases externas cada vez que un nuevo consumidor entra en juego? Espero que no
fuente