完成Lab3後對代碼可複用性的一些思考

在完成Lab3的過程中,我大概嘗試了三種ADT設計方法。最開始使用的是樸素的方法(即對於一個具體實例而言,寫一個特定的類提供給該實例使用),這種方法自然很快就被否決了;於是對delegation進行了嘗試,也實現了全部功能,個人感覺可複用性是較好的;最後在課程中學習了decorator設計方法後,對decorator進行了嘗試,並作爲了最終版本呈現在了實驗代碼中。這篇博客,主要想對後兩種方法進行比較,討論一下哪種模式更有利於提高代碼的可複用性。

一、Delegation(委派)
        delegation的設計思想還是很顯然的。例如在Lab3中,每一個計劃項都需要對Resource,Location,Time和State這四類屬性進行相應操作。那麼根據delegation的思想,在計劃項類中就不再具體實現這些操作方法,而是委派給R,L,T,S這四個接口去實現;在每一個接口實現的操作中,可能需要滿足不同的需求,那麼爲該接口構建不同的接口實現類,在具體的計劃項中調用需要的接口實現類即可(比如在Plane實例中可能存在兩個location,那麼Plane調用的就是Location接口下的MultiLocation實現類)。由於使用delegation思想設計的代碼已經被刪除了,不能展示相關代碼,我這裏就暫且用一張圖來演示。
在這裏插入圖片描述
設計的邏輯如上圖所示,PlanningEntry通過調用delegattion接口中的不同類實現自身的個性化需求。


二、Decorator(裝飾)
        decorator的實現相對比較複雜,我們就拿Location的裝飾來解釋。下面我們來看代碼。

//首先我們需要創建PlanningEntry接口
public interface PlanningEntry<R> {
	
	/**
	 * get the list of locations in the entry
	 * 
	 * @return the list of locations in the entry
	 */
	public List<Location> getLocations();
	
	/**
	 * change the location of the entry
	 * 
	 * @param SetLocation
	 * @return true if the location can be changed; false otherwise
	 */
	public boolean changeLocation(Location SetLocation);
}


//然後創建最一般的PlanningEntry實例,它的設計思路採用delegation,調用一個普通的Location委派類
public class Normal<R> implements PlanningEntry<R> {
	private LocationEntry location;
	
	public Normal(LocationEntry SetLocation) {
		location = SetLocation;
	}
	
	@Override
	public List<Location> getLocations() {
		return location.getLocations();
	}

	@Override
	public boolean changeLocation(Location SetLocation) {
		return location.changeLocation(SetLocation);
	}
}

代碼寫到這裏我們就可以實現一個調用一般的location方法的計劃項了。接下來就要通過decorator實現個性化操作。

//首先創建一個抽象類作爲PlanningEntry接口的子類
public abstract class LocationDec<R> implements PlanningEntry<R> {
	protected PlanningEntry<R> entry;
	
	public LocationDec(PlanningEntry<R> SetEntry) {
		entry = SetEntry;
	}
	
	@Override
	public abstract List<Location> getLocations();
	
	@Override
	public abstract boolean changeLocation(Location SetLocation);
}

//接着創建抽象類的子類,不需要個性化的方法直接調用entry的delegation實現即可,而需要個性化的方法進行具體的重寫
//例如這裏的MultiLocationEntry,changeLocation功能不可用,所以直接在方法的實現中return false即可實現個性化操作
public class MultiLocationEntry<R> extends LocationDec<R> {
	public MultiLocationEntry(PlanningEntry<R> SetEntry) {
		super(SetEntry);
	}
	
	@Override
	public List<Location> getLocations() {
		return entry.getLocations();
	}

	@Override
	public boolean changeLocation(Location SetLocation) {
		return false;
	}
}

那麼對於Location的裝飾,我們就做好了。如果想要新建一個調用MultiLocation的計劃項,我們只需要“做一個套娃”就行了。

PlanningEntry<R> entry = new MultiLocationEntry<R>(new Normal<R>(...)); //Normal的初始化省略了

同樣的,當我們構建好了四個decorator後,只要一層一層的把裝飾套進去。

//PlaneEntry的工廠方法
public class FlightFactory {
	public static PlanningEntry<Plane> getEntry(LocationEntry location, ResourceEntry<Plane> resource,
			EntryState state, TimeEntry time){
		return new UnBlockableStateEntry<Plane>(new MultiLocationEntry<Plane>(new Normal<Plane>(location, resource, state, time)));
	}
}

//TrainEntry的工廠方法
public class TrainFactory {
	public static PlanningEntry<Train> getEntry(LocationEntry location, ResourceEntry<Train> resource,
			EntryState state, TimeEntry time, List<Integer> block) {
		return new BlockableTimeEntry<Train>(new MultiLocationEntry<Train>(
				new Normal<Train>(location, resource, state, time)), block);
	}
}

//CourseEntry的工廠方法
public class CourseFactory {
	public static PlanningEntry<Teacher> getEntry(LocationEntry location, ResourceEntry<Teacher> resource,
			EntryState state, TimeEntry time) {
		return new UnBlockableStateEntry<Teacher>(new Normal<Teacher>(location, resource, state, time));
	}
}

怎麼樣,是不是覺得這樣套娃很方便而且很有成就感。但是我想在這裏潑一盆冷水,decorator也沒有我們看上去這麼高效便捷。首先看上面的代碼就可以看出來實現一個decorator的過程其實還是相當繁瑣的,如果僅僅爲了一種個性化裝飾而開發一整套裝飾流程顯得大材小用了一點;其次通過上面的代碼感受不到的是,一個計劃項涉及到的方法可能有十多種,但是一次裝飾可能只修改其中一兩個方法,其餘的方法仍需要通過機械化的Override(如下圖所示),這是decorator作爲PlanningEntry接口實現類的一個不可避免的侷限性。(當然這也可能是我自身設計的問題,希望大家能解答我的疑惑)

        注:如上圖所示,實際的MultiLocationEntry裝飾類中,存在大量的重複化機械化的delegation
所以這樣來看,decorator在Lab3中的編寫效率可能還不如delegation好,只是當我意識到這樣一個情況的時候,decorator的設計模式已經完成,而且decorator設計完成後對計劃項的新建和調用真的十分方便,所以就使用了它。(但這並不意味着decorator在Lab3中就是最佳設計方案吧)

總結

我們不能夠簡單扣上一個decorator一定比delegation好用的帽子,而是要具體問題具體分析。對於那些分類情況多,可能存在組合爆炸的問題而言,使用decorator可能是一個很高效的選擇;但是對於Lab3而言,僅僅存在三種組合,那麼可能直接的delegation反而可以更加高效。所以我們在設計ADT之前,一定要考慮清楚利弊再下手,我就是缺乏這方面的經驗才幾次三番的改方案,走了很多彎路。當然,對於我這樣的新手,走些彎路,體驗各種各樣的設計模式,也許本身是件好事。

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