0

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

如果这不是一个合适的用途,我会不会更好地不使用 DI 并坚持在客户端硬实例化这些类?

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

4

1 回答 1

0

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

我假设这些意味着实现类

一些类具有其他属性以扩展基本实现。

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

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

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

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

这是一种方式:

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

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 会给你带来的。总是有理由添加这些额外的属性。在上面的例子中,有时在加速之前做一件“特殊”的事情,在加速之后做另一件“特殊”的事情。

这种特定情况可以通过以下方式解决:

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 和硬实例化并不相互排斥。仅仅因为您不以接口实例的形式注入依赖项并不意味着您不能注入具体实例。

于 2013-03-20T23:52:07.907 回答