这是IOC的典型用例吗?

时间:2010-01-27 23:36:41

标签: design-patterns oop dependency-injection inversion-of-control abstraction

我当前的应用程序允许用户通过一组管理员屏幕定义自定义Web表单。它本质上是一个EAV类型的应用程序。因此,我无法硬编码HTML或ASP.NET标记来呈现给定页面。相反,UI从服务层请求Form对象的实例,而该服务层又使用多个RDMBS表构造一个。表单包含您希望在这样的上下文中看到的类:Form =&gt; IEnumerable<FormSections> =&GT; IEnumerable<FormFields>

以下是服务层的外观:

public class MyFormService: IFormService{

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

一切都很好(一段时间)。 UI对于给定表单中存在哪些部分/字段并不明智:它很乐意将它接收的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 个答案:

答案 0 :(得分: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模式。我一直使用Decorator:)

答案 1 :(得分:0)

  

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

您无法删除要求的固有复杂性。不管怎样,您需要检测用例并采取相应措施。

您可以列出所有可能的组合,或尝试智能地分解处理 - 如果可能的话。例如,如果由于要求的性质有N * M = X种可能性,那么详尽列出X方法确实不好。您应该考虑因素必须从XN案例的组合中创建M个可能的表单。

在不知道确切要求的情况下,很难说。然后有许多可能的方法来考虑这样的组合物,例如, 装饰器 ChainOfResponsability 过滤器等等。这些都是组成复杂逻辑的方法。

  

即,一直生活的用户界面   在无知的幸福关于   实施细节   OpenFormForViewing,必须拥有   知识并创建一个实例   IFormViewCreator。

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

在MVC中,这样的逻辑进入控制器,它将更新模型/表单并分派到正确的视图/渲染。 ASP.NET

也可以这样做