6

我的基类Car包含engine无法在基类中初始化的字段。我只能在子类中初始化它,例如在ElectricCari can writeengine = new ElectricEngine中。但是我在基类中使用字段。所以我有一个使用但未初始化的字段:

public class Car {

    protected Engine engine;

    public void Start() {
        engine.Start();
        // do something else
    }

    public void Stop {
        engine.Stop();
        // do something else
    }

    public void Diagnose() {
        engine.Diagnose();
        // anotherField.Diagnose();
        // oneAnotherField.Diagnose();
    }

}

如何更好地初始化引擎?

版本 1. 字段保证被初始化,但有很多字段构造函数看起来很难看。没有错误但丑陋。

public class Car {

    protected Engine engine;

    public Car(Engine engine) {
        this.engine = engine;
    }

    public void Start() {
        engine.Start();
        // do something else
    }

    public void Stop {
        engine.Stop();
        // do something else
    }

    public void Diagnose() {
        engine.Diagnose();
        // anotherField.Diagnose();
        // oneAnotherField.Diagnose();
    }

}

public class ElectricCar : Car {
    public ElectricCar() : base (new ElectricEngine()) {
    }
}

版本 2. 子类应该记住初始化字段,与子类有这样的“合同”可能会引入错误(未初始化的字段)。

public class Car {

    protected Engine engine;

    public Car() {
    }

    public void Start() {
        engine.Start();
        // do something else
    }

    public void Stop {
        engine.Stop();
        // do something else
    }

    public void Diagnose() {
        engine.Diagnose();
        // anotherField.Diagnose();
        // oneAnotherField.Diagnose();
    }

}

public class ElectricCar : Car  {
    public ElectricCar() {
        engine = new ElectricEngine();
    }
}

版本 3. 保证初始化字段。构造函数很清楚。但是从构造函数调用虚方法(可能很危险,一般不推荐)。

public class Car {

    protected Engine engine;

    public Car() {
        InitializeEngine();
    }

    protected abstract void InitializeEngine();

    public void Start() {
        engine.Start();
        // do something else
    }

    public void Stop {
        engine.Stop();
        // do something else
    }

    public void Diagnose() {
        engine.Diagnose();
        // anotherField.Diagnose();
        // oneAnotherField.Diagnose();
    }

}

public class ElectricCar : Car  {
    public ElectricCar() {
    }

    protected void override InitializeEngine() {
        engine = new ElectricEngine();
    }
}

所以每个版本都有优缺点。哪个版本更好?或者,您甚至可以提出其他建议。

4

4 回答 4

5

第 3 版是对模板方法设计模式的一种借鉴。如果你的基类不能提供合理的默认实现,但是你要求每辆车都有一个引擎,那么将创建委托给基类是一个非常合适且安全的解决方案。我会稍微调整你的初始化是这样的:

protected abstract Engine InitializeEngine();

然后在 Car 的构造函数中:

public Car() {
    engine = InitializeEngine();
}

这将使合同非常明确。您的子类只需要提供一个引擎,而您的基类将保证在调用构造函数后分配引擎变量。

于 2013-01-14T08:06:22.967 回答
2

另一种选择可能是:

public class Car {

    private Engine engine; //PRIVATE  

    protected Engine MyEngine {   //PROTECTED PROPERTY
        get {
            if(engine == null) 
               engine = new Engine(); 
            return engine;
        }
    }
}

这样,调用者将安全地使用始终初始化的成员,因为它检查protected了它只能访问的内部属性,因为字段是private.

于 2013-01-14T08:06:34.403 回答
1

我会投票给选项 1。您在构造函数中明确说明每个都Car 必须Engine, BrakingSystem,ECU等。您还知道这些是在Car. 如果您将它们的创建延迟到第一次访问并且创建它们时出现问题,那么适当地处理异常将更加困难。

于 2013-01-14T08:32:36.750 回答
0

然后使用 property 而不是 field for Engine,因为非私有字段很难调试。

关于设计 首先,您必须声明Carabstract并使用IEngine行为而不是引擎类。然后对于任何具体的汽车(即子类),您可以选择适当的注入类型(通过构造函数,通过属性,...)。

public interface IEngine
{
     void Start();
     void Stop();
     void Diagnose();
}


public abstract class Car
{
    protected Car(IEngine engine)
    {
         Engine = engine;
    }

    protected IEngine Engine {get; set;}

    public void Start() {
        engine.Start();
        // do something else
    }

    public void Stop() {
        engine.Stop();
        // do something else
    }

    public void Diagnose() {
        engine.Diagnose();
        // anotherField.Diagnose();
        // oneAnotherField.Diagnose();
    }
}

public class ConcreteCar : Car
{
    public ConcreteCar(IEngine engine):base(engine)  // injection by constructor
    {
    }

    ...
}

用法:

Car concreteCar = new ConcreteCar(new ConcreteEngine());

编辑

您可以强制派生类初始化引擎。请参阅更新的示例。

于 2013-01-14T08:44:39.380 回答