C# 多态性和依赖注入
这些天我经常面对这种情况,我正在寻找一个优雅的解决方案。我有:C# 多态性和依赖注入,c#,oop,dependency-injection,polymorphism,unity-container,C#,Oop,Dependency Injection,Polymorphism,Unity Container,这些天我经常面对这种情况,我正在寻找一个优雅的解决方案。我有: public abstract class TypeA { public abstract void AbtractMethod(IDependency dependency); } public class TypeB : TypeA { public override void AbtractMethod(ISpecializedDependencyForB dependency) { } } publi
public abstract class TypeA
{
public abstract void AbtractMethod(IDependency dependency);
}
public class TypeB : TypeA
{
public override void AbtractMethod(ISpecializedDependencyForB dependency) { }
}
public class TypeC : TypeA
{
public override void AbtractMethod(ISpecializedDependencyForC dependency) { }
}
public interface IDependency { }
public interface ISpecializedDependencyForB : IDependency { }
public interface ISpecializedDependencyForC : IDependency { }
我的目标是在客户端透视图中使事情透明,并像这样使用此代码:
TypeA myDomainObject = database.TypeARepository.GetById(id); // The important point here is that I don't know if the object is of TypeB or TypeC when I consume it.
IDependency dependency = ? // How do I get the right dependency
myDomainObject.AbtractMethod(dependency);
所以问题是,因为我不知道对象的具体类型,所以我不能向其中注入正确的依赖关系
我现在做的是创建一个抽象工厂,注入正确的属性。我有两个问题,第一个问题是我最终会有很多工厂。第二个原因是它使多态性变得无用,因为客户机实际上需要关心“管理”底层类型(我需要在工厂中注入所有可能的依赖项,并在客户机代码上实例化工厂)
1) 因此,我考虑将属性注入与unity结合使用,但在手动实例化对象之后,我无法确定是否有可能解决对象的依赖关系。即使使用这种方法,我想我仍然会遇到同样的问题:我不确定unity是否会检查对象的实际类型,并解决正确的依赖关系(如果存在这样的语法):
unityContainer.Resolve<TypeA>(myDomainObject)
unityContainer.Resolve(myDomainObject)
如果没有,我将需要提前知道类型,并将返回到相同的问题
2) 我发现这篇文章提到EF为DI提供了一些机制,但它似乎只是为了注入框架服务(PluralizationService等)。否则,这将是实现这一目标的好方法
3) 我也不能在这种情况下使用DI。。。从概念上看,DI似乎不适合多态性。不过,我对这个想法并不感兴趣
我很乐意为我试图实现的属性注入找到一个解决方案,或者我可以使用的模式的想法。然而,我真的不想仅仅为了这个目的而创建一个大的基础设施和混淆我的代码
注意:在这种情况下,我不希望您使用域事件
谢谢任何了解依赖关系的人都可以在具有函数的接口IDependencyProvider后面生活吗
IDependency GetDependency(Type type).
这甚至可能只是返回一个对象和类,该类实现了接口需要知道所有子类型及其相关依赖项
AbstractMethod随后更改为:
void AbstractMethod(IDependencyProvider provider);
在您的子类中,您然后重写此项并调用
var dependency = provider.GetDependency(this.GetType());
您的中间层对子类型或子依赖项一无所知。这是一个有趣的问题,我想的是您的存储库知道并创建了TypeB和TypeC类,因此您可以在此时添加正确的依赖项
public class TypeARepository
{
private ISpecializedDependencyForB depB;
private ISpecializedDependencyForC depC;
public TypeARepository(ISpecializedDependencyForB depB, ISpecializedDependencyForC depC)
{
this.depB = depB;
this.depC = depC;
}
public TypeA GetById(string id)
{
if (id == "B")
{
return new TypeB(depB);
}
else
{
return new TypeC(depC);
}
}
}
然后,TypeB和TypeC将使用它们对依赖项的私有引用来实现它们的抽象方法,而不是在方法中传递它
我自己经常会遇到各种形式的问题,在我看来,如果类型之间存在硬链接,那么仅仅通过注入配置或类似配置来设置它是错误的。因为它允许安装程序设置错误的配置
这种方法还允许您使用unity注入依赖项;DR将多态
AbstractMethod
的IDependency
参数替换为特定于实现的构造依赖性参数,该参数由IoC容器而不是使用者注入
更详细的信息
原始类层次结构需要看起来更像这样,继承多态性才能工作,因为超类虚拟方法和子类重写方法必须匹配签名:
public abstract class TypeA // superclass
{
public abstract void AbtractMethod(IDependency dependency);
}
public class TypeB : TypeA // subclass 1
{
public override void AbtractMethod(IDependency dependency)
{
Contract.Requires(dependency is ISpecializedDependencyForB);
// ...
}
}
public class TypeC : TypeA // subclass 2
{
public override void AbtractMethod(IDependency dependency)
{
Contract.Requires(dependency is ISpecializedDependencyForC)
// ...
}
}
但是,这种设计有一些不符合实际的地方:
- 似乎违反了,因为尽管
AbtractMethod()
声明它接受基本IDependency
接口,但这两个子类实际上依赖于专门的子类依赖关系
- 对于这些方法的调用者来说,建立正确的依赖关系并将其传递给方法以便正确调用它也是不寻常的,也可能是不方便的
因此,如果可能的话,我将采用一种更传统的方法来安排依赖项,通过这种方法,依赖项将传递给子类构造函数,并且在需要时可用于多态方法。这样就不需要为方法提供适当的IDependency
。让IoC容器来执行适当的依赖项解析:
- 使用构造函数注入在类
TypeB
和TypeC
- 如果存在向使用者公开基类
TypeA
上的IDependency
的次要要求,则向IDependency
类型的TypeA
添加额外的抽象属性(但这似乎不确定)
- 根据Ewan的观察,存储库需要某种策略模式来提供多态域实体(
B
或C
)。在这种情况下,将存储库耦合到一个工厂来完成这项工作。混凝土工厂需要绑定到容器才能进入Resolve()
所以把这些放在一起,你可能会得到这样的结果:
using System;
using System.Diagnostics;
using Microsoft.Practices.Unity;
namespace SO29233419
{
public interface IDependency { }
public interface ISpecializedDependencyForB : IDependency { }
public interface ISpecializedDependencyForC : IDependency { }
public class ConcreteDependencyForB : ISpecializedDependencyForB {};
public class ConcreteDependencyForC : ISpecializedDependencyForC { };
public abstract class TypeA
{
// Your polymorphic method
public abstract void AbtractMethod();
// Only exposing this for the purpose of demonstration
public abstract IDependency Dependency { get; }
}
public class TypeB : TypeA
{
private readonly ISpecializedDependencyForB _dependency;
public TypeB(ISpecializedDependencyForB dependency)
{
_dependency = dependency;
}
public override void AbtractMethod()
{
// Do stuff with ISpecializedDependencyForB without leaking the dependency to the caller
}
// You hopefully won't need this prop
public override IDependency Dependency
{
get { return _dependency; }
}
}
public class TypeC : TypeA
{
private readonly ISpecializedDependencyForC _dependency;
public TypeC(ISpecializedDependencyForC dependency)
{
_dependency = dependency;
}
public override void AbtractMethod()
{
// Do stuff with ISpecializedDependencyForC without leaking the dependency to the caller
}
public override IDependency Dependency
{
get { return _dependency; }
}
}
public interface ITypeAFactory
{
TypeA CreateInstance(Type typeOfA);
}
public class ConcreteTypeAFactory : ITypeAFactory
{
private readonly IUnityContainer _container;
public ConcreteTypeAFactory(IUnityContainer container)
{
_container = container;
}
public TypeA CreateInstance(Type typeOfA)
{
return _container.Resolve(typeOfA) as TypeA;
}
}
public class TypeARepository
{
private readonly ITypeAFactory _factory;
public TypeARepository(ITypeAFactory factory)
{
_factory = factory;
}
public TypeA GetById(int id)
{
// As per Ewan, some kind of Strategy Pattern.
// e.g. fetching a record from a database and use a discriminating column etc.
return (id%2 == 0)
? _factory.CreateInstance(typeof (TypeB))
: _factory.CreateInstance(typeof (TypeC));
// Set the properties of the TypeA from the database after creation?
}
}
class Program
{
static void Main(string[] args)
{
// Unity Bootstrapping
var myContainer = new UnityContainer();
myContainer.RegisterType<ISpecializedDependencyForB, ConcreteDependencyForB>();
myContainer.RegisterType<ISpecializedDependencyForC, ConcreteDependencyForC>();
myContainer.RegisterType(typeof(TypeB));
myContainer.RegisterType(typeof(TypeC));
var factory = new ConcreteTypeAFactory(myContainer);
myContainer.RegisterInstance(factory);
myContainer.RegisterType<TypeARepository>(new InjectionFactory(c => new TypeARepository(factory)));
// And finally, your client code.
// Obviously your actual client would use Dependency Injection, not Service Location
var repository = myContainer.Resolve<TypeARepository>();
var evenNumberIsB = repository.GetById(100);
Debug.Assert(evenNumberIsB is TypeB);
Debug.Assert(evenNumberIsB.Dependency is ISpecializedDependencyForB);
var oddNumberIsC = repository.GetById(101);
Debug.Assert(oddNumberIsC is TypeC);
Debug.Assert(oddNumberIsC.Dependency is ISpecializedDependencyForC);
}
}
}
使用系统;
使用系统诊断;
使用Microsoft.Practices.Unity;
名称空间SO29233419
{
公共接口独立性{}
公共接口IsSpecializedDependencyForB:IDependency{}
公共接口IsSpecializedDependencyForC:IDependency{}
公共类ConcreteDependencyForB:isSpecializedDependencyForB{};
C的公共类ConcreteDependencyForC:IsSpecializedDependencyForC{};
公共抽象类TypeA
{
//你的多态方法
公开摘要无效
public abstract class TypeA
{
public abstract void AbtractMethod();
}
public class TypeB : TypeA
{
private ISpecializedDependencyForB SpecializedDependencyForB
{
get
{
return GetSpecializedDependencyForB.CreateSpecializedDependencyForB();
}
}
public override void AbtractMethod() { // do stuff with dependency }
}
public static class GetSpecializedDependencyForB
{
public static ISpecializedDependencyForB DependencyForB
{
return CreateSpecializedDependencyForB();
}
public delegate ISpecializedDependencyForB CreateSpecializedDependencyForBDelegate();
public static CreateSpecializedDependencyForBDelegate CreateSpecializedDependencyForB;
}
public static void RegisterTypes(IUnityContainer container)
{
// .... registrations are here as usual
GetSpecializedDependencyForB.CreateSpecializedDependencyForB = CreateMyDomainService;
}
private ISpecializedDependencyForB CreateMyDomainService()
{
return container.Value.Resolve<ISpecializedDependencyForB>();
}
TypeA myDomainObject = database.TypeARepository.GetById(id);
myDomainObject.AbtractMethod();