用State模式建立向导的实践。。。

向导就是类似于安装软件时,一大堆下一步的那个窗体。每一个上一步下一步意味着窗体上的控件需要改变,相关的按钮的功能等也需要改变。在制作一个向导时,最初设计中,我用的enum来表示每一个步骤。比如:

switch(stepEnum)
{
case StepEnum.FirstStep:
  //some work
  break;
case StepEnum.SecondStep:
  //some work
  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);

这样可以更好的保持可扩展性和可重用性。

posted on 2006-08-20 21:03  duguguiyu  阅读(1034)  评论(1编辑  收藏  举报

导航