最近一直在看java的設計模式 ,感覺印象最深刻的就是"面向接口編程",在java中,盡量多定義接口,因為設計的最重要的目的就是應對各種變化,而接口本身就是一種變化着的Class的直接反映,通過實現特定接口的各種具體類,達到應對變化的目的,下面以Proxy模式為例:
Proxy的模式最主要的目的,原有的類對象由於某種原因不能訪問,需要通過一個新的類來間接地去實現,這個新的類就稱為代理類,舉個例子說明,老王買/賣魚的例子
public
class
SellFisher
{
public int sellFish() {
System.out.println("my fish is delicious!!");
return 10;
}
}






這是一個具體的賣魚類,表示魚10元/斤,如果這個類被用到系統中的時候,系統應對變化的靈活性就會大打折扣,請看如下:







如果以后魚的價格變化,或者具體的賣魚方法發生變化,就必須修改已經有的SellFisher的sellFish()的代碼,這個情況
使得系統的可擴展性降低,我們肯定會想到解決方案了,定義一個接口,請看代碼:









我們所做的變化,只是把SellFisher從Class提升到Interface,這個時候好處自然很明顯了,這個SellFisher本身就代表
了一種不確定,變化.大家在做開發的時候,閱讀源代碼的時候,肯定遇到過這種情況,總是跟蹤類的對象看代碼實現,如果發現了接口變量,就會比較郁悶,得了解它的具體實現類是什么,這個具體實現類的變化通常就可以應對需求的變化,系以及系統擴展.請看上例子,如果魚的價格變化,或者具體的賣魚方法發生變化,我們只需要新增加SellFisher的實現,
而不必修改已有的代碼,對此我的理解是對系統來說,
新增加一個類的代碼風險要大大低於對已有類的代碼的修改.我覺得這個也是設計模式的立足點吧(如果你喜歡修改已有代碼,那么設計模式就沒有多大意義了)
言歸正傳,有了上面的知識准備,我們接上面的例子來解釋Proxy的模式就簡單多了,比如現在魚的價格變化,賣魚的提示也發生變化了,我們需要一個新的Proxy類來實現
interface
SellFisher {
int sellFish();
}
public class ConcreteSellFisher implements SellFisher {
public int sellFish() {
System.out.println( " my fish is delicious!! " );
return 10 ;
}
}
public class ProxySellFisher implements SellFisher {
private SellFisher sell;
public ProxySellFisher(SellFisher sell) {
this .sell = sell;
}
public int sellFish() {
System.out.println( " the fish price higher " );
return sell.sellFish() + 10 ;
}
}
int sellFish();
}
public class ConcreteSellFisher implements SellFisher {
public int sellFish() {
System.out.println( " my fish is delicious!! " );
return 10 ;
}
}
public class ProxySellFisher implements SellFisher {
private SellFisher sell;
public ProxySellFisher(SellFisher sell) {
this .sell = sell;
}
public int sellFish() {
System.out.println( " the fish price higher " );
return sell.sellFish() + 10 ;
}
}
看上面,這個是Proxy模式的代碼例子實現,我們現在在SellFishSystem 使用ProxySellFisher來賣魚了,由ProxySellFisher再調用原來的ConcreteSellFisher類.具體的一些特征總結為:
1.有個代理類Proxy(和原來的實現類繼承同一接口),該類里引用了原來的具體功能實現類(這里是ConcreteSellFisher)
2.重寫實現方法,加一些新的變化的元素(比如魚的價格上漲)
JDK里的Proxy類也實現了這個模式,只不過它叫動態代理,因為它的代理類變成了InvocationHandler了,執行的方法
是invoke了,從而變得更加靈活了,請看代碼
public
class
ProxySellFisher
implements
InvocationHandler {
private SellFisher sell;
public ProxySellFisher(SellFisher sell) {
this .sell = sell;
}
public Object invoke(Object obj, Method method, Object[] args) throws Throwable {
System.out.println( " the fish price higher " );
return (Integer)method.invoke(sell, args) + 10 ;
}
}
public class ClientTest {
public static void main(String args[]) {
SellFisher s = new ConcreteSellFisher();
InvocationHandler p = new ProxySellFisher(s);
Object obj = Proxy.newProxyInstance(s.getClass().getClassLoader(), s.getClass().getInterfaces(), p);
((SellFisher)obj).sellFish();
}
}
private SellFisher sell;
public ProxySellFisher(SellFisher sell) {
this .sell = sell;
}
public Object invoke(Object obj, Method method, Object[] args) throws Throwable {
System.out.println( " the fish price higher " );
return (Integer)method.invoke(sell, args) + 10 ;
}
}
public class ClientTest {
public static void main(String args[]) {
SellFisher s = new ConcreteSellFisher();
InvocationHandler p = new ProxySellFisher(s);
Object obj = Proxy.newProxyInstance(s.getClass().getClassLoader(), s.getClass().getInterfaces(), p);
((SellFisher)obj).sellFish();
}
}
請注意,
invoke(Object obj,Method method,Object[] args),這里的第一個參數obj其實可以看作沒有用處的,不知道jdk為什么要把它也當作一個參數放這里,methd.invoke()方法,需要把原來的具體實現類作為參數傳遞進去,method.invoke(obj,args)相當於obj.method(args)
總結,從設計模式的角度講,大家以后編程中,盡量要面向接口,更通俗一點就是,
一個類中使用的別的對象成員變量,最好定義成接口的變量而不是實際實現類的變量