面向对象六大原则----接口隔离原则,迪米特原则

Java 中面向对象编程六大原则:

单一职责原则 英文名称是Single Responsibility Principle,简称SRP

开闭原则 英文全称是Open Close Principle,简称OCP

里氏替换原则 英文全称是Liskov Substitution Principle,简称LSP

依赖倒置原则  英文全称是Dependence Inversion Principle,简称DIP

接口隔离原则 英文全称是InterfaceSegregation Principles,简称ISP

迪米特原则 英文全称为Law of Demeter,简称LOD,也称为最少知识原则(Least Knowledge Principle)


系统有更高的灵活性——接口隔离原则

接口隔离原则英文全称是InterfaceSegregation Principles,简称ISP。它的定义是:客户端不应该依赖它不需要的接口。另一种定义是:类间的依赖关系应该建立在最小的接口上。接口隔离原则将非常庞大、臃肿的接口拆分成为更小的和更具体的接口,这样客户将会只需要知道他们感兴趣的方法。接口隔离原则的目的是系统解开耦合,从而容易重构、更改和重新部署。

比如 你定义了一个接口
public interface I {
    public void method1();  
    public void method2();  
    public void method3();  
    public void method4();  
    public void method5();  
}
但是
A只想实现接口中的method1方法
B只想实现接口中的method2,method3方法
C只想实现接口中的method4,method5方法

