Java中關於Decorator(裝飾),Delegation(委派),Proxy(代理)的一些理解

        在正式學習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中的一些想法,希望這些博客能給大家一些啓發吧。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章