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

来源:互联网 发布:mysql select 多张表 编辑:程序博客网 时间:2024/06/07 03:15

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方法。如下图:

既然这些文件流都是实现了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<Girl> listGirls = new ArrayList() ;            //初始化女生            for(int i=0;i<20;i++){                listGirls.add(new Girl());            }            //告诉体育委员开始执行清查任务            monitor.countGirls(listGirls);        }    }    /**     * 班长     */    public class Monitor {        //有清查女生的工作        public void countGirls(List<Girl> 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<Girl> 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老鸟

                                                
0 0