就一个类而言,应该仅有一个引起它的变化原因(换句话说,也就是一个类最好制作一件事(这个一件事的定义比较难定义,看个人))
例子:imageLoader
最开始的imageLoader里面有 图片加载逻辑 和 处理图片缓存的逻辑 ,这在我们看来就不符合SRP原则,所以要分开,分成图片加载逻辑(ImageLoader),和缓存部分(ImageCache)
开闭原则定义是:软件中的对象(类,模块,函数等)应该对于扩展是开放的,但是对于修改是封闭的。(换句话说,让软件需要变化时,我们应该尽量通过扩展的方式来实现变化,而不是通过修改已有的代码来实现)
例子:ImageLoader 需要加个缓存机制(本地或者网络)
问题:怎么让缓存机制变得可扩展,可以让用户随便改
public class ImageLoader { //图片缓存 ImageCache mImageCache = new MemoryCache(); .....//一些代码 //注入缓存实现 public void setmImageCache(ImageCache cache){ mImageCache = cache; } public void display(String imageUrl,ImageView imageView){ //如果图片不是null就设置图片 //如果图片是null就去获取 //提交到线程池去下载 submitLoadRequest(imageUrl,imageView); } public void submitLoadRequest(final String imageUrl ,final ImageView imageView){ ....//一些代码 @Override public void run () { ...//一些代码 mImageCache.put(imageUrl, imageView); } } }
上面是最终代码,可以看出解决了问题,用什么缓存机制由我们自己说了算通过setImageCache(这里默认用了Memory)
public interface ImageCache{ public Bitmap get(String url); public void put(String url,Bitmap bmp); }
然后不管是内存缓存还是本地缓存,或者两者都有都继承这个就行,然后用户自己定制就可以了
比如
MemoryCache,DiskCache,DoubleCache都是实现接口ImageCache,所以可以这么干
imageloader.setImageCache(new MemoryCache())
imageloader.setImageCache(new DiskCache())
imageloader.setImageCache(new DoubleCache())
所有引用基类的地方必须能透明地使用其子类的对象。(说白了就是只要父类能出现的地方子类就能出现,而且替换为子类也不会产生任何错误或异常,使用者可能根本就不需要知道是父类还是子类,但是反过来就不行,有子类的地方,父类未必就能适应,最后其实就是抽象)
MemoryCache,DiskCache,DoubleCache都是实现接口ImageCache,所以可以这么干
imageloader.setImageCache(new MemoryCache())
imageloader.setImageCache(new DiskCache())
imageloader.setImageCache(new DoubleCache())
这个就是用到了里氏替换原则。
依赖倒置原则有以下几个关键点。
(1)高层模块(调用端)不应该依赖低层模块(具体实现类),两者都应该依赖抽象
(2)抽象(接口或者抽象类)不应该依赖细节
(3)细节(实现接口或继承抽象类而产生的类就是细节)应该依赖抽象
在Java语言中的表现就是:模块间的依赖通过抽象发生,实现类之间不发生直接依赖关系,其依赖关系是通过接口或抽象类产生的。
例如上面的例子:
public class ImageLoader { //图片缓存 ImageCache mImageCache = new MemoryCache(); ..... }
可以看出ImageLoader依赖的是接口ImageCache,这个就是对的
如果是下面这样那就是依赖细节了
public class ImageLoader { //图片缓存 MemoryCache memoryCache = new MemoryCache(); .....// }
这样就不行,不方便扩展
ImageCache mImageCache = new MemoryCache();
这样写的话,如果有需求要改的话只需要实现ImageCache类或者继承其他已有的ImageCache子类完成相应的缓存功能。这样就保证了高扩展性!!!
其实就是客户端不应该依赖他不需要的接口(也可以理解为:类间的依赖关系应该建立在最小的接口上)
public void put(String url,Bitmap bmp){ try{ FileOutputStream fileOutputStream = null; fileOutputStream = new FileOutputStream(cacheDir + url); bmp.compress(Bitmap.CompressFormat.PNG, 100 , fileOutputStream); }catch (FileNotFoundException e){ e.printStackTrace(); }finally { if(fileOutputStream != null){ try{ fileOutputStream.close(); }catch{ e.printStackTrace(); } } } }
Try catch各种嵌套,这样看起来很不好看影响代码阅读性
<span style="font-family: Arial, Helvetica, sans-serif;">public final class CloseUtils{</span>
private CloseUtils(){} /** * 关闭Closeable对象 * @param closeable * */ public static void closeQuitely(Closeable closeable){ if(null != closeable){ try{ closeable.close(); }catch (IOException e){ e.printStackTrace(); } } } } public void put(String url ,Bitmap bmp){ FileOutputStream fileOutputStream = null; try{ FileOutputStream fileOutputStream = null; fileOutputStream = new FileOutputStream(cacheDir + url); bmp.compress(Bitmap.CompressFormat.PNG, 100 , fileOutputStream); }catch (FileNotFoundException e){ e.printStackTrace(); }finally { CloseUtils.closeQuitely(fileOutputStream); } }
这个方法的基本原理就是依赖于Closeable抽象而不是具体实现,并且建立在最小化依赖原则的基础上,他只需要知道这个对象是否可关闭,其他的一概不关心
一个对象应该对其他对象有最少的了解
例子:在北京租房
/** * 房间 * */ public class Room{ public Room(float area ,float price){ this.area = area; this.price = price; } @Override public void toString(){ return "Roomasdsad" } } /** * 中介 * */ public class Mediator{ List<Room> mRooms = new ArrayList<>(Room)(); public Mediator(){ for(int i =0;i<5;i++){ mRooms.add(new Room(14+i,(14+i)*150)); } } public List<Room> getAllRooms(){ return mRooms; } } /** * 租户 * */ public void Tenant{ public float roomArea; public float roomPrice; public static final float diffPrice = 100.0001f public static final float diffArea = 0.00001f; public void rentRoom(Mediator mediator){ List<Room> rooms = mediator.getAllRooms(); for(Room room : rooms){ if(isSuitable(room)){ System.out.println("租到房间啦 !" +room); break; } } } }
可以看出Tenant不仅依赖Mediator类,还需要频繁地与Room类打交道
,租户类的要求只是通过中介找到一间适合自己的房间罢了,如果把这些检测条件都放在Tenant类中,那么中介类的功能就被弱化了,而导致Tenant与Room的耦合较高
(会导致当Room变化时Tenant也必须跟着变化,Tenant又与Mediator耦合)
/** * 房间 * */ public class Room{ public Room(float area ,float price){ this.area = area; this.price = price; } @Override public void toString(){ return "Roomasdsad" } } /** * 中介 * */ public class Mediator{ List<Room> mRooms = new ArrayList<>(Room)(); public Mediator(){ for(int i =0;i<5;i++){ mRooms.add(new Room(14+i,(14+i)*150)); } } public void rentOut(float area,float price){ for(Room room : rooms){ if(isSuitable(area ,price ,room)){ System.out.println("租到房间啦 !" +room); break; } } private boolean isSuitable(float area , float price,Room room){ return Math.abs(room.price - price )<Tenant.diffPrice &&Math.abs(room.area -area ) - Tenant.diffprice; } } } /** * 租户 * */ public void Tenant{ public float roomArea; public float roomPrice; public static final float diffPrice=100.0001f public static final float diffArea=0.00001f; public void rentRoom(Mediator mediator){ System.out.println("租到房了"+mediator.rentOut(roomArea,roomPrice)) } }
这里只是将对于room的判定操作转移到了Mediator类中,这本应该是Mediator的职责