開發的過程當中會不可避免的引用一些第三方庫,好比網絡請求庫、圖片加載庫等等。就拿圖片加載庫來講,程序中不會只有一個地方來引用到此庫,可能有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類便可,而代理類不須要任何變更即可以在不一樣動態庫之間切換。