面向对象六大原则
1、面向对象的六大原则
1、单一职责原则
- 简单的说,一个类应该是一组相关性很高的函数和数据的封装,也就是一个类一个功能。
- 比如:一个图片加载器ImageLoader,可能大多代码都是封装成一个类,也就是图片的加载逻辑和图片的缓存逻辑在一起,而运用单一职责原则,是把图片加载逻辑和图片缓存逻辑分开成两个类,一个ImageLoader,一个ImageCache,这样ImageCache只负责图片的缓存,将ImageLoader的代码量减少了,职责清晰了,耦合度也降低,修改时各自的逻辑时也不会相互影响。
2、开闭原则
-
这里深有体会,所谓的开闭原则,就是对扩展开发,对修改关闭,但现实没这么绝对,应该是应该尽量通过扩展方式来实现变化,而不是通过修改原有代码来实现。
-
还是ImageLoader为例:
原来的ImageCache只有内存缓存,需要多一种本地缓存,好吧,运用单一职责原则,就分成三个类ImageLoader、MemoryCache、DiskCache,然后ImageLoader代码为:public class ImageLoader { MemoryCache mMemoryCache = new MemoryCache(); DiskCache mDiskCache = new DiskCache(); boolean isUseDiskCache = false; ExecutorService mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); public void display(String url, ImageView imageView){ Bitmap bitmap = isUseDiskCache ? mDiskCache.get(url) : mMemoryCache.get(url); if (bitmap != null){ imageView.setImageBitmap(bitmap); return; } //没有缓存就网络加载 } public void setUseDiskCache(boolean useDiskCache){ isUseDiskCache = useDiskCache; } }
上面的代码存在的问题,多了一个本地缓存,需要去修改ImageLoader的逻辑,需要增加set方法,当然这里看不出什么,而实际项目中,但功能代码多复杂,很可能修改后出现BUG,那么运用开闭原则效果如下:
首先定义接口ImageCache,让MemoryCache和DiskCache实现:public interface ImageCache { Bitmap get(String url); void put(String url, Bitmap bitmap); } //MemoryCache public class MemoryCache implements ImageCache { private LruCache<String,Bitmap> mLruCache; public MemoryCache() { //初始化mLruCache } @Override public Bitmap get(String url) { return mLruCache.get(url); } @Override public void put(String url, Bitmap bitmap) { mLruCache.put(url,bitmap); } } //DiskCache public class DiskCache implements ImageCache { @Override public Bitmap get(String url) { return null; //本地获取 } @Override public void put(String url, Bitmap bitmap) { //本地存 } }
接下来看ImageLoader:
public class ImageLoader { private ImageCache mImageCache = new MemoryCache();//默认内存缓存 ExecutorService mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); public void display(String url, ImageView imageView){ Bitmap bitmap = mImageCache.get(url); if (bitmap != null){ imageView.setImageBitmap(bitmap); return; } //没有缓存就网络加载 } public void setImageCache(ImageCache imageCache){ mImageCache = imageCache; } }
看到这里有很深的体会,使用就这样:
ImageLoader imageLoader = new ImageLoader(); imageLoader.setImageCache(new DiskCache());
使用者也可以继承ImageCache去扩展,回头想下:对扩展开放,对修改关闭。
3、里氏替换原则
- 定义:所有引用基类的地方必须能够正确的使用其子类对象,其实就是继承和多态。
- 上面的ImageCahe就是里氏替换原则,在ImageLoader中使用了ImageCahe,它的子类都有put和set的功能,它们都可以替代 ImageCahe工作。
4、依赖倒置原则
- 概念的东西很抽象,一句话概况就是面向接口编程,目的就是解耦。
- 没错,还是上面的ImageLoader,ImageLoader依赖于ImageCache而不是依赖于MemoryCahe或者DiskCache,这样加载模块和缓存模块不直接发生关系,通过接口间接产生关系。
- 这样在来看依赖倒置原则的特点就能理解:高层模块不应该依赖底层模块,两者应该依赖抽象,高层模块就是调研端,也就是ImageLoader,底层模块就是实现类,也就是MemoryCache或者DiskCache,抽象就是接口ImageCache。
5、接口隔离原则
-
说白了,就是让客户端依赖的接口尽可能的小,好吧,还是很抽象,直接上个例子:
上面的DiskCache必然要用到OutputSteam, 它实现了一个叫Closeable的接口:try { outputStream.close(); } catch (IOException e) { e.printStackTrace(); }
很显然,看到try...catch很不爽,每次都要写,果断封装下:
public void close(Closeable closeable){ if (closeable != null){ try { closeable.close(); } catch (IOException e) { e.printStackTrace(); } } }
这样提高了重用性,并且建立在最小依赖原则的基础上,它只需要知道这个对象是可关闭,其他不关心。通过Closeable接口将可关闭的对象抽象起来,这样客户端依赖于Closeable就可以对客户端隐藏其他接口信息,客户端只需要知道这个对象可关闭即可。
6、迪米特原则
-
通俗的说,一个类应该对自己需要耦合或调用的类知道得最少,类的内部如何实现与调用者或者依赖这没关系,调用者或依赖者只需要知道它需要的方法即可。
-
以中介的例子为例,找中介找房:
如果是这样:public class Room { public String area; public String price; public Room(String area, String price) { this.area = area; this.price = price; } } //中介 public class Mediator { List<Room> mRooms = new ArrayList<>(); public Mediator(){ for(int i = 0;i < 5; i++){ mRooms.add(new Room(14+i,(14+i)*150)); } } public List<Room> getRooms(){ return mRooms; } } //租客 public class Tenant { private float area = 60; private float price = 2000; public void rentRoom(Mediator mediator){ for (Room room : mediator.getRooms()) { if (room.area >= area && room.price <= price){ System.out.println("找到了"); return; } } } }
看到一个问题,租客想找房,他不应该和Room打交道,也就是Tenant 类不应该出现Room类,分清关系,Room应该和Mediator打交道,Tenant只和Mediator打交道,那么这样:
public class Mediator { List<Room> mRooms = new ArrayList<>(); public Mediator(){ for(int i = 0;i < 5; i++){ mRooms.add(new Room(14+i,(14+i)*150)); } } public Room rentOut(float area,float price){ for (Room room : mRooms) { if (room.area >= area && room.price <= price){ return room; } } return null; } } public class Tenant { private float area = 60; private float price = 2000; public void rentRoom(Mediator mediator){ mediator.rentOut(area,price); } }