だから、あなたは継承について尋ねていると思います。まあ、それを行うことができるバニラの方法はこれです:
public interface ICar
{
void ShiftGearUp();
void ShiftGearDown();
void SwitchLightsOn();
void SwitchLightsOff();
void Brake();
void Accelerate();
}
public class Car : ICar
{
public virtual void ShiftGearUp() { }
public virtual void ShiftGearDown() { }
public virtual void SwitchLightsOn() { }
public virtual void SwitchLightsOff() { }
public virtual void Brake() { }
public virtual void Accelerate() { }
}
public class DeluxeCar : Car
{
public override void SwitchLightsOn()
{
//calls the implementation in the Car class.
base.SwitchLightsOn();
//implement custom behavior.
this.AdjustCabinAmbientLighting();
}
private void AdjustCabinAmbientLighting() { }
}
このようにして、変更したい動作のみをオーバーライドします。
しかし、継承より構成を優先するという世界的に有名なルールがあります。
この方法でソリューションを形成します。
public interface ICar
{
void ShiftGearUp();
void ShiftGearDown();
void SwitchLightsOn();
void SwitchLightsOff();
void Brake();
void Accelerate();
}
public abstract class AbstractCar : ICar
{
protected ITransmission Transmission;
protected ILights Lights;
protected IEngine Engine;
protected IBrakes Brakes;
public void ShiftGearUp()
{
this.Transmission.ShiftUp();
}
public void ShiftGearDown()
{
this.Transmission.ShiftDown();
}
public void SwitchLightsOn()
{
this.Lights.SwitchOn();
}
public void SwitchLightsOff()
{
this.Lights.SwitchOff();
}
public void Brake()
{
this.Brakes.Brake();
}
public void Accelerate()
{
this.Engine.Accelerate();
}
}
public class Car : AbstractCar
{
public Car()
{
this.Lights = new AcmeLights();
//todo
//this.Engine = init engine object;
//this.Brakes = init brakes object;
//this.Transmission = init transmission object;
}
}
public class DeluxeCar : AbstractCar
{
public DeluxeCar()
{
this.Lights = new FancyLights();
//todo
//this.Engine = init engine object;
//this.Brakes = init brakes object;
//this.Transmission = init transmission object;
}
}
public interface ITransmission
{
void ShiftUp();
void ShiftDown();
}
public interface ILights
{
void SwitchOn();
void SwitchOff();
}
public interface IEngine
{
void Accelerate();
}
public interface IBrakes
{
void Brake();
}
public class AcmeLights : ILights
{
private LightSwitch Switch;
public void SwitchOn() { this.Switch.On(); }
public void SwitchOff() { this.Switch.Off(); }
}
public class FancyLights : ILights
{
private LightSwitch Switch;
private CabinAmbientLightingController AmbientLightingController;
public void SwitchOn()
{
this.Switch.On();
this.AmbientLightingController.AdjustLightLevel();
}
public void SwitchOff()
{
this.Switch.Off();
this.AmbientLightingController.AdjustLightLevel();
}
}
public class LightSwitch
{
public void On() { }
public void Off() { }
}
public class CabinAmbientLightingController
{
public void AdjustLightLevel() { }
}
編集: 次に、選択した依存性注入ツールを使用して、コンストラクターを介して Car および Deluxe car の依存性を注入できます。これはおそらく「AOP」です。
編集:その目的のために、おそらく動的プロキシを使用できます。有名なチュートリアルはこれです。少し学習曲線のようなものかもしれません (そして、この問題に対する最良の解決策ではないかもしれませんが、私は他の解決策を知りません) - 全体として、素晴らしいフレームワークであり、興味深い読み物です。
私がすること、そして私の DynProxy が少しさびていることに注意してください、次のように空のクラスを宣言します
public class RocketCar
{
}
次に、ターゲットなしでクラス プロキシを生成し、インターセプターを使用して呼び出しを転送します。したがって、依存関係を注入できる汎用インターセプターを作成する必要があります (上記の 2 つの具体的な Car のように)。そのため、そのようなクラスは 1 つしかなく、必要に応じて構成して使用します。これは1日か2日でコーディングできるはずだと思います:)アイデアは、ここの「インターフェースを実装しないクラス」セクションです。RocketCar に IRocket と ICar を実装させます。
しかし!1 つのクラスに多くのインターフェイスを実装することは、最善の方法ではありません。あなたはSRPを壊しています。とにかくこれらのインターフェースがすべてある場合、クライアントコードが個々のインターフェースによってそれらを参照するようにしないのはなぜですか? 結局のところ、RocketCar が ICar を実装する場合、その Accelerate メソッドは、それが別の車の一部であることを認識してはなりません。言い換えれば、RocketCar は、RocketCar がどこで使用されても他の ICar 実装で置き換えることができるように動作する必要があります。そうしないと、 LSPが壊れてしまいます。
そのため、ICar と IRocket を別々のクラスにすることをお勧めします。これらの特定の実装は、相互の相互作用をモデル化する具体的な型参照を持つことができます。
クラスに複数のインターフェイスを実装させるべきではないと言っているわけではありませんが、それは規則ではなく例外であるべきです。