在正式學習4.2節關於委派的內容之前,我對於選擇何種方式實現Lab3以提高代碼的複用性感到十分困惑。直到我查閱到這樣一篇博客:Java設計模式 — Decorator(裝飾),Delegation(委託) ,Proxy(代理),有了一些想法。這篇博客中的例子對於理解裝飾,委派和代理有很大的幫助,也能加深對課程中4.2節相關知識的理解,感興趣的小夥伴可以直接閱讀原博客,當然我下面也會依據我的理解進行改編。
我們先來一個生動形象的例子:衆所周知,哈工大即將迎來百年校慶,屆時會有許多校友回到母校參加。爲了選擇合適的交通工具來哈爾濱,需要建造一個票價查詢系統。我們姑且把來哈爾濱的交通工具框定在火車和飛機這兩種類型上面,但是會發現不管是火車還是飛機都有不同的艙位選擇(比如飛機有頭等艙,商務艙和經濟艙)。如果火車和飛機各有三種倉位,組合一下就會出現六個票種。如果我們爲每一個票種寫一個單獨的類,那麼工程量巨大並且不可避免一些代碼可複用性低造成的風險。因此,我們就需要進入我們的第一個主題,Decorator(裝飾)。
一. Decorator(裝飾)
裝飾顧名思義就是要給一個我們已有的東西加一些點綴,使它與之前有所不同。例如一位校友在來哈爾濱的行程中選擇了乘坐飛機的經濟艙,但是他臨時改變了主意,將經濟艙升爲了商務艙,這個升艙的過程就可以視作是他給他的機票進行了裝飾。下面我們來看如果運用裝飾來實現不同票價表示的例子。
我們先定義一個普通的火車票和機票
// 交通工具的接口
public interface Transportation {
public double price(); // 價格
}
// 普通的火車票
public class Train implements Transportation{
private final double trainPrice = 100;
@Override
public double price() {
return trainPrice;
}
}
// 普通的飛機票
public class Plane implements Transportation{
private final double planePrice = 500;
@Override
public double price() {
return planePrice;
}
}
接下里,我們要對普通的火車票和機票加一些裝飾
// 裝飾類
public abstract class TransDecorator implements Transportation {
protected Transportation trans;
public TransDecorator(Transportation SetTrans) {
trans = SetTrans;
}
@Override
public abstract double price();
}
// 頭等艙
public class FirstClass extends TransDecorator{
private final double addRate = 1.0;
public FirstClass(Transportation SetTrans) {
super(SetTrans);
}
@Override
public double price() {
return trans.price() * (1+addRate); //升艙需要在原有票價基礎上提高
}
}
// 商務艙
public class SecondClass extends TransDecorator{
private final double addRate = 0.5;
public SecondClass(Transportation SetTrans) {
super(SetTrans);
}
@Override
public double price() {
return trans.price() * (1+addRate); //升艙需要在原有票價基礎上提高
}
}
於是,調用這些裝飾,我們就能得到以下六類票:
Transportation normalPlane = new Plane(); // 經濟艙飛機,500
Transportation secondClassPlane = new SecondClass(new Plane()); // 商務艙飛機,750
Transportation firstClassPlane = new FirstClass(new Plane()); // 頭等艙飛機,1000
Transportation normalTrain = new Train(); // 普通火車,100
Transportation secondClassTrain = new SecondClass(new Train()); // 二等座火車,150
Transportation firstClassTrain = new FirstClass(new Train()); // 一等座火車,200
如果說,飛機和火車上還存在各種服務(比如火車上的餐車),就會出現更多種類的票價,我們可以通過加入更多的修飾來實現,這裏我就不再枚舉了。
那我們再回到剛纔的例子中,這次我們把範圍縮小一點,只取一個乘客進行討論:HTY雖然不是校友,但是他也需要買票(什麼時候能開學吶)。但是買票這種事情,不是說你上飛機的時候把錢拍到機長手裏就可以的,而是要提前在網站上買好票。於是我們又要進入下一個主題,Delegation(委派)。
二. Delegation(委派)
這也是一個很生活化的詞語。當有些事情我們不想做 做不到的時候,就需要伸手委託他人幫助,這一個行爲我們稱之爲白嫖委派。例如上課舉的例子:請律師打官司等等。 所以在剛纔那個例子中,HTY作爲一名乘客本身買不了機票,需要藉助購票網站購買機票,也就是在使用委派的操作。下面我們來看代碼是如何實現的。
// 乘客接口
public interface Passengers {
public double spend();
public void arrival();
}
// 一名乘客
public class Passenger implements Passengers{
private transPlotform plotform; // 委派購票平臺購買
public Passenger(int trans, int category) {
plotform = new transPlotform(trans, category);
}
@Override
public double spend() {
return plotform.price();
}
@Override
public void arrival() {
System.out.printf("Arrive at HIT, spend ¥%.1f\n", spend());
}
}
// 購票平臺
public class transPlotform implements Transportation {
Transportation trans;
public transPlotform(int SetTrans, int SetCategory) { // SetTrans = 0(火車)/1(飛機), SetCategory = 0(普通)/1(二等)/2(一等)
if (SetTrans == 0) trans = new Train();
else trans = new Plane();
if (SetCategory == 1) trans = new SecondClass(trans);
else if (SetCategory == 2) trans = new FirstClass(trans);
}
@Override
public double price() {
return trans.price();
}
}
於是,我們可以得到下面的結果:
Passengers hty = new Passenger(1, 1); // 買了一張商務艙的機票
hty.arrival();
// out: Arrive at HIT, spend ¥750.0
我們可以看到HTY本身並沒有買票,而是委託購票平臺買了票,他做的是支付購票的錢。
我們還是要回到剛纔的例子中來:HTY比較窮,但是更不幸的是,他還很蠢,分不清自己的錢買得起什麼票。於是他需要一個人告訴他,他當前買的票是否付得起。這裏就要引出我們的最後一個主題,Proxy(代理)。
三. Proxy(代理)
代理也很好理解,例如很多國外的遊戲公司只精通開發遊戲,而怎麼在中國賣遊戲,他們並不擅長,於是就需要找某訊(沒有打廣告)等一些公司代替他們宣傳和銷售。當大家看完接下來的代碼後,可能會有一個困惑,代理和委派好像是一樣的。其實不然,仔細比較兩者的代碼,你會發現:代理本身什麼也不做,只是在調用被代理類的方法;而委派,是要調用自己的方法解決客戶需求的。(這麼一看,是不是覺得代理商都是些薅羊毛的奸商呢
// 代理類
public class smartPassenger implements Passengers {
private Passenger passenger;
public smartPassenger(Passenger SetPass) {
passenger = SetPass;
}
@Override
public double spend() {
return 0;
}
@Override
public void arrival() {
if (passenger.spend() > 500) { //如果乘客購買的票貴於500
System.out.println("You don't have enough money! Stay at home!");
}
else {
passenger.arrival();
}
}
}
我們來看上面這個代理類能幹什麼
Passengers hty = new Passenger(1, 1); // HTY企圖買一張商務艙機票
Passengers xxd = new smartPassenger((Passenger)hty); // 這時XXD突然出現,代替HTY決策一下該不該買這張票
xxd.arrival(); // 發現HTY並沒有這麼多錢,於是讓他老老實實呆在了家裏
hty = new Passenger(1, 0); // HTY只能重新選擇了一張經濟艙的機票
xxd = new smartPassenger((Passenger)hty); // XXD再次審覈
xxd.arrival(); // 發現錢剛好夠,於是HTY可以高高興興的回學校了
結論
以上討論的Decorator(裝飾),Delegation(委派),Proxy(代理)這三種操作,在實際的代碼編寫中具有很大的價值,我也只是根據上文給出的原博客進行了一些改編並加入自己的理解。至於深入理解乃至熟練運用,還需要在實踐中多加練習,例如正在進行的Lab3,就有很多機會運用這些操作。我也會 可能在接下來的博客中介紹自己在設計Lab3中的一些想法,希望這些博客能給大家一些啓發吧。