My base class Car
contains field engine
which can not be initialized in base class. I can initialize it in subclass only, for example in ElectricCar
i can write engine = new ElectricEngine
. However I use field in base class. So I have a field which used but not initialized:
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();
}
}
How to better initialize engine?
Version 1. Field guaranteed to be initialized but with many fields constructor will look ugly. Bug-free but ugly.
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()) {
}
}
Version 2. Subclasses should remember to initialize the field, having such "contract" with subclasses may introduce bugs (uninitialized field).
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();
}
}
Version 3. Field guaranteed to be initialized. Constructor is clear. But calling virtual method from constructor (potentially dangerous, not recommended in general).
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();
}
}
So every version has pros and cons. Which version is better? Or probably you can suggest even something else.
If you do not explicitly initialize a base class or member that has constructors by calling a constructor, the compiler automatically initializes the base class or member with a default constructor.
Use the name of the base class in an initializer-list. The initializer-list appears after the constructor signature before the method body and can be used to initialize base classes and members. class Base { public: Base(char* name) { // ... } }; class Derived : Base { public: Derived() : Base("hello") { // ... } };
The members of base aggregate class cannot be individually initialized in the constructor of the derived class.
To initialize a class member variable, put the initialization code in a static initialization block, as the following section shows. To initialize an instance member variable, put the initialization code in a constructor.
Version 3 is sort of a take on the Template method design pattern. If your base class can't provide a reasonable default implementation, but you require every car to have an engine, delegating the creation to the base class is a very appropriate and safe solution. I would slightly adjust your initialization to be something like this:
protected abstract Engine InitializeEngine();
Then in your constructor for Car:
public Car() {
engine = InitializeEngine();
}
This will make the contract very clear. Your subclasses simply need to provide an engine and your base class will guarantee that the engine variable is assigned after the constructor is called.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With