SOLID原则-接口隔离原则

简介

前面我们已经介绍了

今天来讲解接口隔离原则。

接口是Java 编程语言的核心功能之一,在企业应用开发中被广泛应用来抽象业务和多接口的实现能力。从写代码的角度,写一个接口是简单的,你可以用 interface 关键字创建一个接口并在接口里声明一些方法,其他的类可以通过 implements 关键字来实现接口定义的方法。作为一个 java 使用者,你肯定写过很多接口,但是有没有想过这样一个问题:之前写的接口是否是合理的,有没有好的设计原则可以指导我们编写出良好的接口? 下面详解的 接口隔离原则 可以回答这个问题。

接口隔离原则(Interface Segregation Principle,简称ISP)代表SOLID 原则中的ISOLID 原则是面向对象编程中设计原则,遵循这些原则可使我们的代码易读、易维护、易升级、易修改。接口隔离原则是 Robert C.Martin 在2002年出的一本书 《Agile Software Development: Principles, Patterns and Practices》 里提出的,原则的描述是 Clients should not be forced to depend upon interfaces that they do not use,直译为: 客户端不应该被强迫依赖它不需要的接口。其中客户端是指实现接口的类。

接口隔离原则告诉我们接口不应包含实现类不需要的方法,否则,这类接口我们称之为胖接口,实现胖接口的类要为那些不需要的方法提供空实现,除此之外,当接口发生变化时实现类也会被迫修改,尤其是那些使用不到的方法签名发生变化。接口隔离原则主张把胖接口分离成高内聚的小接口,这些小接口被称为角色接口。每个角色接口只包含与他紧密相关的方法,从而,实现类只实现与它相关联的接口。

违反接口隔离原则的例子

有一个需求,开发一个生产不同类型的玩具的应用,每个玩具会有价格和颜色的基本属性,如汽车和火车有移动的行为,但另一些玩具,如飞机有移动和飞的行为。因此,我们定义一个接口

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

如果玩具是飞机,它能实现这个接口的所有方法,如过是房子,只能实现部分方法,代码如下:

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 提供了方法 movefly 的空实现。此时接口Toy 的设计违反了ISP,ToyHouse 的实现会影响代码的可读性和是维护代码的工程师困惑:为啥这些方法只提供了空实现呢。
违反ISP也可能导致违反开闭原则,继续以上的例子来讲,如果为Toy 添加一个 walk() 方法,那么实现Toy的类都将被修改,违反了对修改关闭的原则。

遵循接口隔离原则

为了满足ISP,你可以把胖接口Toy 分离成三个小接口: Toy,Movable,Flyable , 把方法move移到接口Movable,方法fly()移到接口Flyable,让实现类按需实现相应的接口。

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

public interface Movable {
    void move();
}

public interface Flyable {
    void fly();
}

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

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

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

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

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

总结

接口隔离原则和单一职责原则有同样的目的:确保设计出小的、聚焦的、高内聚的软件组件。两者不同的是,单一职责原则关心的是类,接口隔离原则关心的是接口。接口隔离原则比较容易理解和使用。但是,也要当心防止接口被分割的过细,因此,需要思考接口的职责范围,可以根据角色划分接口。

除了我们上面讲解的java 编程语言的接口外,我们常讲的接口还有两类

  • 一类是一组接口集合,常见是微服务的接口和类库的接口。
  • 另一类是大个 API 接口或者函数,调用者只需要函数中的部分功能,我们需要把大而全的函数拆分成粒度更细的多个函数,让调用者只依赖它需要的那个细粒度的函数。

以上两类接口,从更广泛的角度理解,ISP 也同样适用,可以指导我们在高层次上进行设计。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值