面向对象六大原则----接口隔离原则,迪米特原则
2016-08-02 17:58
316 查看
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老鸟
相关文章推荐
- 面向对象六大原则(四):接口隔离原则
- 面向对象六大原则(六):迪米特原则
- 面向对象的六大原则之 —— 接口隔离原则
- 面向对象六大原则--迪米特原则
- 面向对象的六大原则之 —— 接口隔离原则
- 面向对象的六大原则之 —— 迪米特原则
- 面向对象的六大原则之 —— 迪米特原则
- 面向对象六大原则(二):里式替换原则
- 安卓设计模式(一)面向对象六大设计原则
- 面向对象六大原则之依赖倒置原则
- 《Android源码设计模式》----面向对象六大原则
- Android源码设计模式解析与实战——面向对象六大原则(一)
- 面向对象六大原则
- 从面向对象六大原则开始
- 面向对象六大原则随记
- 设计模式六大原则 - (4):接口隔离原则
- 面向对象的六大原则
- 设计模式之开篇---面向对象的六大原则
- 面向对象的六大原则
- 设计模式六大原则之--接口隔离原则(ISP)