Marnner Back-end Dev Engineer

接口隔离原则

2017-09-28
Marnner
OOP

Interface Segregation Principle

Interface Segregation Principle

接口形成了Java编程语言的核心部分,它们被广泛应用于企业应用程序中,以实现抽象,并支持类型的多个继承 - 类实现多个接口的能力。从编码的角度来看,编写接口很简单。您可以使用interface关键字来创建一个接口并在其中声明方法。其他类可以使用该接口与implements关键字,然后提供已声明方法的实现。作为Java程序员,您必须编写大量的接口,但关键问题是您是否在保留设计原则的同时编写它们?编写接口时要遵循的设计原则是接口隔离原则。

接口隔离原则代表面向对象编程的五个SOLID原理的“I”,用于编写更易于阅读,可维护,易于升级和修改的精心设计的代码。敏捷软件开发:原理,模式和实践,他在2002年的书中提到,这一原则首先被罗伯特·马丁(Robert C. Martin)用来咨询施乐。这个原则指出,“客户不应该被迫依赖于他们不使用的方法”。这里,术语“客户端”是指接口的实现类。

接口隔离原理说的是,您的接口不应该随着实现类不需要的方法而膨胀。对于这样的接口,也称为“胖接口”,即使对于他们不需要的方法,实现类也不必要地被迫提供实现(虚拟/空)。此外,实现类在接口变化时可能会发生变化。方法签名的方法或更改的添加需要修改所有实现类,即使其中一些不使用该方法。

接口隔离原则主张将“胖接口”分为更小和高度凝聚力的接口,称为“角色接口”。每个“角色接口”都声明一个或多个特定行为的方法。因此,客户端而不是实现“胖接口”,只能实现方法与它们相关的那些“角色接口”。

接口隔离原则违规(错误示例)

考虑构建不同类型玩具的应用程序的要求。每个玩具都会有一个价格和颜色。一些玩具,如玩具车或玩具火车,还可以移动,而玩具飞机等玩具也可以移动和飞行。这是界定玩具行为的接口。

Toy.java


public interface Toy {
    void setPrice(double price);
    void setColor(String color);
    void move();
    void fly();
}

代表玩具飞机的课程可以实现玩具接口,并提供所有接口方法的实现。但是,想象一个代表玩具屋的类。这是ToyHouse班的外观。

ToyHouse.java

public class ToyHouse implements Toy {
    double price;
    String color;
    @Override
    public void setPrice(double price) {
        this.price = price;
    }
    @Override
    public void setColor(String color) {
        this.color=color;
    }
    @Override
    public void move(){}
    @Override
    public void fly(){}    
}

如代码中所示,ToyHouse需要提供move()和fly()方法的实现,尽管它不需要它们。这违反了接口隔离原则。这种违规会影响代码的可读性,并使程序员混淆。想象一下,您正在编写ToyHouse类,IDE的智能感知功能会弹出fly()方法以进行自动完成。不完全是玩具屋想要的行为,是吗?

违反接口隔离原则也会导致违反开闭原则。例如,考虑到玩具接口被修改为包括一个walk()方法来容纳玩具机器人。因此,即使玩具不走路,您现在也需要修改所有现有的玩具实施类,以包括一个步行方法。事实上,玩具实施阶段永远不会关闭修改,这将导致易于维护的难度和昂贵的脆弱应用程序。

遵循接口隔离原则

通过遵循接口隔离原则,您可以解决玩具构建应用程序的主要问题 - 玩具接口强制客户端(实现类)依赖于它们不使用的方法

解决方案是将玩具接口分成多个角色接口,以实现特定的行为。让我们分离玩具接口,使我们的应用程序现在有三个接口:玩具,可移动和可飞行。

Toy.java


package guru.springframework.blog.interfacesegregationprinciple;
public interface Toy {
     void setPrice(double price);
     void setColor(String color);
}

Movable.java


package guru.springframework.blog.interfacesegregationprinciple;
public interface Movable {
    void move();
}

Flyable.java


package guru.springframework.blog.interfacesegregationprinciple;
public interface Flyable {
    void fly();
}

在上面的例子中,我们首先使用setPrice()和setColor()方法编写了Toy接口。由于所有玩具都将具有价格和颜色,所有玩具实施类都可以实现此接口。然后,我们写了Movable和Flyable接口来表示玩具中的移动和飞行行为。我们来写实现类。

ToyHouse.java

package guru.springframework.blog.interfacesegregationprinciple;
public class ToyHouse implements Toy {
    double price;
    String color;
    @Override
    public void setPrice(double price) {
        this.price = price;
    }
    @Override
    public void setColor(String color) {
        this.color=color;
    }
    @Override
    public String toString(){
        return "ToyHouse: Toy house- Price: "+price+" Color: "+color;
    }
}

ToyCar.java