如果你这个时候就写一个接口,让A,B,C都去实现I接口的话,这样会导致A,B,C中会实现不需要的方法,这样设计接口会导致接口比较臃肿,因此我们要把这个接口分开写。继续以我们之前的ImageLoader示例来说明一下。我们在使用了OutputStream或者其他可关闭的对象之后,我们必须保证它们最终被关闭了,我们的SD卡缓存类中就有这样的代码:

  //将下载的图片缓存到sd卡里
    @Override
    public void put(String key, Bitmap bitmap){
        if(!mCachDir.exists()){
            return;
        }
        //对传入的key值进行MD5处理
        String savedFilePath = mCacheDirPath+File.separator+hashKeyForDisk(key);
        FileOutputStream fileOutputStream = null;
        try {
            fileOutputStream = new FileOutputStream(new File(savedFilePath));
            bitmap.compress(Bitmap.CompressFormat.PNG, 100, fileOutputStream);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        finally {
            if(fileOutputStream != null) {
                try {
                    fileOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

我们看到的这段代码可读性非常差,各种try…catch嵌套,都是些简单的代码,但是会严重影响代码的可读性,并且多层级的大括号很容易将代码写到错误的层级中。大家应该对这类代码也非常反感,那我们看看如何解决这类问题。 我们可能知道Java中有一个Closeable接口,该接口标识了一个可关闭的对象,它只有一个close方法。如下图:

面向对象六大原则----接口隔离原则,迪米特原则_第1张图片

既然这些文件流都是实现了Closeable接口,那只要建一个方法统一来关闭这些对象不就可以了么,代码如下:

public class CloseUtils {

    private CloseUtils(){}

    public static void close(Closeable closeable){
        try {
            if(closeable != null) {
                closeable.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
我们再看看把这段代码运用到上述的put方法中的效果如何:

    //将下载的图片缓存到sd卡里
    @Override
    public void put(String key, Bitmap bitmap){
        if(!mCachDir.exists()){
            return;
        }
        //对传入的key值进行MD5处理
        String savedFilePath = mCacheDirPath+File.separator+hashKeyForDisk(key);
        FileOutputStream fileOutputStream = null;
        try {
            fileOutputStream = new FileOutputStream(new File(savedFilePath));
            bitmap.compress(Bitmap.CompressFormat.PNG, 100, fileOutputStream);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        finally {
            CloseUtils.close(fileOutputStream);
        }
    }

代码简洁了很多!而且这个closeUtils方法可以运用到各类可关闭的对象中,保证了代码的重用性。CloseUtils的close方法的基本原理就是依赖于Closeable抽象而不是具体实现(这不是前面讲的依赖倒置原则么),并且建立在最小化依赖原则的基础,它只需要知道这个对象是可关闭,其他的一概不关心,也就是这里的接口隔离原则。

试想一下,如果在只是需要关闭一个对象时,它却暴露出了其他的接口函数,比如OutputStream的write方法,这就使得更多的细节暴露在客户端代码面前,不仅没有很好地隐藏实现,还增加了接口的使用难度。而通过Closeable接口将可关闭的对象抽象起来,这样只需要客户端依赖于Closeable就可以对客户端隐藏其他的接口信息,客户端代码只需要知道这个对象可关闭(只可调用close方法)即可。


更好的可扩展性——迪米特原则

迪米特原则英文全称为Law of Demeter,简称LOD,也称为最少知识原则(Least Knowledge Principle)。虽然名字不同,但描述的是同一个原则:一个对象应该对其他对象有最少的了解。通俗地讲,一个类应该对自己需要耦合或调用的类知道得最少,类的内部如何实现、如何复杂都与调用者或者依赖者没关系,调用者或者依赖者只需要知道他需要的方法即可,其他的我一概不关心。类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大。

迪米特法则还有一个英文解释是:Only talk to your immedate friends,翻译过来就是:只与直接的朋友通信。什么叫做直接的朋友呢?每个对象都必然会与其他对象有耦合关系,两个对象之间的耦合就成为朋友关系,这种关系的类型有很多,例如组合、聚合、依赖等。

我们来说个例子说明怎么做到只和朋友交流。 说是有这么一个故事,老师想让班长确认一下全班女生来齐没有,就对他说:“你去把全班女生清 一下。”班长没听清楚,或者是当时脑子正在回忆什么东西,就问道:“亲哪个?”老师¥#……¥%。 我们来看这个笑话怎么用程序来实现:

    /**
     * 老师类
     */
    public class Teacher {
        //老师对班长发布命令, 清一下女生
        public void commond(Monitor monitor){
            List listGirls = new ArrayList() ;
            //初始化女生
            for(int i=0;i<20;i++){
                listGirls.add(new Girl());
            }
            //告诉体育委员开始执行清查任务
            monitor.countGirls(listGirls);
        }
    }

    /**
     * 班长
     */
    public class Monitor {
        //有清查女生的工作
        public void countGirls(List listGirls){
            System.out.println(" 女生数量是: "+listGirls.size());
        }
    }

    /**
     * 女生
     */
    public class Girl {
    }

下面是模拟命令过程:

    /**
     * 调用类
     */
    public class Client {
        public static void main(String[] args) {
            Teacher teacher = new Teacher();
            //老师发布命令
            teacher.commond(new Monitor());
        }
    }

运行Client的结果就是:女生数量是:20

我们回过头来看这个程序有什么问题,首先来看 Teacher 有几个朋友,就一个Monitor类,这个就是朋友类, 迪米特法则要求一个类只和朋友类交流, 但是 commond 方法中我们与 Girl 类有了交流,声明了一个 List动态数组,也就是与一个陌生的类 Girl 有了交流,这样设计不好,耦合严重,修改时很容易出错。下面看看重新设计Teacher和Monitor类:

 /**
     * 老师类
     */
    public class Teacher {
        //老师对班长发布命令, 清一下女生
        public void commond(Monitor monitor){
            //告诉体育委员开始执行清查任务
            monitor.countGirls();
        }
    }

    /**
     * 班长
     */
    public class Monitor {
        //有清查女生的工作
        public void countGirls(){
            List listGirls = new ArrayList() ;
            //初始化女生
            for(int i=0;i<20;i++){
                listGirls.add(new Girl());
            }
            System.out.println(" 女生数量是: "+listGirls.size());
        }
    }

    /**
     * 女生
     */
    public class Girl {
    }

    /**
     * 调用类
     */
    public class Client {
        public void main(String[] args) {
            Teacher teacher = new Teacher();
            //老师发布命令
            teacher.commond(new Monitor());
        }
    }
程序做了一个简单的修改,就是把 Teacher 中的对女生类 List初始化(这个是有业务意义的,产生出 全班的所有人员)移动到了 Monitor 的 countGrils 方法中,避开了 Teacher 类对陌生类 Girl 的访问, 减少系统间的耦合, 使得系统具有更低的耦合性和更好的可扩展性。

在我们之前的ImageLoader代码里面,ImageLoader只与ImageCahe交流,而具体实现ImageCahe的各个Cache类可以有自己不同的实现细节,但是这些细节对ImageLoader来说都是不可见的,这里也是用到了迪米特原则。

代码github地址:点击打开链接

更多精彩Android技术可以关注我们的微信公众号,扫一扫下方的二维码或搜索关注公共号:  Android老鸟

                                                 面向对象六大原则----接口隔离原则,迪米特原则_第2张图片

你可能感兴趣的:(android系列)