我的基类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();
}
}
所以每个版本都有优点和缺点。哪个版本更好?或者你甚至可以建议别的东西。
答案 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
必须拥有Engine
,BrakingSystem
,{{您还知道这些是在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());
修改强>
您可以强制派生类初始化引擎。查看更新的示例。