面向对象编程的六大原则
让项目拥有变化的能力--依赖倒置原则
依赖倒置原则的英文全程是Dependence Inversion Principle,缩写是DIP,依赖倒置原则是一种特定的解耦形式,使得高层次的模块不依赖于低层次的模块的实现细节的目的,依赖模块被颠倒了。简单的说有以下几点:
- 高层模块不应该依赖于底层模块,两者都应该依赖于其抽象。
- 抽象不应该依赖于细节。
- 细节应该依赖于抽象。
在Java语言中,抽象就是指接口和抽象类,两者都是不能直接被实例化的;细节就是指实现类,实现接口或继承抽象类而产生的类就是细节,细节可以被直接实例化。高层模块是指调用端,低层模块就是具体实现类。依赖倒置原则在Java中的表现就是:模块间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或者抽象类产生的。
在我们之前的章节中,有一章的ImageLoader是依赖于具体实现的,代码如下:
public class ImageLoader {
// 内存缓存(直接依赖于细节)
MemoryCache mMemoryCache = new MemoryCache ();
// 线程池,线程数为CPU所允许的数量
ExecutorService mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
//加载图片到ImageView中
public void displayImg(final String url, final ImageView imageView) {
}
//从网络获取图片
private Bitmap downloadImg(String imageUrl) {
}
}
然后我们分析出该图片加载类只有内存缓存,已经无法满足我们的需求,于是我们设计出了DoubleCache类,此时便需要修改我们的代码:
public class ImageLoader {
// 双缓存(直接依赖于细节)
DoubleCache mDoubleCache = new DoubleCache ();
// 线程池,线程数为CPU所允许的数量
ExecutorService mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
//加载图片到ImageView中
public void displayImg(final String url, final ImageView imageView) {
}
//从网络获取图片
private Bitmap downloadImg(String imageUrl) {
}
}
我们将MemoryCache修改为DoubleCache,并且修改了部分缓存代码,就轻松地满足了我们的需求。但是!这里就违反了我们第二章所讲的开闭原则,这样设计出来的代码在后期需求改变时需要不停地修改,会使代码冗余并且容易出错。我们在开闭原则一章结束时的代码就很好地解决了这个问题,一句话概括起来就是:依赖抽象,而不依赖于具体实现。通过依赖注入的方式,可以保证系统的灵活性,我们回顾一下之前的代码(不熟悉的同学请回头阅读开闭原则:
ImageCache抽象类
public interface ImageCache {
Bitmap get(String url);
void put(String url,Bitmap bitmap);
}
ImageLoader类
public class ImageLoader {
// 图片缓存,依赖于抽象,并且有一个默认的实现
ImageCache mImageCache = new MemoryCache();
// 设置缓存策略,依赖于抽象
public void setmImageCache(ImageCache cache){
mImageCache = cache;
}
public void displayImage(String imageUrl,ImageView imageView){
}
}
在这里,我们建立了ImageCache抽象,并且让ImageLoader依赖于抽象而不是具体细节。当我们的缓存策略发生改变时,我们只需要实现ImageCache或者集成已有的ImageCache子类完成具体的代码实现即可,这样就保证了我们的软件的高拓展性,有了变化的能力,这就是依赖倒置原则。
从上述的几节我们也发现,好像要让系统变得灵活,抽象似乎成了我们唯一的手段。