【问题标题】:How to manage interface segregation when using an IoC container? [duplicate]使用 IoC 容器时如何管理接口隔离? [复制]
【发布时间】:2011-02-05 09:26:25
【问题描述】:

可能重复:
StructureMap singleton usage (A class implementing two interface)

我目前正在设计一个小型系统,并且我目前正在使用 structureMap 作为 IoC。我最近才明白接口隔离的意义……我现在想知道。

如果我有一个特定的业务对象,它将实现三个接口......我应该如何在代码的配置和实例化中处理这个?

假设我在一个简单的场景中有两个接口,用于一个名为 EmployeeServiceObject 的服务层类。 IGenericEntity 和 IEmployeeServiceObject。

GenericEntity 将为类提供 CRUD 功能,而 IEmployeeServiceObject 将为业务查询/操作提供结果集。

如果在外观/服务层方法上,我必须使用 EmployeeServiceObject 类并实际使用两个接口的功能...应该如何处理?

最初我认为正确的做法是设置 IoC 的配置以将 IEmployeeServiceObject 映射到 EmployeeServiceObject,向工厂询问对象,然后在需要使用 CRUD 功能时将其转换为 IGenericEntity,但我不太确定。这似乎也不正确,因为我永远不会正式声明具体类实际上正在实现未在 ioc 容器配置中设置的接口。

我当然知道创建同一个具体类的两个实例但要求不同的接口...听起来更糟。

应该如何处理?

【问题讨论】:

  • 您好,我不知道如何在 StructureMap 中执行此操作的详细信息,但是您应该能够注册具有多个接口的类。然后,您可以设置单例范围以在请求任一接口时检索相同的实例。
  • 感谢山姆。我不想知道如何仅在 StructureMap 上执行此操作。我正在寻找使用任何 IoC 容器处理此问题的方式……如何执行此操作的原理。

标签: design-patterns inversion-of-control structuremap ioc-container design-principles


【解决方案1】:

如果我正确理解了这个问题,你有以下场景(在 C# 代码中):

public interface IGenericEntity { /**/ }

public interface IEmployeeServiceObject { /**/ }

public class EmployeeServiceObject : IEmployeeServiceObject, IGenericEntity

如果是这样,EmployeeServiceObject 实现这两个接口的事实是一个实现细节。您可能有其他实现分别实现每个接口。

ISP 的重点是每个接口都建模一个单独的关注点,因此如果您经常发现自己处于同时需要 IGenericEntity 和 IEmployeeServiceObject 的情况下,您应该质疑分离成两个接口是否合适有意义。

否则您应该单独请求每个接口,因为任何转换尝试都会破坏Liskov Substitution Principle

如果您确实需要两者,则需要同时请求两者:

public class Foo
{
    public Foo(IGenericEntity ge, IEmployeeServiceObject eso) { /**/ }

    // ...
}

如果您有一个类 (EployeeServiceObject) 实现了这两个接口,您需要告诉 DI 容器这是这种情况。在这一点上,我们正在进入特定于容器的细节领域,所以如何你如何做到这一点因每个容器而异。

例如,Windsor 具有 Forward 方法,可让您指定将对一个接口的请求转发到另一种类型。

Poor Man's DI中很简单

var eso = new EmployeeServiceObject();
var f = new Foo(eso, eso);

了解如何使用Poor Man's DI 构成依赖层次总是一件好事,因为它可以为您提供关于容器如何理解同一事物的宝贵线索。

【讨论】:

  • 马克,你说得很好,如果我想出一个场景,我必须在同一个方法中使用来自两个接口(由同一个对象实现)的方法,那么也许是分离关注点不是最佳的,也许这两种方法都应该属于同一个接口。我同意如果我施放会打破 Liskov 的原则。在那种情况下,我什至不知道我是否要向容器注册两次具体类,每个接口一次。
  • 就像你提到的那样,如果场景应该出现在我绝对需要两者的地方,也许在构造函数中说明两个依赖项是要走的路。现在,它将来到特定的 IoC 容器实现,看看它是否匹配相同的具体实现映射到两个接口,以及它是否要重用该实例,只是在内部强制转换为第二个接口。非常好的评论思考如何用可怜的人的 DI 来完成。
【解决方案2】:

如果可以的话,在介绍 DI/IoC 实现之前,我想先介绍一下您的 API。我认为您的痛点正试图引导您进行更好的设计。

EmployeeServiceObject 似乎有太多的职责。在创建对象时考虑Single Responsibility Principle。在您的描述中,EmployeeServiceObject 既是外观/服务层也是是 CRUD 层。通过为 CRUD 操作(IGenericEntity)分配接口,您将 CRUD 实现暴露给外观的使用者(IEmployeeServiceObject)。

鉴于您所描述的这种情况和类型,请考虑另一种方法(我还会重新考虑您的类型名称以更准确地描述其用途):

public interface IEmployeeServiceObject 
{
    // Service methods
    decimal GetSalary(string employeeId);
}

public class EmployeeServiceObject : IEmployeeServiceObject
{
    private IGenericEntity<Employee> _entity;

    public EmployeeServiceObject(IGenericEntity<Employee> entity)
    {
        _entity=entity;
    }

    public decimal GetSalary(string employeeId)
    {
        return _entity.Get(employeeId).Salary;
    }
}

public interface IGenericEntity<T>
{
    // CRUD Methods including...
    T Get(string id);
}

public class GenericEntity<T> : IGenericEntity<T> 
{
    T Get(string id){...}
}

使用您选择的容器注册 IEmployeeServiceObject/EmployeeServiceObject 和 IGenericEntity/GenericEntity 对。 (StructureMap 可以按照约定注册这些类型。)

当您从容器请求 IEmployeeServiceObject 时,容器会先将 GenericEntity 注入到 EmployeeServiceObject 的构造函数中,然后再将其提供给您。

通过这种方式,您可以避免您描述的投射/注册问题。无需强制容器以次优方式工作。此外,您的外观只公开其客户端所需的内容,并将 CRUD 操作推迟到另一个类(不需要向客户端公开)。

【讨论】:

  • 马克。感谢您提出不同的方法。其实你说得对,设计不是最优化的,CRUD 功能不应该暴露在服务层中,但是,这只是我想出的一个场景,类似于我的。尽管您的设计是完全有效的,并且绝对是我提供的示例场景的最佳选择,但我仍然认为程序员可能需要在同一个对象中使用来自两个接口的方法(在容器中注册) ,我想知道应该如何处理。
  • 一个有效的技术问题,但这不是 Liskov 违规(我假设我们都可以在朋友之间说 SOLID :) 让客户知道或关心接口是否由同一个类实现?似乎实现细节正在泄漏到客户端。
【解决方案3】:

我没有找到如何将我的问题作为重复问题关闭...但我在另一个 stackoverflow 问题中找到了我需要的确切方法。

Mark 提到的 Windsor 中的 Forward 方法,它也存在于 StructureMap 中……看来这将是做事的正确方法。

StructureMap singleton usage (A class implementing two interface)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-06
    • 2011-08-04
    • 2016-10-19
    相关资源
    最近更新 更多