如何初始化基类中应该在子类中初始化的字段?

时间:2013-01-14 08:02:32

标签: c# oop

我的基类Car包含无法在基类中初始化的字段engine。我只能在子类中初始化它,例如在ElectricCar我可以写engine = 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 个答案:

答案 0 :(得分:5)

版本3类似于Template method design pattern。如果你的基类不能提供合理的默认实现,但是你需要每辆车都有一个引擎,那么将创建委托给基类是一个非常合适和安全的解决方案。我会略微调整你的初始化,如下所示:

protected abstract Engine InitializeEngine();

然后在Car的构造函数中:

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

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

答案 1 :(得分:2)

另一种选择可能是:

public class Car {

    private Engine engine; //PRIVATE  

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

通过这种方式,调用者将是安全的,它将使用始终初始化的成员,因为它在protected属性中检查它只能访问,因为字段为private。< / p>

答案 2 :(得分:1)

我投票支持选项1.您明确在构造函数中声明每个Car 必须拥有EngineBrakingSystem,{{您还知道这些是在ECU之前创建的。如果您在第一次访问之前延迟创建它们并且创建它们时出现问题,那么适当地处理异常将更加困难。

答案 3 :(得分:0)

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

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

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());

修改

您可以强制派生类初始化引擎。查看更新的示例。