开发的过程当中会不可避免的引用一些第三方库,好比网络请求库、图片加载库等等。就拿图片加载库来讲,程序中不会只有一个地方来引用到此库,可能有N个类会用到此库来显示图片。当咱们后期须要更换其它第三方库时问题就来了,咱们得改多少代码,程序中到底有多少地方引用了这个第三方库,万一改代码的时候引起了其余的bug怎么办。java
问题来了咱们应该如何避免这种“牵一发而动全身”的囧况。
网络
图1,普通引用第三方库的作法
ide
图2,从新设计以后的引用流程this
从上图咱们能看到咱们经过一个中间层来引用“第三方图片加载库”。这样作的好处是无论第三方图片加载库你换成Picasso仍是Glide咱们改变的只是这个中间层,其余的咱们一行代码都不须要改动。url
首先抽象一个ImageLoader接口设计
public interface ImageLoader() { void displayImage(String url, ImageView imageView, int defaultImage); }
好比当前是使用第一种第三方库First来展现项目中的图片,就建一个ImageLoaderSubject类来实现上面的接口代理
public class ImageLoaderSubject implements ImageLoader{ public ImageLoaderSubject() { } @Override public void displayImage(String url, ImageView imageView, int defaultImage) { First.loadImage(url, imageView, defaultImage); } }
接下来咱们写一个代理类来帮咱们实现图片加载的任务code
public class ImageLoaderProxy implements ImageLoader { private ImageLoader imageLoader;//做为代理类的一个属性 private ImageLoaderProxy imageLoaderProxy; public static ImageLoaderProxy newInstance() { if(imageLoaderProxy == null) { imageLoaderProxy = new ImageLoaderProxy(); } return imageLoaderProxy; } public ImageLoaderProxy() { this.imageLoader = new ImageLoaderSubject(); } @Override public void displayImage(String url, ImageView imageView, int defaultImage) { System.out.println("do some thing"); imageLoader.displayImage(url, imageView, defaultImage); System.out.println("do other thing"); } }
这样咱们在程序中再次引用第三方库时能够直接使用下面的方法对象
ImageLoaderProxy.newInstance().displayImage(url, imageView, defaultImage);
当咱们换成第二种第三方库来完成图片加载时 ,咱们只需为该第三方库新建一个调用的类ImageLoaderSubject2 实现,并将代理类中的这行代码 imageLoaderProxy = new UniversalImageLoader();换成imageLoaderProxy = new ImageLoaderSubject2() 便可。blog
还用一种动态代理的方法,能够没必要为特定的接口操做特定代理对象。可以使用一个处理者代理多个接口的操做对象。
处理者必须操做java.lang.reflect.InvocationHandler接口
设计一个ImagerLoaderHandler类
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class ImageLoaderHandler implements InvocationHandler { private Object target; public Object bind(Object target) { this.target = target; return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = null; try { result = method.invoke(target, args); } catch (IllegalAccessException | IllegalArgumentException e) { System.out.println(e.toString()); } return result; } }
在须要调用图片加载的地方使用ImageLoaderHandler的bind()方法来绑定被代理的对象。
ImageLoaderHandler imageLoaderHandler = new ImageLoaderHandler(); ImageLoader imageLoaderProxy = (ImageLoader) imageLoaderHandler.bind(new ImageLoaderSubject()); imageLoaderProxy.displayImage(url, imageView, defaultImage);
主要是使用Proxy.newProxyInstance()方法创建代理对象,调用时必须指定类加载器,告知要代理的借口,以及接口上定义的方法被调用时的处理者(InvocationHandler实例),Proxy.newProxyInstance()方法底层会使用原生方式生成代理对象的class实例,并利用它来生成代理对象,代理对象会指定要代理的接口。
这样使代理类更具备通用型,每次只须要改动ImageLoaderSubject类便可,而代理类不须要任何变更即可以在不一样动态库之间切换。