【问题标题】:Generics and Loose Coupling: Can Class<T> be decoupled without type assumptions?泛型和松散耦合:Class<T> 可以在没有类型假设的情况下解耦吗?
【发布时间】:2013-03-12 22:54:47
【问题描述】:

我一直在玩松耦合我的数据访问层。我发现依赖注入过程很有帮助,但在考虑使用泛型时遇到了一些难题。

有没有办法让一个支持泛型类型参数的类真正解耦和类型安全?我担心的是,即使您写入一个通用接口,如果派生类型与您最初编码的实体不同,那么您可能会遇到一些编译器可能无法捕获的令人讨厌的运行时错误。

问题是,当我编写代码以从数据库中获取数据并对对象进行水合时,我在尝试在下一层上实现它时遇到了难题。如果我传入下面的 Foo2 之类的类,我可以破坏我的代码,因为没有“隐式”转换。我一直在使用反射来尝试放松,但我不断回到从数据库中获取数据时需要对具体类型进行水合的问题。然后,该类型会在转换和类型保证方面产生问题。此外,如果我想抽象实体库中所有类型的所有方法,我可以通过反射来做到这一点,但我仍然遇到泛型只能对显式“where T: ISomeInterface”语句做出类型保证的问题.最后,如果我们有更多的派生类型或者从接口分支形成新类型的类型,这个模型就会崩溃。认为我需要为曾经做过的每一种类型实现新的数据访问对象会迫使数据访问层发生变化。这打破了我对松散耦合的定义。

所有这一切似乎迫使我回到这个问题 - 泛型可以以真正松耦合的方式使用吗?如果可以,您可以提供哪些参考资料?

简化示例:

public interface IEntity
{
    // stuff for state and methods ...
}
public interface IRepository<T> where T : IEntity
{
    void Save(out int id, T obj);
    T Load(int id);
}
public interface IFoo : IEntity
{
    int Id { get; set; }
    //All other IEntities goodness
    //Some new goodness specific to Foo
}
//Concrete Entities:
public class Foo : IFoo
{
    // blah blah
}
public class Foo2 : IFoo
{
    // new blah blah
}

public class FooRepository : IRepository<Foo> //OOPS, Looks like we have settled in on a concrete type!
{

    public void Save(out int id, Foo obj)
    {
        // ADO.NET code to access Sql ...
        id = 1;// this would actually be the result of the Sql insert output parameter
        return;
    }

    public Foo Load(int id)
    {
        Foo foo = new Foo();
        // ADO.Net code to access Sql
        return foo;
    }
}

那么,您将如何处理可以处理任何 IFoo 派生对象并且仍然能够返回完全形成的具体类型的代码,而不管程序员在路上做什么?是否最好放弃通用部分并坚持依赖注入?任何参考资料、指导等都会很棒。

