接口隔离原则是否适用于具体方法?


10

由于接口隔离原则表明,不应强迫任何客户端依赖其不使用的方法,因此客户端不应为其接口方法实现空方法,否则应将此接口方法放入另一个接口。

但是具体方法呢?我应该分开不是每个客户都会使用的方法吗?考虑以下类别:

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;
    }
}

在上面的代码中,CarShop根本不在Car中使用方法isQualityPass(),我应该将isQualityPass()分离到一个新类中:

public class CheckCarQualityPass{
    public boolean isQualityPass(Car car){
    }
}

为了减少CarShop的耦合?因为我认为如果isQualityPass()需要额外的依赖,例如:

public boolean isQualityPass(){
    HttpClient client=...
}

即使实际上从未使用HttpClient,CarShop也将依赖HttpClient。所以我的问题是:根据接口隔离原则,我是否应该分离并非每个客户端都会使用的具体方法,以便这些方法仅在客户端实际使用时才依赖客户端,以减少耦合?


2
通常,汽车是否知道何时通过“质量”检查?还是可能自己封装的业务规则?
Laiv

2
由于字接口在ISP意味着它是关于接口。因此,如果您的Car类中有一个不希望(所有)用户知道的方法,则创建(多个)该类实现的接口,该接口Car仅声明在接口上下文中有用的方法。
蒂莫西·卡特尔

@Laiv我敢肯定,我们很快就会看到车辆的知识。;)
统一建模三明治

1
汽车将知道制造商希望他知道的东西。大众知道我在说什么:-)
Laiv

1
您提到了一个接口,但是您的示例中没有接口。我们是在谈论将Car变成一个接口以及在该接口中包括哪些方法?
尼尔

Answers:


6

在您的示例中,CarShop不依赖isQualityPass,并且不被迫为方法创建空实现。甚至没有接口。因此,术语“ ISP”在这里根本不匹配。并且只要像这样isQualityPass的方法非常适合该Car对象,而又不会给它增加额外的责任或依赖性,就可以了。无需仅将一个类的公共方法重构到另一个地方,因为存在一个客户端未使用该方法。

但是,不管哪个客户端使用或不使用该方法,使像这样的域类Car直接依赖于类似的东西HttpClient可能也不是一个好主意。CheckCarQualityPass只是将逻辑移到一个单独的类中不称为“ ISP”,这称为“关注点分离”。可重用汽车对象的关注点可能不应该是至少至少不是直接进行任何外部HTTP调用,这会限制可重用性,而且测试性也太多。

如果isQualityPass不能轻松地移到另一个类,则替代方法是Http通过在构造时IHttpClient注入的抽象接口进行调用Car,或者通过将整个“ QualityPass”检查策略(封装了Http请求)注入Car对象中。但是,恕我直言,这只是第二好的解决方案,因为它增加了整体复杂性而不是降低了复杂性。


如何解决isQualityPass方法的策略模式?
Laiv

@Laiv:从技术上讲,这肯定可以(请参阅我的编辑),但是它将导致Car对象更复杂。这不是解决方案的第一选择(至少不是在这个人为的示例中)。但是,我不知道在“真实”代码中可能更有意义。
布朗

6

所以我的问题是:根据接口隔离原则,我是否应该分离并非每个客户端都会使用的具体方法,以便这些方法仅在客户端实际使用时才依赖客户端,以减少耦合?

接口隔离原则并不是要禁止访问不需要的内容。这是关于不坚持访问您不需要的内容。

接口不归于实现它们的类所拥有。它们归使用它们的对象所拥有。

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

这里使用的是getTax()getCost()。坚持要求的是可以通过访问所有内容Car。问题是坚持Car意味着它坚持要访问isQualityPass()不需要的东西。

这可以解决。您询问是否可以具体固定。它可以。

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

该代码甚至都不知道CarLiability是接口还是具体的类。这是好事。它不想知道。

如果是接口,则Car可以实现它。这不会违反ISP,因为即使isQuality()在ISP 中Car CarShop也没有坚持使用它。这可以。

如果是具体的,则可能是isQuality()不存在或已移至其他地方。这可以。

也可能CarLiability是一个具体的包装Car,将工作委派给它。只要CarLiability不暴露isQuality()CarShop可以了。当然,这只是开罐的道路,CarLiability必须弄清楚如何以必须采取Car的相同方式遵循ISP CarShop

简而言之,isQuality()不需要Car因为ISP 而被删除。不需要的隐含isQuality()需求CarShop因为CarShop不需要而被删除,因此它不应该要求它。


4

接口隔离原则是否适用于具体方法?

但是具体方法呢?我应该分开不是每个客户都会使用的方法吗?

并不是的。有不同的方法来隐藏Car.isQualityPassCarShop

1.访问修饰符

从得墨meter耳定律的角度来看,我们可以考虑CarCardShop不是成为朋友。它使我们有资格做下一个。

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

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

请注意,两个组件都位于不同的程序包中。现在CarShopCar 受保护的行为没有可见性。(如果上面的例子看起来很简单,请事先打扰一下)。

2.接口隔离

ISP的前提是,我们用抽象的工作,而不是与具体类的作品。我将假定您已经熟悉ISP的实现和角色接口

尽管有实际的Car实现方式,但没有什么阻止我们实践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();
  }
}

我在这里做了什么。我缩小了角色接口Billable之间的交互,CarCarShop通过角色接口Billable进行了缩小。请注意getPrice签名的更改。我故意修改了论点。我想弄清楚,这CarShop只是“捆绑/绑定”了可用的角色接口之一。我可以跟踪实际的实现,但是我不知道实际的实现细节,而且我担心实际getPrice(String carId)可以访问具体类。如果有的话,那么与ISP一起完成的所有工作将变得无用,因为开发人员只能进行铸造工作,并且只能与Billable接口一起工作。无论我们多么有条理,诱惑总是存在。

3.单一责任

恐怕我无法说出Car和之间的依赖关系HttpClient是否足够,但是我同意@DocBrown的观点,它提出了一些警告,值得进行设计审查。此时,得墨meter耳定律和ISP都不会使您的设计“更好”。他们只会掩盖问题,而不是解决问题。

我建议将DocBrown 策略模式作为可能的解决方案。我同意他的观点,即这种模式会增加复杂性,但我也认为任何重新设计都可以。这是一个权衡,我们想要的去耦越多,我们拥有的零件就越多(通常)。无论如何,我认为双方都同意重新设计是非常可取的。

加起来

不,您不需要将具体方法移至外部类,因为它们不会被访问。可能有无数的消费者。每次有新用户使用时,您是否会将所有具体方法移至外部类?我希望你不要。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.