1

ここで多くの投稿を見てきましたが、この質問に対する答えが完全にはわかりません。

コンポジションを使用してインターフェイスを実装し、すべてのメソッドをコンポーネントで呼び出すことができるようにしたいと考えています。

  1. コンポーネント (つまり、車) のインターフェイスを作成します。
  2. クラスを作成し、コンポーネントを実装します (つまり、SimpleCar)
  3. Component メンバーで呼び出して、Component インターフェイスを実装する別のクラスを作成します (つまり、SimpleCar メンバーでメソッドを呼び出してメソッドを実装する DeluxeCar を作成します)。

ここで重要な点は、DeluxeCar に SimpleCar のメソッドを呼び出させるラッパー メソッドをすべて記述せずにパート 3 を実行できるようにしたいということです。C# デコレータまたはいくつかの C# テンプレート マジックを追加するだけでよいことをお勧めします。

私が見た 2 つの解決策: 1. ExtensionMethods - CarComposite インターフェイスを実装し、その上に拡張メソッドを配置できると思います (C. Lawrence Wenham の回答: Multiple Inheritance in C# でここで行われたことと同様)。ただし、Composite インターフェイスに Component インターフェイスも実装させることはできないと思います。少なくとも、私は実用的な例を得ることができませんでした。多分私は間違っています。2. アスペクト指向プログラミング。PostSharp または別の AOP フレームワークを使用してステップ 3 を実行します。これを行う方法に関するサンプル コードを見つけることができませんでした。さらに、ライセンスが気に入らず、完全に無料のソリューションが必要なため、PostSharp は避けたいと思います。とにかく、PostSharp は、無料版ではこれを行うことができないと述べていると思います (AspectInheritance http://www.)。Spring.Net の実際の例は大いに役立ちます。

標準の C# ライブラリまたは Spring.net を使用する際に #3 の例を教えてください。

アップデート

h.alex の提案は興味深いですが、それでも私が必要とすることはできません。抽象クラスの使用は少し制限されています。Car インターフェースに加えて Boat インターフェースがあると想像してください。次に、Boat と Car の両方を実装する AmphibiousVehicle を作成します。C# には多重継承がないため、AbstractCar クラスと AbstractBoat クラスの両方を継承することはできません。ボイラープレートをさらに追加し、AbstractAmphibiousVechicle 抽象クラスも作成する必要があります。あまり多くの異なるクラスがない場合は、これで問題ないかもしれません。しかし、多くの異なるクラスを作成し、これらの動作を組み合わせて使用​​したい場合があります。これは、ビデオゲームではかなり一般的です。ゲームオブジェクトでできるようにしたいさまざまな側面がたくさんあるかもしれません (押す、投げる、集めるなど)。

PostSharp にあるこのようなものが欲しいのですが、同じことができる制限の少ないライセンスのフレームワークがあるかどうかを知りたいです。これは、たとえばSpring.netで簡単に実行できるものですか? http://doc.postsharp.net/postsharp-3.0/##PostSharp-3.0.chm/html/T_PostSharp_Aspects_CompositionAspect.htm

http://doc.postsharp.net/postsharp-3.0/Default.aspx#PostSharp-3.0.chm/html/20c81e23-af91-4688-a672-b0f3cce793a9.htm

4

1 に答える 1

1

だから、あなたは継承について尋ねていると思います。まあ、それを行うことができるバニラの方法はこれです:

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 を別々のクラスにすることをお勧めします。これらの特定の実装は、相互の相互作用をモデル化する具体的な型参照を持つことができます。

クラスに複数のインターフェイスを実装させるべきではないと言っているわけではありませんが、それは規則ではなく例外であるべきです。

于 2013-07-30T07:24:27.267 に答える