【问题讨论】:

    标签: .net generics reflection dependency-injection loose-coupling


    【解决方案1】:

    对于您在此处遇到的问题,您始终可以使用typeof(或者可能是不同的运算符/方法)来获取对象的动态类型。这样你就可以在遇到一些转换错误之前执行检查。

    您可以将具体类型限制设置为您的顶级类型,但这确实违背了泛型的目的。

    否则,我真的不知道如何解决您的问题。如果您使用泛型,重点是添加松散耦合和与类型无关的泛型操作。你可以检索它的唯一方法是你 typeof 然后重铸,捕捉边缘情况以防止铸造错误。关于你的问题:

    可以以真正松耦合的方式使用泛型吗?

    我不太明白你的意思?从你的问题的语义上,你当然可以。这实际上是泛型的目的之一。

    那么,您将如何处理可以处理任何 IFoo 派生对象并且仍然能够返回完全形成的具体类型而不管程序员在路上做什么的代码?

    这也很模糊。我可以写:

    public SomeType method(IFoo obj)
    {
    ...
    }
    

    这仍然可以处理任何 IFoo。你不需要泛型。如果你想具体返回你传入的类型,那么:

    public T method<T>(IFoo obj) where T:IFoo
    {
        return (T)obj;
    }
    

    希望这会有所帮助。 LMK 如果我做错了。

    【讨论】:

    • 我知道问题不是 100% 完全形成的。我不想深入了解这方面的杂草,因为您制作的总体 cmets 确实与我正在玩的一些东西保持一致。你的最后一个结构非常有趣。但是,如果我没记错的话,如果您对类强制执行类型约束,则不允许您对方法强制执行条件。部分问题是我一直在使用更复杂的反射类,它可以让我传入任何泛型类型,然后从底层数据访问对象返回“保存”和加载方法。我有冲突。
    • ... 冲突在于 DataAccessClass 其中 T : IBaseObject。 IEntity:IBaseObject 和 IFoo:IEntity。这会产生层次结构所需的正确链,但是如果我尝试从 DataAccessClass 内部调用 IFoo 派生类的“Load”和“Save”方法,并使用反射来决定哪些 DAO 类在其中调用方法如果如您所说, typeof() 不等于 true,则除了引发异常之外,没有其他方法可以强制执行类型安全。也许我最好不要将这些聚合起来,而是将它们分离到自己的类/方法中以进行数据访问。
    • 我想我应该补充一点,在上面的层中使用此代码将是:DataAccessClass.Load(int),然后在 DAL 的 DataAccessClass 中决定所有实体类型之间的选择。当然,名称都被过度简化了,但实体可以是许多不同的商品(例如书籍、音乐、计算机……)。
    • 如果您对正确的模式或抽象有想法,我将不胜感激。我可能需要重新考虑设计(例如插入抽象工厂模式或其他东西)。
    • 类型约束和方法参数是有区别的。 之间的内容是类型约束(例如,您传入的类型以执行您想要的任何操作)。您的方法是任何实际变量。您可以将 作为另一个参数接受器,但仅适用于类型。 IE。方法()
    【解决方案2】:

    我不得不承认对你的难题有点困惑;我不确定它可能是什么。

    生成具体类型的存储库显然必须知道如何创建它们。为什么不为每种类型创建一个存储库,以便每个都实现IRepository&lt;IFoo&gt;?这样你就可以把具体的实现留给你的 DI 容器的配置。

    例如,在 ninject 中我会写一些类似于

    Bind<IRepository<IFoo>>.To<Foo2Repository>()
    

    进行设置。如果您需要更细粒度的控制,我所知道的大多数容器都支持某种范围界定方式,以便绑定仅在某些条件下有效。

    我是否误解了什么,或者这是否解决了您的问题?

    【讨论】:

    • 有趣...我会更深入地研究 Bind 和 ninject。我在这个项目中所做的一切都是从头开始的,因此可能会从探索中受益。我已经稍微避开了 IOC 容器,看看我是否会在性能方面犯错,但很快就会考虑这些工具。
    • 我认为遵循 DI 模式的一大好处是您可以使用 IoC 容器 - 可测试性显然是另一个。根据我的经验,性能影响可以忽略不计。
    • 我可能需要考虑这一点。系统的复杂性是我需要抽象的一件事,以便于使用。单独的类型库将有几个类别。每个类别可以有几种具体类型。每个具体类型都将有两个其他派生。这就是为什么我试图让这个问题保持简单。该架构将是一个 3 层应用程序,其 WCF 服务将域层连接到表示层。后端现在是 SQL,但我想保留选择的能力。它将需要扩展,因为我预计会推动很多用途......
    • ...我想我将域层部署为一项服务,我可以跨实例进行负载平衡以实现可扩展性。您在什么情况下使用过 IoC 容器?你觉得哪个最好? (性能与易用性/可维护性相平衡)。有什么警告的话? PS - 通过可扩展我说的是数百万用户
    【解决方案3】:

    你可以在

    中定义常见的东西
    class BaseFooRepository<TFoo> : IRepository<TFoo> where TFoo : IFoo
    

    然后将更具体的代码放入

    class Foo2Repository : BaseFooRepository<Foo2>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-20
      • 2012-02-27
      • 2017-06-05
      • 2017-05-09
      • 2016-12-04
      • 2020-10-26
      相关资源
      最近更新 更多