¿El principio de segregación de interfaz se aplica a métodos concretos?

10

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?

ggrr
fuente
2
¿Usualmente un automóvil sabe cuándo pasa la "calidad"? ¿O tal vez es una regla de negocios que podría encapsularse por sí misma?
Laiv
2
Como la palabra interfaz en ISP implica que se trata de interfaces . Entonces, si tiene un método en su Carclase que no desea que (todos) los usuarios conozcan, cree (más de una) interfaz que la Carclase implemente y que declare solo métodos útiles en el contexto de las interfaces.
Timothy Truckle
@Laiv Estoy seguro de que pronto veremos vehículos que saben mucho más que eso. ;)
sándwich de modelado unificado
1
Un automóvil sabrá lo que su fabricante quiere que él sepa. Volkswagen sabe de lo que estoy hablando :-)
Laiv
1
Menciona una interfaz, pero no hay ninguna interfaz en su ejemplo. ¿Estamos hablando de convertir Car en una interfaz y qué métodos incluir en dicha interfaz?
Neil

Respuestas:

6

En su ejemplo, CarShopno depende isQualityPassy 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 como isQualityPasssea ​​un método que se ajuste bien al Carobjeto, 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 Cardependa directamente de algo así HttpClientprobablemente tampoco sea una buena idea, independientemente de qué clientes usen o no el método. Mover la lógica a una clase separada CheckCarQualityPasssimplemente 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 isQualityPassno se puede mover fácilmente a otra clase, la alternativa sería hacer las Httpllamadas a través de una interfaz abstracta IHttpClientque se inyecta en el Carmomento de la construcción, o inyectando toda la estrategia de verificación "QualityPass" (con la solicitud Http encapsulada) en el Carobjeto . Pero esa es, en mi humilde opinión, la segunda mejor solución, ya que aumenta la complejidad general en lugar de reducirla.

Doc Brown
fuente
¿Qué pasa con el patrón de estrategia para resolver el método isQualityPass?
Laiv
@Laiv: técnicamente, esto funcionará, claro (ver mi edición), pero dará como resultado un Carobjeto 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é.
Doc Brown
6

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?

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.

public class CarShop{
    ...
    public int getCarPrice(int carId){
        Car car=carList[carId];
        int price=car.getTax() + car.getCost()... (some formula);
        return price;
    }
}

Lo que se usa aquí es getTax()y getCost(). En lo que se insiste es en todo lo accesible Car. El problema es insistir en Carsignifica que insiste en el acceso al isQualityPass()cual no es necesario.

Esto se puede arreglar. Usted pregunta si se puede arreglar concretamente. Puede.

public class CarShop{
    ...
    public int getCarPrice(int carId){
        CarLiability carLiability=carLiabilityList[carId];
        int price=carLiability.getTax() + carLiability.getCost()... (some formula);
        return price;
    }
}

Ninguno de ese código sabe si CarLiabilityes una interfaz o una clase concreta. Eso es bueno. No quiere saberlo.

Si es una interfaz, Carpodría implementarlo. Esto no violaría a ISP porque aunque isQuality()está adentro Car CarShopno 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 CarLiabilitytrata de una envoltura de concreto Carque le delega trabajo. Mientras CarLiabilityno se exponga, isQuality()entonces CarShopestá bien. Por supuesto, esto simplemente patea la lata en el camino y CarLiabilitytiene que descubrir cómo seguir al ISP de Carla misma manera que CarShoptenía que hacerlo.

En resumen, isQuality()no es necesario eliminarlo Cardebido a ISP. La necesidad implícita de isQuality()necesidades debe eliminarse CarShopporque CarShopno la necesita, por lo que no debe solicitarla.

naranja confitada
fuente
4

¿El principio de segregación de interfaz se aplica a métodos concretos?

¿Pero qué hay de los métodos concretos? ¿Debo separar los métodos que no todos los clientes usarían?

Realmente no. Hay diferentes maneras de ocultar Car.isQualityPassa CarShop.

1. Modificadores de acceso

Desde el punto de vista de la Ley de Deméter , podríamos considerar Cary CardShopno ser amigos . Nos legitima para hacer lo siguiente.

package com.my.package.domain.model
public class Car{
    ...
    protected boolean isQualityPass(){...}
}

package com.my.package.domain.services
public class CarShop{
    ...
}

Tenga en cuenta que ambos componentes están en paquetes diferentes. Ahora CarShopno tiene visibilidad sobre comportamientos Car 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 Carimplementación real , nada nos impide practicar ISP.

//role interfaces 
public interface Billable{
   public int getCosts();
   public int getTaxs();
}

//role interfaces
public interface QualityAssurance{
   public boolean isQualityPass();
}

public class Car implements Billable, QualityAssurance{
   ...
}

public class CarShop {
  ...
  public int getPrice(Billable billable){
     return billable.getCosts() * billable.getTaxs();
  }
}

Lo que he hecho aquí. He reducido la interacción entre Carya CarShoptravés de la interfaz de rol Facturable . Tenga en cuenta el cambio en la getPricefirma. He modificado intencionalmente el argumento. Quería hacer obvio que CarShopsolo 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 el getPrice(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 Cary HttpClientes 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

Laiv
fuente