使用依赖项注入公开类的其他属性的最佳方法是什么

时间:2013-03-20 20:54:54

标签: .net dependency-injection

我可能会以错误的方式接近这一点。我希望在一类类上使用依赖注入,以便在运行时根据设置我可以将正确的类解析为正确的类。我创建了一个接口,其中包含将在所有类中使用的基本属性和方法。一些类具有其他属性以扩展基本实现。除了反射或方法之外,还有其他方法来获取和设置其他属性以在客户端公开/与扩展属性交互吗?

如果这不合适,我会更好地不使用DI并坚持在客户端上对这些类进行硬实例化吗?

感谢您对此提出任何建议。

1 个答案:

答案 0 :(得分:0)

  

我创建了一个接口,其中包含将在所有类中使用的基本属性和方法。

我认为意味着实现类

  

有些类具有其他属性来扩展基础实现。

对于进一步讨论,您必须了解这些其他属性是否真的需要从这些类的客户端的角度来看,或者它们实际上只是私有实现细节。

  • 假设它们是私有实现细节,那么您不应该公开显示它们,并且您可以愉快地继续作为接口实例访问对象。

  • 如果客户需要使用其他属性,那么如果您正在进行访问,则违反了LSP(Liskov替换原则,SOLID原则之一)那些通过接口实例上的反射。请注意,这些原则不是规则,但遵循它们将对您有所帮助。违反LSP告诉我,你并不是真的想要像在单一界面后面那样抽象对象。虽然在这种情况下使用强制转换或 as 运算符来查看实际类型是完全合法的,但它既不会在您的嘴里留下美味,也不会之后胃部感觉良好。

  

除了反射或方法之外,还有其他方法来获取和设置其他属性以在客户端公开/与扩展属性交互吗?

这是一种方式:

首先,今天这是你在没有额外属性的“简单”情况下所拥有的:

interface ICar
{
    void Accelerate();
}

class CompactCar : ICar
{
    public void Accelerate()
    {
        // Slooooow acceleration
    }
}

class NasCar : ICar
{
    public void Accelerate()
    {
        // Yiehaa!
    }
}

sealed class TrafficSimulator
{
    private readonly List<ICar> _cars = new List<ICar>();

    public void AddCar(ICar car)
    {
        _cars.Add(car);
        car.Accelerate();
    }
}

然后你会发现自己为某些具体类型添加了一些属性,而其他类型则添加了一些属性:

class CompactCar : ICar
{
    // Can only be used *before* the car is accelerated
    public bool CanOnlyTurn { get; set; } 
    ...
}

class NasCar : ICar
{
    // Can only be used *after* the car is accelerated
    public string NumberPlate { get; set; }
    ...
}

sealed class TrafficSimulator
{
    ...
    public void AddCar(ICar car)
    {
        _cars.Add(car);

        // Check if there's something to do before accelerating
        CompactCar compact = car as CompactCar;

        if (compact != null)
        {
            compact.CanOnlyTurn = true;
        }

        car.Accelerate();

        // Check if there's something to do after accelerating
        NasCar nascar = car as NasCar;

        if (nascar != null)
        {
            nascar.NumberPlate = "I rule!";
        }
    }
}

现在,当你继续这样做时,上面的内容将变得非常混乱。这就是违反LSP会带给你的。总是有理由添加这些额外的属性。在上面的例子中,有时在加速之前做一个“特殊”的事情,之后做另一个“特殊”的事情。

此特定情况可以通过以下方式解决 sortof

interface ICarSetup
{
    void BeforeAccelerate();
}

interface ICarTeardown
{
    void AfterAccelerate();
}

class ActionSetup : ICarSetup
{
    private readonly Action _action;

    public ActionSetup(Action action)
    {
        _action = action;
    }

    public void BeforeAccelerate()
    {
        _action();
    }
}

class NullSetup : ICarSetup
{
    public void BeforeAccelerate()
    {
    }
}

class ActionTeardown : ICarTeardown
{
    private readonly Action _action;

    public ActionTeardown(Action action)
    {
        _action = action;
    }

    public void AfterAccelerate()
    {
        _action();
    }
}

class NullTeardown : ICarTeardown
{
    public void AfterAccelerate()
    {
    }
}

sealed class TrafficSimulatorDriver
{
    private TrafficSimulator _trafficSimulator;

    public void RunTheSimulation()
    {
        var nascar = new NasCar();
        var nascarSetup = new NullSetup();
        var nascarTeardown = new ActionTeardown(() => nascar.NumberPlate = "I rule!");
        _trafficSimulator.AddCar(nascar, nascarSetup, nascarTeardown);

        var compact = new CompactCar();
        var compactSetup = new ActionSetup(() => compact.CanOnlyTurn = true);
        var compactTeardown = new NullTeardown();
        _trafficSimulator.AddCar(compact, compactSetup, compactTeardown);
    }
}

sealed class TrafficSimulator
{
    ...
    public void AddCar(ICar car, ICarSetup setup, ICarTeardown teardown)
    {
        _cars.Add(car);
        setup.BeforeAccelerate();
        car.Accelerate();
        teardown.AfterAccelerate();
    }
}
  

如果这不合适,我会更好地不使用DI并坚持在客户端上对这些类进行硬实例化吗?

不要混淆这两个问题; DI和硬实例化并不相互排斥。仅仅因为你没有以接口实例的形式注入依赖项并不意味着你不能注入一个具体的实例。