【问题标题】:Is this a typical use case for IOC?这是 IOC 的典型用例吗?
【发布时间】:2010-01-27 23:36:41
【问题描述】:

我当前的应用程序允许用户通过一组管理屏幕定义自定义 Web 表单。它本质上是一个 EAV 类型的应用程序。因此,我无法对 HTML 或 ASP.NET 标记进行硬编码来呈现给定页面。相反,UI 从服务层请求一个 Form 对象的实例,而后者又使用多个 RDMBS 表构造一个。表单包含您希望在这种情况下看到的类:Form=> IEnumerable<FormSections>=>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
       }
}

再次一切正常,力量恢复平衡。 UI 继续不知道表单中的内容,并且实现了我们的关注点分离。然而,仅仅短短几周后,业务就提出了一个新要求:在某些情况下,我们应该只应用我上面提到的一些表单转换。

在这一点上,感觉“显式方法”方法已经走到了尽头,除非我想以爆炸式的方法结束(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);
       }
}

从表面上看,这似乎是可以接受的方法,但有一定的气味。也就是说,一直生活在对OpenFormForViewing的实现细节一无所知的UI中,必须拥有IFormViewCreator的知识并创建一个实例。

  1. 我的问题有两个: 更好的方法来实现 我追求的可组合性? (也许通过 使用 IoC 容器或家庭 轧制工厂创建 具体的 IFormViewCreator)?
  2. 我是不是彻底搞砸了 抽象在这里?

【问题讨论】:

  • 我同意你对气味的看法。似乎 UI 不必了解 IFormViewCreator。给你一个问题:用户界面是否知道你应该返回哪个“风味”的参数? UI 传递一个对象可能更有意义,该对象用作确定要应用哪些转换的各种值的容器。然后,您的 OpenFormForViewing() 方法将负责转发到工厂类,或明确找出要返回的“风味”。

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


【解决方案1】:

据我了解,您需要先修改表单,然后再将其发送到 UI 层。这听起来像Decorator 将到位。保留没有 IFormViewCreator 的旧 IFormService 接口。

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

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 模式。我一直在使用装饰器:)

【讨论】:

  • +1 以获得良好的响应。我希望在给出答案之前引发更多讨论。
  • 因为 SO 不会让我这样做。没有可用的“接受”复选标记。那是因为我在这个问题上悬赏了吗?
  • 是的,您应该在赏金活动期间接受答案。但是,如果您仍然想接受它,您可以在元论坛 meta.stackoverflow.com 上寻求帮助。我当然不会介意:)
  • @Dirk @Mark 一旦你获得了(失败的)赏金,这是不可能的。
【解决方案2】:

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

您无法消除需求的固有复杂性。通过一种或另一种方式,您将需要检测用例并采取相应措施。

您可以列出所有可能的组合,或者尝试智能地排除处理 - 如果可能的话。例如,如果由于需求的本质存在N * M = X 的可能性,那么详尽列出X 方法确实不好。您应该考虑因此必须从NM 案例的组合中创建X 可能的形式。

在不知道确切要求的情况下很难说。然后有许多可能的方法来分解这种组合,例如DecoratorChainOfResponsabilityFilter等。这些都是构成复杂逻辑的方式。

也就是说,一直存在的 UI 在无知的幸福中 实施细则 OpenFormForViewing,必须具备 了解并创建实例 IFormViewCreator。

演示文稿应分发在其他地方创建的表单,以便与表单创建无关。然而,表格需要在某处组装/创建。

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

【讨论】:

    猜你喜欢
    • 2012-10-25
    • 1970-01-01
    • 1970-01-01
    • 2016-05-30
    • 1970-01-01
    • 2021-11-12
    • 2010-11-21
    • 2015-01-13
    相关资源
    最近更新 更多