這個對於寫控制項應該很有幫助
需要時候找不到還滿麻煩的
所以就先抓下來
如果你在user control中有個DropDownList(或GridView等其他控制項)
也設了DataSource
但是在Page中的Page_Load抓不到值
看了下圖就應該知道這是正常的
因為當時Control還未data binding
改為在PreRenderComplete再抓資料即可
其他常用的如:PreInit, Init, PreLoad, Load, PreRender等順序如下圖
原網址:ASP.NET Page Life Cycle Overview (MSDN)
有很詳細介紹
//Rock's System Develop Log
/******************************************************************************************
踏入系統開發的工作已經十餘年了,
從 waterfall 走到 agile methodology;
從 procedure-oriented 走到 object-oriented
從 單打獨鬥 走到 teamwork;
從 programmer 走過 SA/SD 走到 team leader
這裡寫的東西一方面是為了內部教育訓練使用, 一方面也希望與各路先進一同研究
**********************************************************************************************/
2012年7月12日
2012年5月11日
白話 Design Pattern (七) Adapter Pattern, Abstract Server Pattern
想像一下,如果今天你的主管交代了一個任務,要你從一台老舊的電腦找一份遠古時期的資料。你試了一下,很幸運的它還能開機,但是,它竟然沒有USB介面來連接你的滑鼠,而這個年代,已經沒有人生產PS/2滑鼠了,要怎麼辦?難道要到拍賣場找一隻堪用的嗎?
如果你知道有『USB to PS/2 轉接頭』這樣的東西,相信這個問題根本不是問題。
事實上,生活中我們的確使用了許許多多的轉接頭,如:電源線三孔轉兩孔、手機用micro usb 轉 usb線、出國用萬用插座...,因為有了轉接器,讓我們可以在不同規格間使用某些設備。
Adapter Pattern正是轉接器。主要目的便是藉由轉接方式,解決介面不同的問題。它的結構也很簡單,就是一頭接我們要的介面,另一頭接提供功能的元件,然後作對應。
使用時機:通常都不是自己新開發的系統,而是為了讓舊有的元件得以繼續運作,或是為了某些外購的元件。
==================================
在Agile Software Development, Principles, Patterns, and Practices一書中,另外提到一個可搭配Adapter的Abstract Server Pattern,它一樣很簡單,卻也是常出現的基本概念。
Abstract Server Pattern就是在原本直接的關聯中加入一個抽象,好讓物件間的藕合度降低。簡單的說,就是DIP(依賴倒轉)原則。
書中的例子(圖上部),原本的設計將『燈』與『開關』直接關聯,這樣一來有開關就必須有燈不可。加入一個interface後,就能將其間的關聯性減輕,日後如果電扇要用這個開關,也只要繼承抽象的interface即可。
如果考慮到使用開關的物件可能來自外部元件或舊有的元件,而無法遵循現在開發的interface,那搭配了Adapter就能進一步達成(如下圖)。
本文將這些模式說得很簡單,其實也是想說其實設計模式未必是艱澀難懂,或需要強記的,事實上,即使是沒聽過設計模式的人,如果在撰寫程式時,考慮了減輕耦合、增加彈性之類的概念,也有可能設計出接近這樣的結構。
學習設計模式不是為了找地方套用這些架構,而是你遇到難以取捨之處,可以參考看看你的設計是否考慮周詳,故了解每個模式的精神、形成原因才是最重要的。
如果你知道有『USB to PS/2 轉接頭』這樣的東西,相信這個問題根本不是問題。
事實上,生活中我們的確使用了許許多多的轉接頭,如:電源線三孔轉兩孔、手機用micro usb 轉 usb線、出國用萬用插座...,因為有了轉接器,讓我們可以在不同規格間使用某些設備。
Adapter Pattern正是轉接器。主要目的便是藉由轉接方式,解決介面不同的問題。它的結構也很簡單,就是一頭接我們要的介面,另一頭接提供功能的元件,然後作對應。
使用時機:通常都不是自己新開發的系統,而是為了讓舊有的元件得以繼續運作,或是為了某些外購的元件。
==================================
在Agile Software Development, Principles, Patterns, and Practices一書中,另外提到一個可搭配Adapter的Abstract Server Pattern,它一樣很簡單,卻也是常出現的基本概念。
Abstract Server Pattern就是在原本直接的關聯中加入一個抽象,好讓物件間的藕合度降低。簡單的說,就是DIP(依賴倒轉)原則。
書中的例子(圖上部),原本的設計將『燈』與『開關』直接關聯,這樣一來有開關就必須有燈不可。加入一個interface後,就能將其間的關聯性減輕,日後如果電扇要用這個開關,也只要繼承抽象的interface即可。
如果考慮到使用開關的物件可能來自外部元件或舊有的元件,而無法遵循現在開發的interface,那搭配了Adapter就能進一步達成(如下圖)。
本文將這些模式說得很簡單,其實也是想說其實設計模式未必是艱澀難懂,或需要強記的,事實上,即使是沒聽過設計模式的人,如果在撰寫程式時,考慮了減輕耦合、增加彈性之類的概念,也有可能設計出接近這樣的結構。
學習設計模式不是為了找地方套用這些架構,而是你遇到難以取捨之處,可以參考看看你的設計是否考慮周詳,故了解每個模式的精神、形成原因才是最重要的。
2012年5月9日
白話 Design Pattern (四) Builder pattern
相較於工廠系列的模式,著重在該產生哪一個物件;我們也可能面臨到建構過程本身就複雜的狀況,這時如何組裝出一個物件就可能是要解決的問題了,這裡要提到的是建構者模式(Builder pattern)。
以學生餐廳的例子,如果將聚焦在點餐櫃台的動作,我們發現服務人員在接獲點餐後,分別有以下幾個動作:
炒飯櫃台:1.放上餐盤 、2. 放上免洗筷與鐵湯匙、3.放上炒飯、4.放上附湯。
麵點櫃台:1.放上餐盤 、2. 放上筷子與長湯匙、3.放上麵品、4.放上飲料 。
想想看,怎麼作比較好?
1.這次的重點在餐點的產生過程比較複雜,最好能夠將過程侷限在服務人員身上,點餐的學生只要享用該完成品,不需知道,也不需參予整個過程。
2.仔細分析,櫃台人員都有相同的四個動作,抽象的描述為:
1) 放上餐盤、2) 放上餐具、3) 放上餐點、4) 放上附湯飲料。
3.所以我們可以想像,整個模型有兩個部分:
1)執行四個動作:這部分兩個櫃台都一樣,見下圖的點餐服務
2) 對四個動作的各自表述:有一個抽象的餐點Builder與繼承它的炒飯Builder、麵食Builder
故,Builder Pattern的類別圖如下:
其中的Director就是負責執行各步驟並產出產品,Builder則確保了其繼承者都會有N個步驟。也有人將Director合併到Builder內,成為一種較簡化的變形。而這個模式中的幾個建構步驟,也可能與工廠系列的模式合作。
總之,Builder模式的重點有:
1.生產的流程複雜,需要被封裝。
2.每個builder都有相同的N個步驟或零件(步驟有先後順序,零件則無)。
前述的用法,產品較類似套餐,即各步驟或零件是固定的,選了A套餐,便能決定了每個部分是什麼。但實務上,如果到速食店點餐,雖然有分:主餐、副食、飲料三個部分,但可能個部分都是消費者當下選擇的(主餐可選雞塊/漢堡/雞堡、副食可選薯條/薯餅...),那就不可能在設計時期(Design Time)把所有的組合Builder都寫好,而需要代入多個參數,像下列:
餐點 A = new 餐點Builder(主食.雞塊, 副食.薯條, 飲料.coca).build();
那麼,如果建構時所需的參數非常多,要怎麼辦?下例是使用了Fluent Interface的技巧,讓新增客戶資料時,可以像以下這樣,簡潔易讀。
客戶 A = new 客戶Builder()
.Name("Rock")
.email("xxx@ooo.com")
.cellphone("09xx123456")
.build();
大致的寫法是:
public class 客戶
{
public 客戶(string ID, string Name, string email, string cellphone)
{
...
}
}
public class 客戶Builder
{
private 客戶 _Customer;
string _Name;
string _email;
string _cellphone;
public string Name(string Name)
{
_Name = Name;
return(this);
}
public string email(string email)
{
_email = email;
return(this);
}
public string cellphone (string cellphone)
{
_cellphone = cellphone;
return(this);
}
public 客戶 build()
{
_Customer = new 客戶(_Name, _email, _cellphone);
return(_Customer);
}
}
最後,如果有人因為看到Builder中包含了多個建置方法,而覺得會跟Abstract Factory搞混,這裡整理了Builder Pattern與Abstract Factory Pattern的不同:
1) Builder著重在隱藏複雜的建置步驟,最後只傳回一個產品。
2) Abstract Factory則是為了維護一系列產品的關聯,會產出某系列的多項產品。
3) Builder模式中,Client不需要認識各個零件的型態。(只要『吃』產出的餐點)
4) Abstract Factory中,Client認識各項的抽象型別或介面,並能使用它們。
2012年4月26日
白話 Design Pattern (三) Abstract Factory, Reflection Factory
繼上篇的工廠方法模式,如果你所面對的世界變得更複雜了,那該怎麼辦?
以學生餐廳的例子,原本的學生的需求很簡單,對餐點只有一個『吃』,因此只有點炒飯或魯麵的問題。
但是今天有家長來反應:『吃麵不是有湯喝嗎?那炒飯呢?我的寶貝兒子吃了你們的炒飯,結果差點渴死,你們怎麼負責?想上電視嗎?』於是校長立刻指示,今後餐點除了主食,也要有湯品。(本例純屬虛構)
重點是本來學生只要『吃』,現在增加『喝』的行為。先看下圖為原先工廠方法。
現在會進化成下圖。
意即每個工廠(點餐台)將產生一系列的物件(炒飯與附湯、湯麵與湯麵的湯﹚,以滿足Client的多個需求。日後如果食物增加了鍋貼,自然也要有對應的湯品。
上圖即為抽象工廠模式(Abstract Factory Pattern),其重點是它可以產生一系列的物件,維護同一系列物件之間的關係,而不會讓炒飯配到湯麵的湯。當然它可以更複雜,比如還要增加飯後『甜點』,這樣每個點餐台便會有三個方法(Method),但道理都是一樣的。
而上例是因為客戶端(學生)多了一種行為,導致原先的工廠方法需要擴充。實務上常常是從另一個角度發展出來,例如你原先設計了一套系統可以處理會員、產品、交易資料,結果公司業務擴充後,多出了一個新的產品線,可是兩個產品線的會員處理規則、產品呈現方式、交易行為皆不同。這樣可能原先根本不需要工廠方法的,就要開始拉出抽象的會員介面、產品介面、交易介面,然後去作抽象化的處理。
也許這時候你會寧願把整套系統Copy一份再修改,覺得比較容易些。但是別忘了,下次可能又有新的產品線等著你。而每複製一份系統,將來的維護成本可是要倍增的(萬一發現了一個bug要更新到每一套已經客製的產品裡,除非你的工作是計件算酬勞的,要不然一定會後悔)。
以上的工廠方法、抽象工廠模式,都在實踐一個重要的觀念--把客戶端與實際產生的物件(產品)隔離,便於日後的擴充不互相影響。如一開始的例子:從會員擴充出黃金會員後,將來若還會有鑽石會員,也能將現有修改降至最小。
但,它們在實際使用上還是有一個缺點:客戶端必須在設計時期(Design Time)指定自己要哪一個工廠。就像走進學生餐廳的學生,每個人都天生就要決定自己該吃麵還是飯,而且不能改。
那遇到了會員資料的例子,我們由資料庫讀出一筆會員資料才知道他是哪一種會員,要怎麼辦?只能使用簡單工廠模式?
是否生成A工廠或B工廠,可以更動態的決定?
這就要講到上篇文章提到的反射(reflection)技術,它能藉由傳入組件名稱、物件名稱在執行時期(Run Time)來產生物件,如果搭配簡單工廠,便能不需要原本的判斷式,直接把物件名稱當變數傳入。也有人稱這樣的做法為反射工廠。
程式大致如下:
如此,當你遇到普通會員,就傳入"Member"字串,產生出Member物件;如果是黃金會員就傳入"GoldMember",產生 GoldMember 物件,而這個字串也可以來自資料庫的欄位,這樣一來就能靈活解決之前的問題了。當然你可以用它配合抽象工廠模式,傳入要實作的工廠名稱,來解決『一系列物件』的問題。
P.S.反射並不是每一種程式語言都支援,並且效能上需要付出相對代價,詳細使用方式請善用搜尋大法。
以學生餐廳的例子,原本的學生的需求很簡單,對餐點只有一個『吃』,因此只有點炒飯或魯麵的問題。
但是今天有家長來反應:『吃麵不是有湯喝嗎?那炒飯呢?我的寶貝兒子吃了你們的炒飯,結果差點渴死,你們怎麼負責?想上電視嗎?』於是校長立刻指示,今後餐點除了主食,也要有湯品。(本例純屬虛構)
重點是本來學生只要『吃』,現在增加『喝』的行為。先看下圖為原先工廠方法。
現在會進化成下圖。
意即每個工廠(點餐台)將產生一系列的物件(炒飯與附湯、湯麵與湯麵的湯﹚,以滿足Client的多個需求。日後如果食物增加了鍋貼,自然也要有對應的湯品。
上圖即為抽象工廠模式(Abstract Factory Pattern),其重點是它可以產生一系列的物件,維護同一系列物件之間的關係,而不會讓炒飯配到湯麵的湯。當然它可以更複雜,比如還要增加飯後『甜點』,這樣每個點餐台便會有三個方法(Method),但道理都是一樣的。
而上例是因為客戶端(學生)多了一種行為,導致原先的工廠方法需要擴充。實務上常常是從另一個角度發展出來,例如你原先設計了一套系統可以處理會員、產品、交易資料,結果公司業務擴充後,多出了一個新的產品線,可是兩個產品線的會員處理規則、產品呈現方式、交易行為皆不同。這樣可能原先根本不需要工廠方法的,就要開始拉出抽象的會員介面、產品介面、交易介面,然後去作抽象化的處理。
也許這時候你會寧願把整套系統Copy一份再修改,覺得比較容易些。但是別忘了,下次可能又有新的產品線等著你。而每複製一份系統,將來的維護成本可是要倍增的(萬一發現了一個bug要更新到每一套已經客製的產品裡,除非你的工作是計件算酬勞的,要不然一定會後悔)。
以上的工廠方法、抽象工廠模式,都在實踐一個重要的觀念--把客戶端與實際產生的物件(產品)隔離,便於日後的擴充不互相影響。如一開始的例子:從會員擴充出黃金會員後,將來若還會有鑽石會員,也能將現有修改降至最小。
但,它們在實際使用上還是有一個缺點:客戶端必須在設計時期(Design Time)指定自己要哪一個工廠。就像走進學生餐廳的學生,每個人都天生就要決定自己該吃麵還是飯,而且不能改。
那遇到了會員資料的例子,我們由資料庫讀出一筆會員資料才知道他是哪一種會員,要怎麼辦?只能使用簡單工廠模式?
是否生成A工廠或B工廠,可以更動態的決定?
這就要講到上篇文章提到的反射(reflection)技術,它能藉由傳入組件名稱、物件名稱在執行時期(Run Time)來產生物件,如果搭配簡單工廠,便能不需要原本的判斷式,直接把物件名稱當變數傳入。也有人稱這樣的做法為反射工廠。
程式大致如下:
public class ReflectionFactory
{
public Product CreateProduct(string ProductName)
{
return(Assembly.Load("組件名稱").CreateInstance(ProductName));
}
}
如此,當你遇到普通會員,就傳入"Member"字串,產生出Member物件;如果是黃金會員就傳入"GoldMember",產生 GoldMember 物件,而這個字串也可以來自資料庫的欄位,這樣一來就能靈活解決之前的問題了。當然你可以用它配合抽象工廠模式,傳入要實作的工廠名稱,來解決『一系列物件』的問題。
P.S.反射並不是每一種程式語言都支援,並且效能上需要付出相對代價,詳細使用方式請善用搜尋大法。
2012年4月19日
白話 Design Pattern (二) Simple Factory, Factory Method
在GoF的書中將23個Design Patterns分為三類,分別為建構類模式(Creational Patterns)、結構類模式(Structural Patterns)、行為類模式(Behavioral Patterns)。這樣劃分簡單且清楚,就由『
建構類模式(Creational Patterns)』開始探討吧!
建構類模式,顧名思義此類的模式目的在建立出物件實體(instantiate )。
建立物件實體?為什麼還要設計模式?不是用一個 "new" 就好了嗎?
試想以下的實例:
阿甘維護了會員一個系統,原本只有一個類別(class)--Member。今天主管來告訴你,公司現在要多出一種會員身分--黃金會員,他們可以在結帳時享受更優惠的折扣。
阿甘不疾不徐的想了一下,打算這樣做:
1.增加一個新的類別:GoldMember來繼承Member。
2.覆寫結帳時的折扣規則。
3.在建立物件時作個判斷:如果該會員是黃金會員則 new GoldMember( ),否則 new Member( )。
因為計價程式會呼叫折扣函數GetDiscount(),所以完全不用改。
太棒了!看來一切可行!
這時他也注意到了出現 new Member( ) 的程式不只一個地方(可能出現在客服介面、帳單管理介面、會員網站等地方),『那就使用拿手的Copy and Paste大法吧!』,很快的這些地方都被換成以上的程式段。
此時,他發現走到身後的主管正在看著,眼角泛著晶瑩的淚光,『一定是我的工作效率太棒了,使用了OO的優點,一下子就把問題解決了,讓主管不禁留下欣慰的眼淚吧!』阿甘不禁得意起來。
如果你正是主管,發現他的問題了嗎?
Copy不是該死,但會把維護的人搞死!
是的,把相同的程式片段分散在多數地方,絕對是開發的大忌。如果日後公司又要增加一種鑽石會員,那這些片段豈不是要一個個的修改,若有地方疏漏就會造成問題,況且需求變動就要到處修改原先的程式,更是違反了OCP(開放封閉原則)。
即便上述的程式沒有重複出現,把應該產生哪種會員的規則放到某處,造成該處多了增加新的會員身分就需變動的理由,也是違反了SRP(單一職則原則)。
那就把該建立哪一個會員的程式片段抽出來吧!
此外,如果你還想到DIP(依賴倒轉)原則,而能將GoldMember與Member的繼承關係,改為一起繼承一個抽象型別(abstract class)或介面(interface)...(其實少用繼承也是SOLID外的開發原則,以後有機會再提吧!)
『賓果!你已經進入Design Patterns的思考領域了!!』
以上就是簡單工廠模式(Simple Factory Pattern)的圖示。
簡單工廠模式又稱靜態工廠模式,簡單的說就是把建立哪一個Product的判斷,放在一個專門的類別裡,而它傳回的型態即是將產品抽象後的abstract class或interface,可以說是一個簡單又好用的模式(雖然它並非GoF提出的23個之中)。
這裡使用了『工廠(Factory)』這個名詞,是把其建構出來的物件當成產品,因而自稱建構者為工廠。
大致程式如下:
但是,它並沒有百分之百滿足OCP,因為每次多了一種新的產品,就必須修改這個判斷式的部分。這時有一個改良作法是使用反射(reflection),但這個先不在此探討。
另一個改良法則是工廠方法模式(Factory Method Pattern),將工廠的部分也抽象化。這樣一來會產生多個對應產品的工廠,此後擴充產品時亦要擴充相對的工廠,看起來複雜的多。
要徹底了解差異,可以舉一個生活實例:
由此可知,開發時期若使用了較有彈性的結構,可能讓開發成本提高,但對於之後的設計變更,便能以較小的代價因應。
那麼結構越複雜的模式一定越好?當然不是,如果你知道這個部分將來變動的機會很低,其實也沒必要殺雞卻急著打造一把牛刀。也許等將來遇到需要的時候,再來重構就好。
重點是,如果你不懂這些設計模式,你的評估一定不夠周詳。
建構類模式,顧名思義此類的模式目的在建立出物件實體(
建立物件實體?為什麼還要設計模式?不是用一個 "new" 就好了嗎?
試想以下的實例:
阿甘維護了會員一個系統,原本只有一個類別(class)--Member。今天主管來告訴你,公司現在要多出一種會員身分--黃金會員,他們可以在結帳時享受更優惠的折扣。
阿甘不疾不徐的想了一下,打算這樣做:
1.增加一個新的類別:GoldMember來繼承Member。
2.覆寫結帳時的折扣規則。
3.在建立物件時作個判斷:如果該會員是黃金會員則 new GoldMember( ),否則 new Member( )。
public class Member
{
public decimal GetDiscount()
{
...
}
}
public class GoldMember : Member
{
public override decimal GetDiscount()
{
...
}
}
if(會員是金卡會員)
{
new GoldMember();
}
else
{
new Member();
}
因為計價程式會呼叫折扣函數GetDiscount(),所以完全不用改。
太棒了!看來一切可行!
這時他也注意到了出現 new Member( ) 的程式不只一個地方(可能出現在客服介面、帳單管理介面、會員網站等地方),『那就使用拿手的Copy and Paste大法吧!』,很快的這些地方都被換成以上的程式段。
此時,他發現走到身後的主管正在看著,眼角泛著晶瑩的淚光,『一定是我的工作效率太棒了,使用了OO的優點,一下子就把問題解決了,讓主管不禁留下欣慰的眼淚吧!』阿甘不禁得意起來。
如果你正是主管,發現他的問題了嗎?
Copy不是該死,但會把維護的人搞死!
是的,把相同的程式片段分散在多數地方,絕對是開發的大忌。如果日後公司又要增加一種鑽石會員,那這些片段豈不是要一個個的修改,若有地方疏漏就會造成問題,況且需求變動就要到處修改原先的程式,更是違反了OCP(開放封閉原則)。
即便上述的程式沒有重複出現,把應該產生哪種會員的規則放到某處,造成該處多了增加新的會員身分就需變動的理由,也是違反了SRP(單一職則原則)。
那就把該建立哪一個會員的程式片段抽出來吧!
此外,如果你還想到DIP(依賴倒轉)原則,而能將GoldMember與Member的繼承關係,改為一起繼承一個抽象型別(abstract class)或介面(interface)...(其實少用繼承也是SOLID外的開發原則,以後有機會再提吧!)
『賓果!你已經進入Design Patterns的思考領域了!!』
簡單工廠模式又稱靜態工廠模式,簡單的說就是把建立哪一個Product的判斷,放在一個專門的類別裡,而它傳回的型態即是將產品抽象後的abstract class或interface,可以說是一個簡單又好用的模式(雖然它並非GoF提出的23個之中)。
這裡使用了『工廠(Factory)』這個名詞,是把其建構出來的物件當成產品,因而自稱建構者為工廠。
public class SimpleFactory
{
public Product CreateProduct(string ProductName)
{
if(ProductName == "A")
{
return(new ProductA());
}
else if (ProductName == "B")
{
return (new ProductB());
}
else
{
return(null);
}
}
}
public interface Product
{
}
public class ProductA : Product
{
}
public class ProductB : Product
{
}
但是,它並沒有百分之百滿足OCP,因為每次多了一種新的產品,就必須修改這個判斷式的部分。這時有一個改良作法是使用反射(reflection),但這個先不在此探討。
另一個改良法則是工廠方法模式(Factory Method Pattern),將工廠的部分也抽象化。這樣一來會產生多個對應產品的工廠,此後擴充產品時亦要擴充相對的工廠,看起來複雜的多。
要徹底了解差異,可以舉一個生活實例:
某學校的學生餐廳有兩個廚師-王師傅與周師傅,王師傅負責炒飯,周師傅負責煮麵。餐廳點餐的模式有以下三種:
(1)沒有Patterns:學生要自己知道自己想吃的東西是哪個師傅作的,然後自己去點。
萬一哪天其中一個師傅換人了,則所有的學生都要知道自己是不是該找新的廚師。
(2)簡單工廠模式:餐廳設置了一個點餐台,後方站一位正妹服務員,櫃台前印著精美的Menu。學生向服務員點餐後,該員再向後台的廚師請求。無論廚師怎麼換,學生找的都是這個點餐台。
(3)工廠方法模式:餐廳設計成料理東西軍模式。左邊是點飯的服務台,上面大大的招牌寫『天下無敵蛋炒飯 』;右邊是點麵的服務台,掛著『世界第一大魯麵』,學生想吃飯就向左走,吃麵的向右走。中間還可以放個人型立牌,寫上『今天你想吃哪一道,Dochi?』
想想,如果今天餐廳要加賣『鍋貼』,則以上三種情況差別在哪?
(1)沒有Patterns:需要點鍋貼的學生都要更新一下資訊,認識新的鍋貼師傅。
(2)簡單工廠模式:點餐台的服務員需要重新訓練,櫃台前的Menu要重新製作,將鍋貼加上去。學生則維持走向點餐台即可。
(3)工廠方法模式:在餐廳中間加設一個鍋貼點餐台,原先兩邊的點餐台完全不用動(符合開放封閉原則)。學生則各自走向正確的點餐台。
又, 『鍋貼』 改由周師傅負責製作,煮麵的則另聘一位林師傅,會有甚麼改變?
(1)沒有Patterns:學生又要更新一次資訊。部分改不過來的學生也許因此崩潰...
(2)簡單工廠模式:點餐台的服務員需要重新訓練,學生無影響。
(3)工廠方法模式:點麵、鍋貼的服務員重新訓練就好(點飯的服務員完全不影響),學生無影響。
(1)沒有Patterns:學生要自己知道自己想吃的東西是哪個師傅作的,然後自己去點。
萬一哪天其中一個師傅換人了,則所有的學生都要知道自己是不是該找新的廚師。
(2)簡單工廠模式:餐廳設置了一個點餐台,後方站一位
(3)工廠方法模式:餐廳設計成料理東西軍模式。左邊是點飯的服務台,上面大大的招牌寫『天下無敵蛋炒飯 』;右邊是點麵的服務台,掛著『世界第一大魯麵』,學生想吃飯就向左走,吃麵的向右走。中間還可以放個人型立牌,寫上『今天你想吃哪一道,Dochi?』
想想,如果今天餐廳要加賣『鍋貼』,則以上三種情況差別在哪?
(1)沒有Patterns:需要點鍋貼的學生都要更新一下資訊,認識新的鍋貼師傅。
(2)簡單工廠模式:點餐台的服務員需要重新訓練,櫃台前的Menu要重新製作,將鍋貼加上去。學生則維持走向點餐台即可。
(3)工廠方法模式:在餐廳中間加設一個鍋貼點餐台,原先兩邊的點餐台完全不用動(符合開放封閉原則)。學生則各自走向正確的點餐台。
又, 『鍋貼』 改由周師傅負責製作,煮麵的則另聘一位林師傅,會有甚麼改變?
(1)沒有Patterns:學生又要更新一次資訊。部分改不過來的學生也許因此崩潰...
(2)簡單工廠模式:點餐台的服務員需要重新訓練,學生無影響。
(3)工廠方法模式:點麵、鍋貼的服務員重新訓練就好(點飯的服務員完全不影響),學生無影響。
由此可知,開發時期若使用了較有彈性的結構,可能讓開發成本提高,但對於之後的設計變更,便能以較小的代價因應。
那麼結構越複雜的模式一定越好?當然不是,如果你知道這個部分將來變動的機會很低,其實也沒必要殺雞卻急著打造一把牛刀。也許等將來遇到需要的時候,再來重構就好。
重點是,如果你不懂這些設計模式,你的評估一定不夠周詳。
白話 Design Pattern (一) 一些觀念
網路上關於 Design Pattern 的文章不少,當然亦有不少精闢的見解。
不過,由於每個人的領悟方式不盡相同,此系列嘗試用自己認為比較『白話』的方式,來解釋Design Pattern,並且會著重在Pattern的用途與差異(因有感這部分在網路上較缺乏),詳細作法則可以查詢其他網路文章。
以下是寫在前面的一些觀念:
2.實務上,解決一個問題常不會只套用一個Pattern,所以Pattern 間不是互相排斥的,可能需要互相合作。
不過,由於每個人的領悟方式不盡相同,此系列嘗試用自己認為比較『白話』的方式,來解釋Design Pattern,並且會著重在Pattern的用途與差異(因有感這部分在網路上較缺乏),詳細作法則可以查詢其他網路文章。
以下是寫在前面的一些觀念:
1.Design Pattern可說是一些有經驗的設計者,針對某些的情況所提出的解決方式。使用上未必要照本宣科的如法炮製,只要了解其觀念、思考方向,也可以有許多變形的作法。
3.單看圖示結構,有許多Pattern會非常相似,想要徹底應用,最好先了解其目的(要解決的問題)。
4.很多情況,要使用哪個Pattern 不會有唯一解,所以還是一句『看需求、看情況決定』。
5.使用Pattern是需要付出代價的(如:複雜度、效率、開發時程),也許對開始的開發時期沒有幫助,但換來的通常是變更、維護時期的好處(如:易修改、較好的擴充性、可讀性)。
6.不使用Pattern 也未必不好,需不需要使用可以用SOLID原則檢視與評估。
4.很多情況,要使用哪個Pattern 不會有唯一解,所以還是一句『看需求、看情況決定』。
5.使用Pattern是需要付出代價的(如:複雜度、效率、開發時程),也許對開始的開發時期沒有幫助,但換來的通常是變更、維護時期的好處(如:易修改、較好的擴充性、可讀性)。
6.不使用Pattern 也未必不好,需不需要使用可以用SOLID原則檢視與評估。
7.承上,不用一開始就過度設計,但記得最重要的心訣--重構(Refactoring),隨時保持要接受重構的心。
『X!原來D賽配湯(Design Pattern)架簡單!』是本系列文章的目標。(X = 讚、耶、哦... 別想歪)
『X!原來D賽配湯(Design Pattern)架簡單!』是本系列文章的目標。(X = 讚、耶、哦... 別想歪)
本系列文章,僅建構在個人的想法,不保證有充分的學理依據。如有謬誤,歡迎指正與討論。
2012年3月13日
白話- OO設計原則 (SOLID原則) - 附生活實例
世界最遙遠的開發目標,不是老闆的天馬行空,不是技術的困難程度,而是你以為就要完成了,需求卻仍然改個不停。 -
- -血汗工程師的心聲
以下的5個原則,也稱為SOLID原則,是用來檢視當系統需求改變時,你是否能快速、安心的面對。是否應該引用Design Pattern,也可用目前是否符合這些原則來做檢視。
這些原則間並非完全互斥,即當你發現違反了某個原則時,通常也會違反其他原則。而其中最重要的便是OCP原則。
1. SRP:Single Responsibility Principle(單一職責)
定義:一個class應該只有一個需要改變的原因。
There should never be more than one reason for a class to change.
白話:一個class只作一件事。
問題:
1)到底切多細,很難一開始就知道。
2)切太細造成複雜度過高。
2. OCP: Open Closed Principle(開放封閉)
定義:軟體設計,應該對擴充開放,對修改封閉。
Software entities like classes, modules and functions should be open for extension but closed for modifications.
白話:軟體要很容易擴充功能,且擴充時原有的code都不需修改。
重要性:OCP是OOD諸多主張的核心。
生活實例:
某情聖:『我永遠在電話中叫女朋友"寶貝",這樣怎麼換人或劈腿都不會穿幫』
3. LSP : Liskov Substitution Principle(Liskov替換)
定義:子類必須能夠替換其父類別。
Inheritance should ensure that any property proved about supertype objects also holds for subtype objects.
白話:設計父類別時,只把所有的子類都有的東西放進來。
生活實例:
女兒問你『把拔,什麼是鳥類?』
你很得意的說『很簡單啊,就是會飛的動物嘛!』 (把『會飛』加到『鳥類』這個概念中)
接著打開鳥類大百科開始解說:
『就像這個老鷹就是會飛的動物』
『這個烏鴉也是會飛的動物 』
『還有.........這個... 企鵝 ......?』@ @|||
PS.『不會飛的鳥』請見Wiki...
與OCP關係:LSP也是為了確保子類別擴充時不會違反OCP 。
4. ISP:Interface Segregation Principle(介面隔離)
定義:客戶(Client)只要依賴它們所需的介面
Clients should not be forced to depend upon interfaces that they don't use.
白話:設計介面也盡量簡單,別把不相關的東西放進來。
生活實例:
業務員:『太太!你看我們最新的遙控器,可以遙控電視、冷氣、撥放器,連洗衣機也可以遙控,讓你一機在手家事一手包辦...』
太太:『那我衣服洗到一半,被轉電視的小朋友按到停止怎麼辦?可以把洗衣機的部分拿掉嗎?』
業務員:『...』
解決方式:
1)委任
2)多重繼承
5. DIP : Dependency Inversion Principle(依賴倒轉)
定義:
1)高階模組不應依賴低階模組,兩者應依賴抽象概念。
High-level modules should not depend on low-level modules. Both should depend on abstractions.
2)抽象概念不應依賴細節,細節應依賴抽象概念。
Abstractions should not depend on details. Details should depend on abstractions.
白話:不要具體的指明物件的關係,而要抽象觀念替代之。
生活實例:
你下班快到家時,接到老婆的電話,才驚覺今天是結婚紀念日。
千萬別在電話中說,『當然,我帶了妳最愛的玫瑰花回家哦!』(具體)
因為很可能稍後發現......附近的花店都關門了...
只要說『當然,親愛的,我帶了禮物回家』(抽象)
這樣一來就能看到路邊賣什麼就買什麼。
PS.你真的以為這樣能過關嗎?
以下的5個原則,也稱為SOLID原則,是用來檢視當系統需求改變時,你是否能快速、安心的面對。是否應該引用Design Pattern,也可用目前是否符合這些原則來做檢視。
這些原則間並非完全互斥,即當你發現違反了某個原則時,通常也會違反其他原則。而其中最重要的便是OCP原則。
1. SRP:Single Responsibility Principle(單一職責)
定義:一個class應該只有一個需要改變的原因。
There should never be more than one reason for a class to change.
白話:一個class只作一件事。
問題:
1)到底切多細,很難一開始就知道。
2)切太細造成複雜度過高。
2. OCP: Open Closed Principle(開放封閉)
定義:軟體設計,應該對擴充開放,對修改封閉。
Software entities like classes, modules and functions should be open for extension but closed for modifications.
白話:軟體要很容易擴充功能,且擴充時原有的code都不需修改。
重要性:OCP是OOD諸多主張的核心。
生活實例:
某情聖:『我永遠在電話中叫女朋友"寶貝",這樣怎麼換人或劈腿都不會穿幫』
3. LSP : Liskov Substitution Principle(Liskov替換)
定義:子類必須能夠替換其父類別。
Inheritance should ensure that any property proved about supertype objects also holds for subtype objects.
生活實例:
女兒問你『把拔,什麼是鳥類?』
你很得意的說『很簡單啊,就是會飛的動物嘛!』 (把『會飛』加到『鳥類』這個概念中)
接著打開鳥類大百科開始解說:
『就像這個老鷹就是會飛的動物』
『這個烏鴉也是會飛的動物 』
『還有.........這個... 企鵝 ......?』@ @|||
PS.『不會飛的鳥』請見Wiki...
與OCP關係:LSP也是為了確保子類別擴充時不會違反OCP 。
4. ISP:Interface Segregation Principle(介面隔離)
定義:客戶(Client)只要依賴它們所需的介面
Clients should not be forced to depend upon interfaces that they don't use.
白話:設計介面也盡量簡單,別把不相關的東西放進來。
生活實例:
業務員:『太太!你看我們最新的遙控器,可以遙控電視、冷氣、撥放器,連洗衣機也可以遙控,讓你一機在手家事一手包辦...』
太太:『那我衣服洗到一半,被轉電視的小朋友按到停止怎麼辦?可以把洗衣機的部分拿掉嗎?』
業務員:『...』
解決方式:
1)委任
2)多重繼承
5. DIP : Dependency Inversion Principle(依賴倒轉)
定義:
1)高階模組不應依賴低階模組,兩者應依賴抽象概念。
High-level modules should not depend on low-level modules. Both should depend on abstractions.
2)抽象概念不應依賴細節,細節應依賴抽象概念。
Abstractions should not depend on details. Details should depend on abstractions.
白話:不要具體的指明物件的關係,而要抽象觀念替代之。
相關觀念:IoC(Inversion of Control, 控制反轉)。另DI(Dependency Injection, 依賴注入)則為其實作的方法。
生活實例:
你下班快到家時,接到老婆的電話,才驚覺今天是結婚紀念日。
千萬別在電話中說,『當然,我帶了妳最愛的玫瑰花回家哦!』(具體)
因為很可能稍後發現......附近的花店都關門了...
只要說『當然,親愛的,我帶了禮物回家』(抽象)
這樣一來就能看到路邊賣什麼就買什麼。
PS.你真的以為這樣能過關嗎?
訂閱:
文章 (Atom)





