tob*_*777 3 c# oop polymorphism dependency-injection unity-container
这些天我经常面临这种情况,我正在寻找一个优雅的解决方案。我有 :
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 { }
Run Code Online (Sandbox Code Playgroud)
我的目标是在客户端的角度使事情变得透明,并像这样使用这段代码:
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);
Run Code Online (Sandbox Code Playgroud)
所以问题是,由于我不知道对象的具体类型,因此我无法向其中注入正确的依赖项。
我目前正在做的是创建一个抽象工厂,以注入正确的属性。我有两个问题,第一个是我最终会拥有很多工厂。第二个是它使多态变得无用,因为客户端实际上需要关心“管理”底层类型(我需要在工厂中注入所有可能的依赖项,并在客户端代码上实例化工厂)。
1)因此,我正在考虑使用统一的属性注入,但是在手动实例化之后,我无法确定是否可以解决对象的依赖关系。即使使用这种方法,我想我仍然会遇到同样的问题:如果存在这样的语法,我不确定 unity 是否会检查对象的实际类型并解决正确的依赖关系:
unityContainer.Resolve<TypeA>(myDomainObject)
Run Code Online (Sandbox Code Playgroud)
如果没有,我需要提前知道类型,然后会回到同样的问题。
2)我发现这篇文章提到EF为DI提供了一些机制,但它似乎只是为了注入框架服务(PluralizationService等......)。否则,这将是实现这一目标的好方法。
3)在这种情况下我也不能使用 DI ......从概念上看,DI 看起来不太适合多态性。不过,我对这个想法并不感到兴奋。
我很乐意为我试图实现的属性注入提供解决方案,或者我可以使用模式的想法。但是,我真的不想创建一个大型基础设施并为此目的混淆我的代码。
注意:在这种情况下,我不希望您使用域事件。
谢谢
TL;DR
用特定于实现的构造依赖参数替换IDependency多态AbstractMethod的参数,该参数由 IoC 容器注入,而不是由消费者注入。
更详细
原始类层次结构需要看起来更像这样,继承多态才能工作,因为超类virtual方法和子类override方法必须匹配签名:
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)
// ...
}
}
Run Code Online (Sandbox Code Playgroud)
然而,有些事情并不符合这种设计:
AbtractMethod()通告它接受基地IDependency接口,两个子类实际上依赖于一个专门的子类的依赖。因此,如果可能的话,我会采用更传统的方法来安排依赖项,从而将依赖项传递给子类构造函数,并且在需要时可用于多态方法。这将需要为方法提供适当IDependency的内容。留给 IoC 容器做相应的依赖解析:
TypeB和TypeCIDependency将基类上的一个暴露TypeA给消费者,那么添加一个额外的抽象属性 to TypeAof type IDependency(但这似乎有问题)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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
tob*_*777 -1
非常感谢您对我的问题感兴趣,我昨天晚上想出了解决方案。目的是让事情对客户端透明,并通过诸如 baseObjectReference.AbstractMethodCall().
我终于意识到,通过利用 static 修饰符并将其用于 DI 目的,我能够实现我所追求的目标。所以我有:
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;
}
Run Code Online (Sandbox Code Playgroud)
然后,在我的统一容器中添加以下代码:
public static void RegisterTypes(IUnityContainer container)
{
// .... registrations are here as usual
GetSpecializedDependencyForB.CreateSpecializedDependencyForB = CreateMyDomainService;
}
Run Code Online (Sandbox Code Playgroud)
将此方法放在同一个统一配置类中:
private ISpecializedDependencyForB CreateMyDomainService()
{
return container.Value.Resolve<ISpecializedDependencyForB>();
}
Run Code Online (Sandbox Code Playgroud)
最后,我可以像这样简单地使用我的对象:
TypeA myDomainObject = database.TypeARepository.GetById(id);
myDomainObject.AbtractMethod();
Run Code Online (Sandbox Code Playgroud)
就是这样!
这里有四件事:
它基本上是一本手册,可以轻松地在 Unity 旁边设置“DI 框架”。
更重要的是,它就像一个魅力!我终于对我的设计感到满意了。我只会在多态情况下使用这种方法,因为在其他情况下在方法中注入正确的依赖项很容易。然而,使用这种方法完全封装域模型可能会很有趣。
| 归档时间: |
|
| 查看次数: |
3277 次 |
| 最近记录: |