6

私の基本クラスには、基本クラスで初期化できないCarフィールドが含まれています。engineサブクラスでのみ初期化できます。たとえば、ElectricCari can write 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

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 .の前に作成されていることも知っています。それらの作成を最初のアクセスまで遅らせ、それらの作成に問題がある場合、例外を適切に処理することがより困難になります。EngineBrakingSystemECUCar

于 2013-01-14T08:32:36.750 に答える
0

Engine非プライベート フィールドはデバッグが非常に難しいため、フィールドの代わりにプロパティを使用します。

設計 について まず、Engine クラスの代わりにCarasを宣言し、behaviorabstractを使用する必要があります。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 に答える