package guru.springframework.blog.interfacesegregationprinciple;
public class ToyCar implements Toy, Movable {
    double price;
    String color;
    @Override
    public void setPrice(double price) {
        this.price = price;
    }
    @Override
    public void setColor(String color) {
     this.color=color;
    }
    @Override
    public void move(){
        System.out.println("ToyCar: Start moving car.");
    }
    @Override
    public String toString(){
        return "ToyCar: Moveable Toy car- Price: "+price+" Color: "+color;
    }
}

ToyPlane.java

package guru.springframework.blog.interfacesegregationprinciple;
public class ToyPlane implements Toy, Movable, Flyable {
    double price;
    String color;
    @Override
    public void setPrice(double price) {
        this.price = price;
    }
    @Override
    public void setColor(String color) {
        this.color=color;
    }
    @Override
    public void move(){
        System.out.println("ToyPlane: Start moving plane.");
    }
    @Override
    public void fly(){
        System.out.println("ToyPlane: Start flying plane.");
    }
    @Override
    public String toString(){
        return "ToyPlane: Moveable and flyable toy plane- Price: "+price+" Color: "+color;
    }
}

你所见,实现类现在只实现他们感兴趣的接口。我们的类没有不必要的代码杂乱,更易于读取,并且由于接口方法的改变而更容易修改。

接下来,我们来写一个类来创建实现类的对象

ToyBuilder.java

package guru.springframework.blog.interfacesegregationprinciple;
public class ToyBuilder {
    public static ToyHouse buildToyHouse(){
        ToyHouse toyHouse=new ToyHouse();
        toyHouse.setPrice(15.00);
        toyHouse.setColor("green");
        return toyHouse;
        }
    public static ToyCar buildToyCar(){
        ToyCar toyCar=new ToyCar();
        toyCar.setPrice(25.00);
        toyCar.setColor("red");
        toyCar.move();
        return toyCar;
    }
    public static ToyPlane buildToyPlane(){
        ToyPlane toyPlane=new ToyPlane();
        toyPlane.setPrice(125.00);
        toyPlane.setColor("white");
        toyPlane.move();
        toyPlane.fly();
        return toyPlane;
    }
}

在上面的代码示例中,我们用ToyBuilder类编写了三个静态方法来创建ToyHouse,ToyCar和ToyPlane类的对象。最后,我们来写这个单元测试来测试我们的例子。

ToyBuilderTest.java

package guru.springframework.blog.interfacesegregationprinciple;
import org.junit.Test;
public class ToyBuilderTest {
    @Test
    public void testBuildToyHouse() throws Exception {
    ToyHouse toyHouse=ToyBuilder.buildToyHouse();
    System.out.println(toyHouse);
    }
    @Test
    public void testBuildToyCar() throws Exception {
    ToyCar toyCar=ToyBuilder.buildToyCar();;
        System.out.println(toyCar);
    }
    @Test
    public void testBuildToyPlane() throws Exception {
    ToyPlane toyPlane=ToyBuilder.buildToyPlane();
        System.out.println(toyPlane);
    }
}

Summary of Interface Segregation Principle

接口隔离原则和单一责任原则都具有相同的目标:确保小型,集中和高度凝聚力的软件组件。不同之处在于单一责任原则涉及类,而接口隔离原则涉及接口。接口隔离原理易于理解,易于理解。但是,识别不同的接口有时可能是一个挑战,因为需要谨慎考虑以避免接口的扩散。因此,在编写接口的同时,考虑实现类具有不同行为集合的可能性,如果是这样,则将接口隔离成多个接口,每个接口具有特定的角色。

Spring框架中的接口隔离原理

在我使用Spring框架进行编程时,我已经写了关于依赖注入编程的这个博客的次数。使用Spring框架进行企业应用程序开发时,接口隔离原则变得尤为重要。

随着您正在构建的应用程序的大小和范围增加,您将需要可插拔组件。即使只是单元测试你的类,接口隔离原则也有一个作用。如果您正在测试一个您为依赖注入编写的类,就像我之前写过的那样,写入接口是理想的。通过设计类以对接口使用依赖注入,实现指定接口的任何类都可以注入到类中。在测试课程时,您可能希望注入一个模拟对象,以满足您的单元测试的需求。但是,当您编写的类正在生产中运行时,Spring Framework将会将该接口的真正全功能实现注入到类中。

接口隔离原则和依赖注入是使用Spring框架开发企业级应用程序时掌握的两个非常强大的概念。

Conclusion

面向对象的语言(如Java)非常强大,为开发人员提供了极大的灵活性。你可以误用或滥用任何语言。在多态性帖子中,我解释了“Is-A”测试。如果你正在编写扩展类的对象,但是’Is-A’测试失败,你可能违反了Liskov替换原则。


上一篇 依赖倒转原则

下一篇 里氏代换原则

Comments

Content