浅谈设计模式-接口隔离原则

书接上回,本篇继续讲一下设计模式六大原则(有些书认为是7大原则)

接口隔离原则

要讲好这个原则,就得解释下几个概念。

接口(interface)

1>接口,抽象方法的集合,

2>接口,一种约束形式,为不相关的类提供约定好的处理服务(方法签名)

3>接口是类的拓展(一个类可以实现多个接口,意味着这个类可以扮演不同接口类型[接口多态])

隔离

汉语词典里面的解释

1>不让聚在一起,使断绝往来;

2>把患传染病的人或畜分开,避免接触。

接口隔离

这个解释起来有点拗口,先抛出结论,等下用代码说活。

1>一个类对另一个类的依赖应该建立在最小的接口上

2>客户端不应该依赖它不需要的接口

案例分析

需求:使用面向对象方式描述鱼和鸟各种行为动作

第一版:

行为接口

/**
 * 行为接口:描述动物行为
 */
public interface IBehavior {
    /**
     * 飞行
     */
    void fly();
    /**
     * 潜行
     */
    void swim();
}

鱼:

public class Fish  implements IBehavior{
    public void fly() {
        System.out.println("鱼:俺是折翼的鱼飞不起....");
    }
    public void swim() {
        System.out.println("鱼:鱼翔浅底说的就是我...");
    }
}

鸟:

public class Bird implements IBehavior  {
    public void fly() {
        System.out.println("鸟:天高任我飞.....");
    }
    public void swim() {
        System.out.println("鸟:羡慕鱼能游.....");
    }
}

动物园:

public class Zoo {
    public void showFly(IBehavior behavior){
        behavior.fly();
    }
    public void showSwim(IBehavior behavior){
        behavior.swim();
    }
    public static void main(String[] args) {
        Zoo zoo = new Zoo();

        Fish fish = new Fish();//鱼
        Bird bird = new Bird();//鸟

        zoo.showFly(bird);
        zoo.showSwim(fish);
    }
}
结果:
鱼:鱼翔浅底说的就是我...
鸟:天高任我飞.....

解析

上述设计存在2个问题点:

1:接口臃肿

        在上案例中, 对Fish类来说,fly()方法是非必须的,对Bird类来说,swim()方法是非必须的,但是照java语法Bird跟Fish作为Animal子类必须去实现。这种臃肿(多余方法)接口设计,称之为胖接口,一点接口方法变动,会影响所有实现该接口的客户端类程序,开发中尽可能的避免这种臃肿接口设计。

2:客户端类依赖不需要的接口

        按照Zoo类的设计,showFly() 方法应该要调用fly 相关的行为动作,showSwim()应该要调用swim 相关的行为动作,但是这2个方法都统一依赖IBehavior接口,语法上没错,错就在接口设计原则中,因为客户端类需要什么方法就提供什么功能,把不需要剔除,尽可能保证接口纯洁性,纯粹性。

第二版:

将IBehavior接口拆解成2个接口

/**
 * 飞行为
 */
public interface IFlyBehavior {
    /**
     * 飞行
     */
    void fly();
}
/**
 * 游动行为
 */
public interface ISwimBehavior {
    /**
     * 潜行
     */
    void swim();
}

如果有动物确实可以又飞,又游,IBehavior依然有效

/**
 * 行为接口:描述动物行为
 */
public interface IBehavior  extends IFlyBehavior, ISwimBehavior{
}

鸟:

public class Bird implements IFlyBehavior {
    public void fly() {
        System.out.println("鸟:天高任我飞.....");
    }
}

鱼:

public class Fish  implements ISwimBehavior{
    public void swim() {
        System.out.println("鱼:鱼翔浅底说的就是我...");
    }
}

动物园:

public class Zoo {
    public void showFly(IFlyBehavior behavior){
        behavior.fly();
    }
    public void showSwim(ISwimBehavior behavior){
        behavior.swim();
    }


    public static void main(String[] args) {
        Zoo zoo = new Zoo();

        Fish fish = new Fish();//鱼
        Bird bird = new Bird();//鸟

        zoo.showFly(bird);
        zoo.showSwim(fish);
    }
}

