簡單工廠
最近發現同事都會物件導向,也會一些設計模式,也常請教對方一些觀念的問題,卻發現他們從來不寫,讓我非常錯愕,為什麼會這技術,卻不用來寫程式,問比較熟的工程師後,才了解原來會不代表會用,怎麼說呢,不知道用在哪,不常用、不熟悉...等。
分享一下我最近練習寫的簡單工廠的過程,跟大家交流一下經驗。
很久以前需要一個1~10的亂數,所以寫了下面的程式
static void Main(string[] args)
{
Random r = new Random();
int minValue = 1;
int maxValue = 10;
int randomNumber = r.Next(minValue, maxValue);
Console.WriteLine(randomNumber);
}
然後有一天業主認為這樣的亂數產生不夠好,他需要隨機密碼的安全亂數,因此找到了保哥佛心寫的一個亂數產生類別,
這時候依照以往的寫法先新增一個類別將網路上人家佛心提供的程式貼過去,接著將原先程式改成
static void Main(string[] args)
{
int minValue = 1;
int maxValue = 10;
int randomNumber = RNG.Next(minValue, maxValue);
Console.WriteLine(randomNumber);
}
改成這樣,耗費的時間也不多,不過要考慮到因為案例簡單,如果複雜一點改code的時候,通常都會一直碎碎念。
後天,當我還在洋洋得意已經寫完的時候,專案經理突然跟我說,業主覺得上次他要求的可有可無,還是保留原有的就好,當下我當場吐血,此時又不能跟專案經理說,已經寫完,寫完也不報備,專案經理會說,那這兩天你在做什麼,要改回去,或許比較容易一點,去版本控管找出舊的版本蓋回去,好吧,當場跟專案經理說,那我用版本控管回復最初的版本。
不過這個舉動將為我未來的加班時數多添增好幾個小時,因為當周星期五,業主反覆思考,下了一個重大的決定,他決定還是安全點好,所以他還是要安全的亂數。
此時回想起那本遲遲沒看完的敏捷軟體開發,記得裡面有一句話的意思是,可以當第一次笨蛋,畢竟我不聰明,但絕對不能當第二次笨蛋。此時我根本就是全天下最蠢的傢伙,明知道會一直變動,業主的心就像海底針.....。
所以此時簡單工廠就派上用場了,我將保哥寫的亂處產生的類別由靜態改為實例,接著很偷懶的直接使用Microsoft Visual Studio 2010 重構的功能,
擷取出IRandom
public interface IRandom
{
int Next();
int Next(int max);
int Next(int min, int max);
}
然後將最原先的亂數產生的程式碼,整理出並實作IRandom介面,
public class MyRandom:IRandom
{
Random rd = new Random();
public int Next()
{
return rd.Next();
}
public int Next(int max)
{
return rd.Next(max);
}
public int Next(int min, int max)
{
return rd.Next(min, max);
}
}
接著寫一個靜態類別,這個類別主要為類別與客戶端之間的接口,也就是客戶端不需要知道有哪些亂數產生的方法,只要知道需要用哪一種亂數產生器。
public class RandomFactory
{
public enum RandomType{RNG,MyRandom}
public static IRandom CreateRandom(RandomType type)
{
IRandom tmpRef;
switch (type)
{
case RandomType.RNG:
tmpRef = new RNG();
break;
case RandomType.MyRandom:
tmpRef = new MyRandom();
break;
default:
throw new Exception("無此型別!!");
}
return tmpRef;
}
}
用到列舉,主要避免客戶端會使用一些根本就沒有的亂數產生器,例如、好亂的亂數,這種東西傳進來也只會進入例外處理,而無此型別,其實可以讓它出現機會更渺茫一點,所以這邊用列舉。
最後我不怕業主改來改去了,他要哪個我就給他哪個,只需要改參數就可以了
static void Main(string[] args)
{
int minValue = 1;
int maxValue = 10;
IRandom r = RandomFactory.CreateRandom(RandomFactory.RandomType.MyRandom);
Console.WriteLine(r.Next(minValue, maxValue));
IRandom rng = RandomFactory.CreateRandom(RandomFactory.RandomType.RNG);
Console.WriteLine(rng.Next(minValue, maxValue));
}
結語:
雖然以上程式碼,還有許多可以修改的地方,不過大致上滿足了開放與封閉原則,將會變動的地方封裝起來,也就是RandomFactory類別,因為不知道業主到底哪一種亂數產生器,所以這部分是會變動的,因此將這部分封裝起來,在客戶端,也盡量符合依賴抽象類別,不依賴具體類別,以便於未來抽換。
未來建議與問題:
寫完以上這個練習後,我發現我在撰寫抽象介面時,太偷懶了,在未來如果某個亂數產生的類別新增方法,就必須變動IRandom,這問題還在研究當中,還未參透ISP介面分割原則,至今已經吃了好幾次悶虧,經常變動介面,導致要修改實作的介面,這問題近期一直苦惱著我,另外,簡單工廠當有新增類別時,以上面例子來說,還要新增列舉與switch理的條件式,這部分我稍微研究了一下,有一個工廠模式,但是運用工廠模式還是要看狀況,不然複雜度的壞味道就跑出來了,再來,如果確定知道未來會不斷增加亂數產生的類別,switch可以改為責任鏈模式。
如文章有錯誤,煩請告知,新人發帖請多包涵