向導就是類似于安裝軟體時,一大堆下一步的那個窗體。每一個上一步下一步意味着窗體上的控件需要改變,相關的按鈕的功能等也需要改變。在制作一個向導時,最初設計中,我用的enum來表示每一個步驟。比如:
switch(stepEnum)
{
case StepEnum.FirstStep:
//some work
break;
case StepEnum.SecondStep:
break:
//...
default:
//...
}
這樣的設計,使得整個代碼中充斥着大量的switch...case語句,并且使得每個步驟涉及到的控件布局,邏輯處理等代碼混在一起。很是醜陋,無法忍受。于是我想用試着用state模式來除掉這些壞掉的味道。
State模式用[GOF]的話來說,使允許一個對象在其内部狀态改變時改變它的行為。我定義一個抽象的步驟類來取代原有的枚舉結構,各步驟繼承與這個抽象類。其定義如下:
public abstract class InitWizardStepState
public abstract void AddControlToContainer(Control container);
public abstract bool CouldGoToNext();
public abstract InitWizardStepState GoToNextStep();
public abstract InitWizardStepState GoToPreStep();
public abstract bool IsFirstStep { get;}
public abstract bool IsLastStep { get;}
通過這樣的設計,将每一個步驟的邏輯判斷和UI設計都封裝在了StepState中。避免了在主視窗中使用枚舉類來判斷各個操作。簡化了主視窗的代碼,實作了相關的隔離,除掉了壞掉的味道。同時,利用單件模式,降低反複上一步,下一步付出的代價。基本達到了目的。
但是實踐就是實踐,沒有經典問題那麼的純粹,有些問題很是折磨人。比如步驟轉換的控制是由誰控制。步驟轉換并不是一個簡單的由1-->2-->3-->...的過程。而根據使用者輸入和選擇的不同,可能存在1-->3-->...這樣的路徑,也可能有1-->2(1)-->3-->...這樣的路徑。設計中是将狀态的轉移控制封裝在了InitWizardStepState類中(使用GoToNextStep和GoToPreStep來控制)。比如:在1步驟是擷取需要轉移和需要删除的檔案清單,2步驟處理轉移檔案,3步驟處理删除檔案。如果1步驟中,使用者并沒有輸入轉移檔案,就可以跳過2直接進到3中。直接封裝在步驟中可以使得這種處理可以在内部完成。但我越來越覺得這不是一個良好的決定。因為并不符合開閉原則。如果我需要建立一個新的向導,這個向導的次序和相關處理與老步驟不同。我如果還想使用原先的步驟類,我必須修改裡面的邏輯,而并不是利用繼承建立新的步驟即可。說明這個設計不夠良好。
導緻這個問題出現的原因,我想是因為在步驟轉換是存在着資料的轉移.比如在1步驟中獲得了某個路徑,在2步驟中需要獲得這個路徑,就将路徑需要由步驟1傳個步驟2。這其實就與state模式的應用範圍産生了偏差。在state模式中,要求作為state的對象可以不依賴于其他對象而獨立變化。是以我一直不确定我所謂的state是不是真正的state。
在下一步工作中,想繼續改造上面的結構。即把各個步驟所需的(輸入和輸出的資料)放在一個類中,作為一個context存在。比如:以上面的轉移檔案和删除檔案為例,建立context如下:
public class Context
public string[] deleteFileList;
public string[] moveFileList;
然後将決定上下步驟的部分從StepState中提取出來。放在主視窗中完成。進一步降低了步驟類之間的耦合。同時增加各步驟類于Context之間的聯系。比如:
将public abstract void AddControlToContainer(Control container);
改為public abstract void AddControlToContainer(Control container,Context context);
這樣可以更好的保持可擴充性和可重用性。
本文轉自 duguguiyu 51CTO部落格,原文連結:http://blog.51cto.com/duguguiyu/363383,如需轉載請自行聯系原作者