解析:

        第二版设计使用接口拆跟接口继承方式解决胖接口问题,鱼,鸟他们只需要实现它们特有的行为接口即可, 如果需要fly跟swim行为,则可以使用IBehavior。当接口发生改动时,客户端类映射对应降低。

        另外,也解决了接口依赖问题Zoo类的showFly,showSwim方法只需要依赖它们需要展示方法的接口,从而达到接口纯粹化目的

结论:

1>代码设计时,不应该强迫类实现它们不需要的接口方法,建议将臃肿接口按需拆分,按需实现。

2>客户端类对某个接口的需求越少越好,保证接口设计时做到专一。

思考:

问: 怎么设计意义在哪?

答:接口按需设计,按需实现,尽可能纯粹,尽可能专一,原因很简单:当某个客户端程序发生变化,迫使其接口跟随发生变更时,纯粹且专一的接口设计,可以降低因为接口变更导致其他客户端跟随变更的影响。这实际上就是避免接口污染问题很好设计方案。

注意:

        接口设计不能太细,一个接口要具备一些基本的功能,能独一完成一个基本的任务,太细就有种过度设计意思。

接口隔离

回到本篇的主题,接口隔离:

1>客户端不应该依赖它不需要的接口

接口设计尽可能纯粹,专一,按需设计, 按需实现

2>一个类对另一个类的依赖应该建立在最小的接口上

客户端类按需设计,减少对接口依赖,缩减对接口依赖范围。

VS单一职责

从上面例子设计上看,细心的朋友会发现跟之前写的单一职责是同一个例子,是不是说接口隔离跟单一职责原则是同一个原则呢?可以说是,也可以说不是

是:

单一职责原则对接口做了约束,要求接口职责单一,代码上体现是接口单一化,细致化。

接口隔离原则也对接口做了月,也要求接口单一化,细节化。

不是:

1>单一职责原则除了对接口做约束外,也约束了,类,方法,而接口隔离约束对象只有接口

2>单一职责原则强调的是程序实现细节,业务功能实现尽可能独立;接口隔离原则针对是接口的抽象,强调类与接口交互。

总结:从代码落地上看2者是一致的,从架构设计上2者有区分的,一个强调内聚,一个强调耦合

运用

我们还是从JDK里面找例子,比如线程中2个较为特殊接口:

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}
@FunctionalInterface
public interface Callable<V> {
    V call() throws Exception;
}

Runnable 接口是线程任务接口,与Thread配合完成线程任务定制,任务执行后,没有返回值。

Callable 接口,可调用接口,与Future + Thread/线程池 配合使用,在完成任务调用后有返回值。

public class App {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //Runable常规用法
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程干活了...");
            }
        });
        thread.start();

        //无返回的线程[非常规用法]
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("线程跑起来---无返回");
            }
        };
        Callable callable = new Callable<String>() {
            @Override
            public String call() throws Exception {
                System.out.println("线程跑起来---有返回");
                return "这是返回值";
            }
        };
        FutureTask taskCall = new FutureTask(callable);
        FutureTask taskRun = new FutureTask(runnable, "没有返回值,给个默认值");

        new Thread(taskCall).start();
        new Thread(taskRun).start();

        System.out.println(taskCall.get());
        System.out.println(taskRun.get());
    }
}

解析

        看上面Runnable常规用法,也是我们平常写最多的一种用法,而非常规用法,则是加上FutureTask实现线程的控制。这里我们不研究线程怎么控制,仅仅研究接口设计。

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}
@FunctionalInterface
public interface Callable<V> {
    V call() throws Exception;
}
  FutureTask taskCall = new FutureTask(callable);
  FutureTask taskRun = new FutureTask(runnable, "没有返回值,给个默认值");

        Runnable Callable 拆分成2个接口, Runnable任务调度无返回, Callable 任务调度有返回,FutureTask 正对这个2个接口设计独立依赖。这个也算接口隔离原则一种体现。

总结

接口隔离原则使用过程中要明确的:

1>客户端不应该依赖它不需要的接口

2>一个类对另一个类的依赖应该建立在最小的接口上

3>按需设计接口,按需实现接口

4>接口隔离原则符合高内聚低耦合的设计思想,从而使得类具有很好的可读性,可扩拓展行和可维护性

5>注意防止过度设计,一个接口要具备一些基本的功能,能独一完成一个基本的任务

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

浪飞yes

我对钱没兴趣~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值