代理模式
所謂代理模式就是在進行交互的時候,不和原本的對象直接交互,而是通過代理的方式,用代理來代替真正的對象進行交互,這樣做的好處是降低了耦合性。
代理模式也是平時比較常用的設計模式之一,代理模式其實就是提供了一個新的對象,實現了對真實對象的操作,或成爲真實對象的替身.在日常生活中也是很常見的.例如A要租房,爲了省麻煩A會去找中介,中介會替代A去篩選房子,A坐享中介篩選的結果,並且交房租也是交給中介,這就是一個典型的日常生活中代理模式的應用.平時打開網頁,最先開到的一般都是文字,而圖片等一些大的資源都會延遲加載,這裏也是使用了代理模式.
代理模式的經常使用方式大約有三種,
1,普通代理:
所謂普通代理,就是創建一個和真是對象相同的類,將真是的對象當作參數傳入到代理中,當外界通過代理進行操作的時候,代理中的真實對象也會進行相應的操作。
步驟很簡單:
1,創建接口,定義共同方法。
2,創建真實類,實現接口
3,創建代理類實現接口,但是增加一步,在構造方法中將真實對象的引用當作參數,拿到真實對象。
4,得到代理對象,通過對代理對象操作,實現真實對象的相應操作
案例如下:
public interface IHouse {
05. void getHouseInfo();
06. void signContract();
07. void payFees();
08.}
接下來定義真實主題,並實現IHouse接口.增加房屋名稱和價格兩個屬性,填充藉口方法,在獲取房屋信息的時候就把房屋名稱和價格log出來;籤合同的時候log出籤合同的時間,付租金的時候log出價格.
01.public class House implements IHouse{
02. private final String TAG = House.class.getSimpleName();
03. private String name;
04. private double price;
05.
06. public House(String name, double price){
07. this.name = name;
08. this.price = price;
09. }
10.
11. @Override
12. public void getHouseInfo() {
13. Log.i(TAG, "House Info- name:" + name + " ¥:" + price);
14. }
15.
16. @Override
17. public void signContract() {
18. Log.i(TAG, "Contract:" + name + " signed at" +
19. new SimpleDateFormat("HH:mm:ss").format(SystemClock.uptimeMillis()));
20. }
21.
22. @Override
23. public void payFees() {
24. Log.i(TAG, "Bill: name-" + name + " $-" + price);
25. }
26.}
定義房屋代理,同樣需要實現IHouse接口,並持有House的引用.可以看到代理類其實就像有封裝House,提供了一些附加操作,例如客戶要看房子的時候代理會先檢索自己庫存的房屋信息,籤合同之前要準備合同之類的.
01.public class ProxyHouse implements IHouse{
02. private final String TAG = ProxyHouse.class.getSimpleName();
03. private IHouse house;
04. public ProxyHouse(IHouse house){
05. this.house = house;
06. }
07. @Override
08. public void getHouseInfo() {
09. Log.i(TAG, "searching");
10. house.getHouseInfo();
11. Log.i(TAG, "search finished");
12. }
13.
14. @Override
15. public void signContract() {
16. Log.i(TAG, "prepare contract");
17. house.signContract();
18. }
19.
20. @Override
21. public void payFees() {
22. house.payFees();
23. }
24.}
對於客戶來說,完全不用跟House進行直接交互,這裏先定義一個房子叫唐頓莊園,租金5k,建立一個房屋代理,把唐頓莊園委託給代理.客戶要找房子,籤合同,付租金直接找代理就行了.
01.IHouse house = new House("Downton Abbey", 5000);
02.IHouse proxyHouse = new ProxyHouse(house);
03.Log.i(TAG, "looking for a perfect house");
04.proxyHouse.getHouseInfo();
05.Log.i(TAG, "thinking");
06.proxyHouse.signContract();
07.proxyHouse.payFees();
08.Log.i(TAG, "so easy");
2.虛擬代理
接下來再說一說虛擬代理,其實也可以叫做延時代理,他的思想就是在真實對象數據量非常大的時候通過代理先代替真實對象,當真正操作真實對象的時候在加載真實對象,這樣做能夠在很大程度上提高效率。
案例如下:
- public ProxyHouse(){
- if (null == house)
- house = new House("Downton Abbey", 5000);
- }
public ProxyHouse(){ if (null == house) house = new House("Downton Abbey", 5000); }
- IHouse proxyHouse = new ProxyHouse();
- Log.i(TAG, "looking for a perfect house");
- proxyHouse.getHouseInfo();
- Log.i(TAG, "thinking");
- proxyHouse.signContract();
- proxyHouse.payFees();
- Log.i(TAG, "so easy");
3,強制代理
強制代理其實就是通過對象找代理,正好和代理的思想相反,
步驟簡單:
1,創建接口
2,實現接口,
3,調用
- public interface IHouse {
- void getHouseInfo();
- void signContract();
- void payFees();
- IHouse getProxy();
- }
public class House implements IHouse{
02. private final String TAG = House.class.getSimpleName();
03. private String name;
04. private double price;
05. private IHouse proxy;
06.
07. public House(String name, double price){
08. this.name = name;
09. this.price = price;
10. }
11.
12. @Override
13. public void getHouseInfo() {
14. if (isProxy())
15. Log.i(TAG, "House Info- name:" + name + " ¥:" + price);
16. else
17. Log.i(TAG, "Please use correct proxy");
18. }
19.
20. @Override
21. public void signContract() {
22. if (isProxy())
23. Log.i(TAG, "Contract:" + name + " signed at" +
24. new SimpleDateFormat("HH:mm:ss").format(SystemClock.uptimeMillis()));
25. else
26. Log.i(TAG, "Please use correct proxy");
27.
28. }
29.
30. @Override
31. public void payFees() {
32. if (isProxy())
33. Log.i(TAG, "Bill: name-" + name + " $-" + price);
34. else
35. Log.i(TAG, "Please use correct proxy");
36. }
37.
38. @Override
39. public IHouse getProxy() {
40. if (null == proxy)
41. proxy = new ProxyHouse(this);
42. return proxy;
43. }
44.
45. private boolean isProxy(){
46. if (null == proxy)
47. return false;
48. else
49. return true;
50. }
51.}
通過真實的對象調用方法得到代理的對象,個人感覺這種代理的用途不是太好。
動態代理
最後就是動態代理了,動態代理是針對當需要的代理非常多的情況下,如果使用普通代理的話,難免需要創建多個相同的代理類,因爲普通代理都必須創建一個和真實類相同的類,所以動態代理使用動態的方式減少了大量冗餘類的出現,給代理增添了擴展性。 下面簡單說一下動態代理: 說到動態代理必須要說兩個類 1,Proxy類,Proxy這個類的作用就是用來動態創建一個代理對象的 2,InvocationHandler每一個動態代理類都需要實現的接口。 他有一個抽象方法: proxy: 指代我們所代理的那個真實對象 method: 指代的是我們所要調用真實對象的某個方法的Method對象 args: 指代的是調用真實對象某個方法時接受的參數 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return null; } 下面看一組完整的案例: public interface Subject { public void rent(); public void hello(String str); } 接着,定義了一個類來實現這個接口,這個類就是我們的真實對象,RealSubject類: public class RealSubject implements Subject { @Override public void rent() { System.out.println("I want to rent my house"); } @Override public void hello(String str) { System.out.println("hello: " + str); } } 下一步,我們就要定義一個動態代理類了,前面說個,每一個動態代理類都必須要實現 InvocationHandler 這個接口,因此我們這個動態代理類也不例外: public class DynamicProxy implements InvocationHandler { // 這個就是我們要代理的真實對象 private Object subject; // 構造方法,給我們要代理的真實對象賦初值 public DynamicProxy(Object subject) { this.subject = subject; } @Override public Object invoke(Object object, Method method, Object[] args) throws Throwable { // 在代理真實對象前我們可以添加一些自己的操作 System.out.println("before rent house"); System.out.println("Method:" + method); // 當代理對象調用真實對象的方法時,其會自動的跳轉到代理對象關聯的handler對象的invoke方法來進行調用 method.invoke(subject, args); // 在代理真實對象後我們也可以添加一些自己的操作 System.out.println("after rent house"); return null; } } 最後,來看看我們的Client類: public class Client { public static void main(String[] args) { // 我們要代理的真實對象 Subject realSubject = new RealSubject(); // 我們要代理哪個真實對象,就將該對象傳進去,最後是通過該真實對象來調用其方法的 InvocationHandler handler = new DynamicProxy(realSubject); /* * 通過Proxy的newProxyInstance方法來創建我們的代理對象,我們來看看其三個參數 * 第一個參數 handler.getClass().getClassLoader() ,我們這裏使用handler這個類的ClassLoader對象來加載我們的代理對象 * 第二個參數realSubject.getClass().getInterfaces(),我們這裏爲代理對象提供的接口是真實對象所實行的接口,表示我要代理的是該真實對象,這樣我就能調用這組接口中的方法了 * 第三個參數handler, 我們這裏將這個代理對象關聯到了上方的 InvocationHandler 這個對象上 */ Subject subject = (Subject)Proxy.newProxyInstance(handler.getClass().getClassLoader(), realSubject .getClass().getInterfaces(), handler); System.out.println(subject.getClass().getName()); subject.rent(); subject.hello("world"); } } Proxy這個類的作用就是用來動態創建一個代理對象的類 public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException loader: 一個ClassLoader對象,定義了由哪個ClassLoader對象來對生成的代理對象進行加載 interfaces: 一個Interface對象的數組,表示的是我將要給我需要代理的對象提供一組什麼接口,如果我提供了一組接口給它,那麼這個代理對象就宣稱實現了該接口(多態),這樣我就能調用這組接口中的方法了 h: 一個InvocationHandler對象,表示的是當我這個動態代理對象在調用方法的時候,會關聯到哪一個InvocationHandler對象上