面向对象的六大原则:
单一职责原则、开闭原则、里氏替换原则、依赖倒置原则、接口隔离原则、迪米特原则。
1、单一职责原则
英文名称:Single Responsibility Principle(SRP)
一个类应该具有单一的职责(职责可以理解为业务、功能)。
如果一个类承担的职责过多,就等于把这些职责耦合在一起,一个职责的变化可能会削弱或者抑制这个类完成其他职责的能力。这种耦合会导致脆弱的设计,当变化发生时,设计会遭受到意想不到的破坏。
软件设计真正要做的许多内容,就是发现职责并把那些职责相互分离。
public class ImageLoader { //耦合了图片显示、图片内存缓存、图片下载功能
//图片内存缓存
LruCache mImageCache;
//线程池
ExecutorService mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
public ImageLoader() {
initImageCache();
}
//初始化
private void initImageCache() {
final int maxMemory = (int)(Runtime.getRuntime().maxMemory() / 1024);
final int cacheSize = maxMemory / 8;
//创建图片内存缓存对象
mImageCache = new LruCache(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
}
};
}
//显示图片
public void displayImage(final String url,final ImageView imageView) {
//从内存缓存中获取图片
Bitmap bitmap = mImageCache.get(url);
if(bitmap != null) {
imageView.setImageBitmap(bitmap);
return;
}
mExecutorService.submit(new Runnable() {
@Override
public void run() {
//下载图片
Bitmap bitmap = downloadImage(url);
if(bitmap == null){
return;
}
//显示图片
if(imageView.getTag().equals(url)){
imageView.setImageBitmap(bitmap);
}
//缓存图片
mImageCache.put(url,bitmap);
}
});
}
//下载图片
public Bitmap downloadImage(String imageUrl) {
Bitmap bitmap = null;
try {
URL url = new URL(imageUrl);
final HttpURLConnection conn = (HttpURLConnection)url.openConnection();
bitmap = BitmapFactory.decodeStream(conn.getInputStream());
conn.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
return bitmap;
}
}
修改方案:
public class ImageLoader { //图片显示
private ImageDownload mImageDownload;
private ImageCache mImageCache;
public ImageLoader() {
mImageDownload = new ImageDownload();
mImageCache = new ImageCache();
}
//显示图片
public void displayImage(final String url,final ImageView imageView) {
//从内存缓存中获取图片
Bitmap bitmap = mImageCache.get(url);
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
return;
}
//下载图片
mImageDownload.downloadImage(url, new ImageDownload.IDownload() {
@Override
public void onSuccess(Bitmap bitmap) {
imageView.setImageBitmap(bitmap);
mImageCache.put(url, bitmap);
}
@Override
public void onFail() {
}
});
}
}
public class ImageDownload { //图片下载
//线程池
ExecutorService mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
//下载图片
public void downloadImage(final String imageUrl, final IDownload listener) {
mExecutorService.submit(new Runnable() {
@Override
public void run() {
try {
Bitmap bitmap;
URL url = new URL(imageUrl);
final HttpURLConnection conn = (HttpURLConnection)url.openConnection();
bitmap = BitmapFactory.decodeStream(conn.getInputStream());
conn.disconnect();
if(bitmap != null) {
listener.onSuccess(bitmap);
return;
}
} catch (Exception e) {
e.printStackTrace();
}
listener.onFail();
}
});
}
public interface IDownload {
void onSuccess(Bitmap bitmap);
void onFail();
}
}
public class ImageCache { //图片内存缓存
//图片内存缓存
LruCache mLruCache;
public ImageCache() {
initImageCache();
}
//初始化
private void initImageCache() {
final int maxMemory = (int)(Runtime.getRuntime().maxMemory() / 1024);
final int cacheSize = maxMemory / 8;
//创建图片内存缓存对象
mLruCache = new LruCache(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
}
};
}
//从内存缓存中获取图片
public Bitmap get(String url) {
return mLruCache.get(url);
}
//将图片保存到内存缓存中
public void put(String url, Bitmap bitmap) {
mLruCache.put(url, bitmap);
}
}
2、开闭原则
英文名称:Open Close Principle(OCP)
软件中的对象(类、模块、方法等)应该对于扩展是开放的,对于修改是封闭的。
面对需求,对程序的改动是通过增加新代码进行的,而不是更改现有的代码。
在我们最初编写代码时,假设变化不会发生。当变化发生时,我们就创建抽象来隔离以后发生的同类变化。我们希望的是在开发工作展开不久就知道可能发生的变化。查明可能发生的变化所等待的时间越长,要创建正确的抽象就越困难。
开发人员应该仅对程序中呈现出频繁变化的那些部分做出抽象,然而,对于应用程序中的每个部分都刻意地进行抽象同样不是一个好主意。拒绝不成熟的抽象和抽象本身一样重要。
public class DiskCache {
private String cacheDir = "sdcard/cache/";
//从SD卡中获取图片
public Bitmap get(String url) {
return BitmapFactory.decodeFile(cacheDir + url);
}
//将图片缓存到SD卡中
@Override
public void put(String url,Bitmap bitmap) {
FileOutputStream fileOutPutStream = null ;
try {
fileOutPutStream = new FileOutputStream(cacheDir + url);
bitmap.compress(Bitmap.CompressFormat.PNG,100, fileOutPutStream);
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
if(fileOutPutStream!=null){
try{
fileOutPutStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
public class ImageLoader {
private ImageDownload mImageDownload;
private ImageCache mImageCache;
private DiskCache mDiskCache; //增加磁盘缓存变量
private boolean mUseDiskCache; //增加是否使用磁盘缓存标记变量
public ImageLoader() {
mImageDownload = new ImageDownload();
mImageCache = new ImageCache();
mDiskCache = new DiskCache(); //创建磁盘缓存对象
}
//设置是否使用磁盘缓存
public void setUseDiskCache(boolean useDiskCache) {
mUseDiskCache = useDiskCache;
}
//显示图片
public void displayImage(final String url,final ImageView imageView) {
Bitmap bitmap;
if(!mUseDiskCache) {
bitmap = mImageCache.get(url); //从内存缓存中获取图片
} else {
bitmap = mDiskCache.get(url); //从磁盘缓存中获取图片
}
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
return;
}
//下载图片
mImageDownload.downloadImage(url, new ImageDownload.IDownload() {
@Override
public void onSuccess(Bitmap bitmap) {
imageView.setImageBitmap(bitmap);
if(!mUseDiskCache) {
mImageCache.put(url, bitmap); //将图片保存到内存缓存中
} else {
mDiskCache.put(url, bitmap); //将图片保存到磁盘缓存中
}
}
@Override
public void onFail() {
}
});
}
}
修改方案:
public interface ICache { //定义图片缓存接口
Bitmap get(String url);
void put(String url,Bitmap bitmap);
}
public class ImageCache implements ICache { //实现图片缓存接口
//图片内存缓存
LruCache mLruCache;
public ImageCache() {
initImageCache();
}
//初始化
private void initImageCache() {
final int maxMemory = (int)(Runtime.getRuntime().maxMemory() / 1024);
final int cacheSize = maxMemory / 8;
//创建图片内存缓存对象
mLruCache = new LruCache(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
}
};
}
//从内存缓存中获取图片
@Override
public Bitmap get(String url) {
return mLruCache.get(url);
}
//将图片保存到内存缓存中
@Override
public void put(String url, Bitmap bitmap) {
mLruCache.put(url, bitmap);
}
}
public class DiskCache implements ICache { //实现图片缓存接口
private String cacheDir = "sdcard/cache/";
//从SD卡中获取图片
@Override
public Bitmap get(String url) {
return BitmapFactory.decodeFile(cacheDir + url);
}
//将图片缓存到SD卡中
@Override
public void put(String url,Bitmap bitmap) {
FileOutputStream fileOutPutStream = null ;
try {
fileOutPutStream = new FileOutputStream(cacheDir + url);
bitmap.compress(Bitmap.CompressFormat.PNG,100, fileOutPutStream);
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
if(fileOutPutStream!=null){
try{
fileOutPutStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
public class ImageLoader {
private ImageDownload mImageDownload;
private ICache mCache; //图片缓存变量,接口类型
public ImageLoader() {
mImageDownload = new ImageDownload();
}
//设置图片缓存方式(可以是内存缓存,也可以是磁盘缓存)
public void setCache(ICache cache) {
mCache = cache;
}
//显示图片
public void displayImage(final String url,final ImageView imageView) {
Bitmap bitmap = mCache.get(url); //从缓存中获取图片(可以是内存缓存,也可以是磁盘缓存)
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
return;
}
//下载图片
mImageDownload.downloadImage(url, new ImageDownload.IDownload() {
@Override
public void onSuccess(Bitmap bitmap) {
imageView.setImageBitmap(bitmap);
mCache.put(url, bitmap); //将图片保存到缓存中(可以是内存缓存,也可以是磁盘缓存)
}
@Override
public void onFail() {
}
});
}
}
3、里氏替换原则
英文名称:Liskov Substitution Principle(LSP)
所有引用基类的地方必须能透明地使用其子类的对象。
里氏替换原则的核心原理是抽象,抽象又依赖于继承这个特性。
建立抽象,通过抽象建立规范,具体的实现在运行时替换掉抽象,保证系统的扩展性、灵活性。开闭原则和里氏替换原则往往是生死相依、不弃不离的,通过里氏替换来达到对扩展开放,对修改关闭的效果。
代码参考:2、开闭原则
接口:ICache,实现类:ImageCache、DiskCache
在ImageLoader中引用的是接口:ICache,在运行时实现类:ImageCache、DiskCache会替换掉接口:ICache
4、依赖倒置原则
英文名称:Dependence Inversion Principle(DIP)
模块间的依赖通过抽象发生,其依赖关系是通过接口或抽象类产生的(面向接口编程或者说是面向抽象编程)。
依赖倒置原则有以下几个关键点:
(1)高层模块不应该依赖低层模块,两者都应该依赖其抽象;
(2)抽象不应该依赖细节;
(3)细节应该依赖抽象。
代码参考:2、开闭原则
接口:ICache,实现类:ImageCache、DiskCache
ImageLoader对ImageCache、DiskCache的依赖是通过接口ICache发生的
5、接口隔离原则
英文名称:Interface Segregation Principle(ISP)
类间的依赖关系应该建立在最小的接口上。
public class DiskCache implements ICache { //实现图片缓存接口
private String cacheDir = "sdcard/cache/";
//从SD卡中获取图片
@Override
public Bitmap get(String url) {
return BitmapFactory.decodeFile(cacheDir + url);
}
//将图片缓存到SD卡中
@Override
public void put(String url,Bitmap bitmap) {
FileOutputStream fileOutPutStream = null ;
try {
fileOutPutStream = new FileOutputStream(cacheDir + url);
bitmap.compress(Bitmap.CompressFormat.PNG,100, fileOutPutStream);
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally { //关闭IO流,代码可读性差
if(fileOutPutStream!=null){
try{
fileOutPutStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
修改方案:
public class CloseUtils {
private CloseUtils() { }
public static void closeIOQuietly(Closeable closeable) {
if(closeable != null) {
try {
closeable.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public class DiskCache implements ICache { //实现图片缓存接口
private String cacheDir = "sdcard/cache/";
//从SD卡中获取图片
@Override
public Bitmap get(String url) {
return BitmapFactory.decodeFile(cacheDir + url);
}
//将图片缓存到SD卡中
@Override
public void put(String url,Bitmap bitmap) {
FileOutputStream fileOutPutStream = null ;
try {
fileOutPutStream = new FileOutputStream(cacheDir + url);
bitmap.compress(Bitmap.CompressFormat.PNG,100, fileOutPutStream);
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally { //关闭IO流,代码可读性好
CloseUtils.closeIOQuietly(fileOutPutStream);
}
}
}
CloseUtils对FileOutputStream的依赖是建立在Closeable接口上的,该接口只定义了一个关闭IO流的方法:
public interface Closeable extends AutoCloseable {
public void close() throws IOException;
}
6、迪米特原则(最少知识原则)
英文名称:Law of Demeter(LOD)
一个对象应该对其他对象有最少的了解。
一个类应该对自己需要耦合或调用的类知道得最少,类的内部如何实现与调用者或者依赖者没关系,调用者或者依赖者只需要知道它需要的方法即可,其他的可一概不用管。类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大。
public class ImageLoader {
private ImageDownload mImageDownload;
private ICache mCache; //图片缓存变量,接口类型
public ImageLoader() {
mImageDownload = new ImageDownload();
}
//设置图片缓存方式(可以是内存缓存,也可以是磁盘缓存)
public void setCache(ICache cache) {
mCache = cache;
}
//显示图片
public void displayImage(final String url,final ImageView imageView) {
Bitmap bitmap = mCache.get(url); //从缓存中获取图片(可以是内存缓存,也可以是磁盘缓存)
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
return;
}
//下载图片
mImageDownload.downloadImage(url, new ImageDownload.IDownload() {
@Override
public void onSuccess(Bitmap bitmap) {
imageView.setImageBitmap(bitmap);
mCache.put(url, bitmap); //将图片保存到缓存中(可以是内存缓存,也可以是磁盘缓存)
}
@Override
public void onFail() {
}
});
}
}
客户端只需要知道ImageLoader的setCache方法及displayImage方法即可,
至于图片缓存如何获取与保存、图片如何下载等都不需要知道。