单一职责原则 英文名称是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。它的定义是:客户端不应该依赖它不需要的接口。另一种定义是:类间的依赖关系应该建立在最小的接口上。接口隔离原则将非常庞大、臃肿的接口拆分成为更小的和更具体的接口,这样客户将会只需要知道他们感兴趣的方法。接口隔离原则的目的是系统解开耦合,从而容易重构、更改和重新部署。
//将下载的图片缓存到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 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来说都是不可见的,这里也是用到了迪米特原则。