仅在运行时知道类型的问题导致DI痛苦

时间:2011-03-22 11:14:16

标签: c# structuremap

我将用一个例子来解释我的问题。

我有一个AnimalService,允许我增加最喜欢的动物在特定动物园的展示时间:

public sealed class AnimalService<TZoo> : IAnimalService<TZoo> where TZoo : IZoo
{
    private readonly IFavouriteAnimalResolver<TZoo> favouriteAnimalResolver;
    private readonly IAnimalShowTimeService animalShowTimeService;

    public AnimalService(
            IFavouriteAnimalResolver<TZoo> favouriteAnimalResolver, 
            IAnimalShowTimeService animalShowTimeService)
    {
        this.favouriteAnimalResolver = favouriteAnimalResolver;
        this.animalShowTimeService = animalShowTimeService;
    }

    public void IncreaseShowTimeForFavouriteAnimal(TZoo zoo)
    {
        var favouriteAnimal = favouriteAnimalResolver.GetFavouriteAnimal(zoo);

        animalShowTimeService.IncreaseShowTimeForAnimal(favouriteAnimal);
    }
}

AnimalService使用解析器为TZoo获取最喜欢的动物,然后调用IAnimalShowTimeService的实例来增加最喜欢的动物将获得的显示时间。下面是IFavouriteAnimalResolver界面的定义及其实现,它允许我为LondonZoo解析最喜欢的动物:

public interface IFavouriteAnimalResolver<TZoo> where TZoo : IZoo
{
    IAnimal GetFavouriteAnimal(TZoo londonZoo);
}

public class LondonZooFavouriteAnimalResolver : IFavouriteAnimalResolver<LondonZoo>
{
    public IAnimal GetFavouriteAnimal(LondonZoo londonZoo)
    {
        return new Lion();
    }
}
Oki,到目前为止一切都很好。现在为了复杂化。一旦运行IncreaseShowTimeForFavouriteAnimal,我想执行一些动物特定的逻辑。所以我的基础AnimalShowTimeService存根看起来像这样:

public class AnimalShowTimeService : IAnimalShowTimeService
{
    public void IncreaseShowTimeForAnimal(IAnimal animal)
    {
        // Update the show time for the animal

        // Now call out to the AnimalUpdatedService<> instance to do any logic required for the animal
    }
}

我希望能够调用一个更新服务,该服务将通过特定动物类型的结构图解析,因此我可以运行与该特定动物类型相关的一些更新逻辑。为此,我有以下animalupdated接口:

public interface IAnimalUpdatedService<T> where T : IAnimal
{
    void LogTheUpdate(T animal);
}

public class DefaultAnimalUpdatedService<T> : IAnimalUpdatedService, IAnimalUpdatedService<T> where T : IAnimal
{
    public void LogTheUpdate(T animal)
    {

    }
}

public class LionUpdatedService : IAnimalUpdatedService<Lion>
{
    public void LogTheUpdate(Lion animal)
    {
        Console.WriteLine("The lion was updated");
    }
}

如您所见,我有一个DefaultAnimalUpdatedService,我想在没有为动物注册特定更新服务时使用它。我还有一个LionUpdatedService,我想在动物园每次狮子会的演出时间增加时使用它。

我的问题是,因为动物园最喜欢的动物可以是任何动物,所以IFavouriteAnimalResolver返回一个IAnimal类型,而不是具体的。所以我不确定如何在Lion的节目时间更新时使用IncreaseShowTimeForAnimal中的structuremap来获取LionUpdatedService服务。我玩过以下代码,但这不起作用,因为我不知道设计时的具体情况:

    public class AnimalShowTimeService : IAnimalShowTimeService
{
    public void IncreaseShowTimeForAnimal(IAnimal animal)
    {
        // Update the show time for the animal

        var animalUpdatedService = ObjectFactory.ForGenericType(typeof(IAnimalUpdatedService<>))
                                                .WithParameters(animal.GetType())
                                                .GetInstanceAs<IDONTKNOWTHECONCRETE>();

        animalUpdatedService.LogTheUpdate(animal);
    }
}

我希望这一切都清楚。 :)

我对StrutureMap不太熟悉,如果有人知道解决这个问题的优雅方法,我将不胜感激。

我已经使用上述代码压缩了我创建的测试项目。你可以在这里下载它,如果你想在一个快速的环境中愚弄: [删除此链接 - 不再需要]

修改

这只是我创建的一个测试项目,用于说明我目前在一个更大,更复杂的项目中遇到的问题。不幸的是,我不能重新设计项目的整个架构,以找到更适合这个解决方案的更好的设计,因为我根本没有时间。能够获得结构图调用只需根据上述要求返回正确的混凝土将是我的直接胜利。了解更好的设计以确保这样的事情不再发生在我身上将是次要的胜利。

谢谢大家:)

2 个答案:

答案 0 :(得分:0)

几点:

a)遵循“告诉,不要问”的原则,不是AnimalShowTimeService的解析类型的重复性。将Animal推送到另一个对象进行选择。

b)对域内的ObjectFactory进行硬编码引用是一种糟糕的设计。 DI容器的目的是分离对象,而不是将耦合移动到其他位置(在本例中为StructureMap)。

编辑:

关于a)不要以多态方式解决它。您不需要泛型来共享行为,而继承层次只会增加耦合。如果你真的需要沿着这条道路前进,我认为实施你自己的公约可能就是你正在寻找的。或者您可以命名IAmimal的每个实例,并使用ObjectFactory.GetInstance(animal.GetType()。ToString())解析服务,但这显然不是理想的。

但我认为重点在于,你这样做是为了DI&amp; amp; DI容器(我认为),如果你不能让你的设计适合,也许你需要废弃它并重新开始,而不是试图将方形钉子推入圆孔。

答案 1 :(得分:0)

Oki,我已经解决了我的问题。即,访客模式。 : - )

快速参考:The Visitor Pattern

所以我定义了一个show time更新的访问者,它可以包含每种特定动物类型的逻辑:

public interface IShowTimeUpdatedVisitor
{
    void Visit(Lion lion);
    void Visit(Elephant lion);
    void Visit(IAnimal animal);
}

public class ShowTimeUpdatedVisitor : IShowTimeUpdatedVisitor
{
    public void Visit(Lion lion)
    {
        //do stuff with a lion
    }

    public void Visit(Elephant elephant)
    {
        //do stuff with an elephant
    }

    public void Visit(IAnimal animal)
    {
        // this will be the default which will be hit if no Visit method for the concrete exists
    }
}

然后我对IAnimal接口进行了修改,以允许每个实现针对ShowTimeUpdatedVisitor调用正确的方法:

public interface IAnimal
{
    void ShowTimeUpdated(IShowTimeUpdatedVisitor updatedVisitor);
}

public class Lion : IAnimal
{
    public void ShowTimeUpdated(IShowTimeUpdatedVisitor updatedVisitor)
    {
        updatedVisitor.Visit(this);
    }
}

现在,我可以像这样实现我的AnimalShowTime服务:

public class AnimalShowTimeService : IAnimalShowTimeService
{
    readonly IShowTimeUpdatedVisitor showTimeUpdatedVisitor;

    public AnimalShowTimeService(
            IShowTimeUpdatedVisitor showTimeUpdatedVisitor)
    {
        this.showTimeUpdatedVisitor = showTimeUpdatedVisitor;
    }

    public void IncreaseShowTimeForAnimal(IAnimal animal)
    {
        animal.ShowTimeUpdated(showTimeUpdatedVisitor);
    }
}

所以最后我没有做任何凌乱的StructureMap代码。 :)

希望这有助于其他人。