15

我目前的应用程序允许用户通过一组管理屏幕定义自定义网页表单。它本质上是一个EAV类型的应用程序。因此,我不能硬编码HTML或ASP.NET标记来呈现给定的页面。相反,UI从服务层请求一个Form对象的实例,然后使用多个RDMBS表构造一个实例。表格中包含有一种类,你会期望在这样的背景下看:Form =>IEnumerable<FormSections> =>IEnumerable<FormFields>这是IOC的典型用例吗?

这里的服务层的样子:

public class MyFormService: IFormService{ 

     public Form OpenForm(int formId){ 
      //construct and return a concrete implementation of Form 
     } 
} 

一切工作出色(一会儿) 。用户界面对于给定表单中存在哪些部分/字段不太明智:它将其接收的Form对象愉快地呈现为功能性ASP.NET页面。

几周后,我从业务中获得了一项新要求:查看表单的不可编辑(即只读)版本时,某些字段值应该合并在一起,其他设计/计算字段应该是添加。我说没问题。简单修改我的服务等级,使其方法更加明确:

public class MyFormService: IFormService{ 

     public Form OpenFormForEditing(int formId){ 
      //construct and return a concrete implementation of Form 
     } 

     public Form OpenFormForViewing(int formId){ 
      //construct and a concrete implementation of Form 
      //apply additional transformations to the form 
     } 
} 

再次,一切都很好,平衡已恢复到部队。用户界面对于表单中的内容仍然是不可知论的,我们关注的分离已经实现。然而,短短几周后,企业提出了一项新的要求:在某些情况下,我们应该只应用的一些上面引用的形式转换。除非我想结束一些方法的爆炸(OpenFormViewingScenario1,OpenFormViewingScenario2等),否则感觉就像“显式方法”已经到了死胡同的地步。相反,我介绍的间接另一个层面:

public interface IFormViewCreator{ 
     void CreateView(Form form); 
} 

public class MyFormService: IFormService{ 

     public Form OpenFormForEditing(int formId){ 
      //construct and return a concrete implementation of Form 
     } 

     public Form OpenFormForViewing(int formId, IFormViewCreator formViewCreator){ 
      //construct a concrete implementation of Form 
      //apply transformations to the dynamic field list 
      return formViewCreator.CreateView(form); 
     } 
} 

从表面上看,这似乎是可以接受的方法,但也有一定的气味。也就是说,UI一直处于对OpenFormForViewing的实现细节的无知幸福中,必须具备并创建一个IFormViewCreator的实例。

  1. 我的问题是双重的:有没有 更好的方式来实现 可组合我之后? (可能通过 使用IoC容器或家庭 滚动工厂创建 具体的IFormViewCreator)?
  2. 我在这里根本搞砸了 抽象吗?
+2

+1也可以做到这一点。 – Lucero 2010-01-27 23:43:57

+0

我同意你的气味。看起来像UI不应该知道IFormViewCreator。对你来说一个问题:用户界面是否了解你应该返回的那些“味道”的参数?对于UI来说,传递一个对象作为确定应用哪个转换的各种值的容器可能更有意义。然后,您的OpenFormForViewing()方法将负责转发到工厂类,或明确指出要返回哪些“风味”。 – 2010-02-08 15:03:55

回答

9

正如我所理解的问题,你需要修改表单之后发送到UI层。这听起来像一个Decorator将适用。保留旧的IFormService接口而不使用IFormViewCreator。

您现在可以创建一个或多个Decorating FormService来实现所需的过滤或修改。

public class MyDecoratingFormService : IFormService 
{ 
    private readonly IFormService formService; 

    public MyDecoratingFormService(IFormService formService) 
    { 
     if(formService == null) 
     { 
      throw new ArgumentNullException("formService"); 
     } 

     this.formService = formService; 
    } 

    public Form OpenFormForEditing(int formId) 
    { 
     var form = this.formService.OpenFormForEditing(formId); 
     return this.TransformForm(form); 
    } 

    public Form OpenFormForViewing(int formId) 
    { 
     var form = this.formService.OpenFormForViewing(formId); 
     return this.TransformForm(form); 
    } 

    public Form TransformForm(Form form) 
    { 
     // Implement transformation/filtering/modification here 
    } 
} 

您现在可以使用一个或多个这样的装饰器来修饰您的原始IFormService实现。

IFormService formService = new MyDecoratingFormService(new MyFormService()); 

您可以根据需要将尽可能多的装饰者(每个都有自己的责任)包裹在一起。

没有明确的需求DI容器来做到这一点,但它很适合其他DI模式。我用的装饰所有的时间:)

+0

+1以获得良好回应。我希望在获得答案之前进行更多的讨论。 – 2010-02-04 15:14:00

+0

@Dirk - 如果你认为这是一个很好的回应,为什么不接受答案? – 2010-02-17 04:40:26

+0

因为SO不会让我。没有可用的“接受”复选标记。那是因为我在这个问题上放了一笔恩赐? – 2010-02-17 15:20:46

0

除非我想用 爆炸的方法 (OpenFormViewingScenario1, OpenFormViewingScenario2等),以结束。

您不能删除固有的复杂性的要求。无论如何,您需要检测用例并采取相应措施。

您可以列出所有可能的组合,或尝试将处理智能化 - 如果可能的话。例如,如果由于要求的性质存在N * M = X的可能性,那么具有X方法的详尽列表确实不好。您应该考虑因素,以便从NM个案的组合中创建X可能的表格。

不知道确切的要求很难说。然后有很多可能的方法来分解这样的组合物,例如, 修饰器,ChainOfResponsability,Filter等等。这些都是构成复杂逻辑的方法。

也就是说,用户界面,其中有大约 OpenFormForViewing的 实施细则无知的幸福一直生活 ,必须具备的 知识和创造 IFormViewCreator的一个实例。

该演示文稿应该发布在其他地方创建的表单,以保持窗体创建不可知。然而,该表单需要在某处进行组装/创建。

在MVC中,这样的逻辑进入控制器,它将更新模型/窗体并分派到正确的视图/渲染。对于好的问题,ASP.NET