構成を通じてインターフェースを実装するクラスのボイラープレートを削減する


10

:私はクラスの持っているA、小さなクラスの数の複合体であるBCD

BC、およびDインターフェースを実装IBICおよびIDそれぞれ。

以来Aのすべての機能をサポートBCおよびDA実装IBICおよびID同様に、しかし、多くのこの残念ながらリードの実装で再ルーティングA

そのようです:

interface IB
{
    int Foo {get;}
}

public class B : IB
{
    public int Foo {get {return 5;}}
}

interface IC
{
    void Bar();
}

public class C : IC
{
    public void Bar()
    {
    }
}

interface ID
{
    string Bash{get; set;}
}

public class D: ID
{
    public string Bash {get;set;}
}

class A : IB, IC, ID
{
    private B b = new B();
    private C c = new C();
    private D d = new D();

    public int Foo {get {return b.Foo}}

    public A(string bash)
    {
        Bash = bash;
    }

    public void Bar()
    {
        c.Bar();
    }

    public string Bash
    {
         get {return d.Bash;}
         set {d.Bash = value;}
    }
}

このボイラープレートのリダイレクトを取り除く方法はありAますか?BCおよびD全て異なるが、共通の機能を実装し、私そのようなA実装IBICおよびIDそれが意味するので、私は渡すことができA、これらのインタフェースにマッチ依存性としてそれ自体、および内側ヘルパーメソッドを公開する必要はありません。


IB、IC、IDを実装するためにクラスAが必要な理由について少しお話しいただけますか?BCDをパブリックとして公開しないのはなぜですか?
Esben Skov Pedersen 2015

私が好むのための私の主な理由だと思うA実装しIBICかつIDそれが書き込みに、より賢明な感じであるPerson.Walk()とは対照的に、Person.WalkHelper.Walk()例えば、。
Nick Udell 2015年

回答:


7

あなたが探しているものは、一般的にミックスインと呼ばれています。悲しいことに、C#はそれらをネイティブにサポートしていません。

1つ2つ3つなど、いくつかの回避策があります。

私は実際に最後のものが本当に好きです。自動生成された部分クラスを使用してボイラープレートを生成するという考えは、おそらく実際に良い解決策を得ることができる最も近いものです。

[pMixins]は、pMixin属性で装飾された部分クラスのソリューションをスキャンするVisual Studioプラグインです。クラスを部分的にマークすることにより、[pMixins]は分離コードファイルを作成し、クラスにメンバーを追加できます。


2

コード自体のボイラープレートは削減されませんが、Visual Studio 2015には、ボイラープレートを自動的に生成するリファクタリングオプションが付属しています。

これを使用するには、まずインターフェースIExampleと実装を作成しますExample。次に、新しいクラスCompositeを作成してそれを継承IExampleさせますが、インターフェースは実装しません。

タイプのプロパティまたはフィールドを追加しExample、あなたにComposite、クラストークンのクイックアクションメニューを開きIExample、あなたの中にCompositeクラスファイルとこの場合の「例」はフィールドまたはプロパティの名前である「『例』を介してインタフェースを実装」を選択します。

Visual Studioはインターフェイスで定義されたすべてのメソッドとプロパティを生成してこのヘルパークラスにリダイレクトしますが、この回答が投稿された時点ではイベントをリダイレクトしないことに注意してください。


1

クラスの処理が多すぎるため、多くの定型文を実装する必要があります。あなたはコメントで言う:

Person.WalkHelper.Walk()とは対照的に、Person.Walk()を書く方が賢明です。

同意しません。歩く行為には多くのルールが含まれる可能性が高く、Personクラスには属していません。Personが実装WalkingServiceするwalk(IWalkable)メソッドを持つクラスに属していますIWalkable

コードを作成するときは、常にSOLIDの原則に留意してください。ここで最も適切な2つは、懸念の分離(ウォーキングコードを別のクラスに抽出する)とインターフェイスの分離(特定のタスク/機能/機能にインターフェイスを分割する)です。複数のインターフェイスを備えたIの一部を使用しているように見えるかもしれませんが、1つのクラスにそれらを実装させることで、すべての優れた作業を取り消しています。


私の例では、機能個別のクラスに実装されており、必要な動作をグループ化する方法として、これらのいくつかで構成されるクラスがあります。実際の実装は私の大きなクラスには存在せず、すべて小さなコンポーネントによって処理されます。おそらくあなたは正しいと思います。ヘルパークラスを公開して直接対話できるようにするのがよいでしょう。
Nick Udell 2015年

4
walk(IWalkable)メソッドのアイデアについては、ウォーキングサービスが発信者の責任であり、担当者の責任ではなく、一貫性が保証されないため、個人的には嫌いです。呼び出し元は、一貫性を保証するためにどのサービスを使用するかを知る必要がありますが、Personクラスに保持することは、依存関係の反転を可能にしながら、歩行動作が異なるように手動で変更する必要があることを意味します。
Nick Udell 2015年

0

あなたが探しているのは多重継承です。ただし、C#にもJavaにもありません。

AからBを拡張できます。これにより、Bのプライベートフィールドやリダイレクトグルーの必要がなくなります。ただし、これはB、C、またはDのいずれかに対してのみ実行できます。

ここで、CをBから継承し、DをCから継承できる場合は、AにDを拡張するだけで十分です。それのための。

C ++では、AにB、C、Dから継承させることができます。

しかし、多重継承はプログラムを推論することを難しくし、それ自体に問題があります。さらに、それを避けるための明確な方法がしばしばあります。それでも、それがない場合もあるので、繰り返しボイラープレートとオブジェクトモデルがどのように公開されるかの間で設計のトレードオフを行う必要があります。

構成と継承を混在させようとしているように感じます。これがC ++の場合、純粋な継承ソリューションを使用できます。しかし、それは私が作曲を受け入れることをお勧めしませんので。


2
opが彼のサンプルコードで行ったように、javaとc#の両方でインターフェイスを使用して複数の継承を持つことができます。
Robert Harvey

1
インターフェースを複数継承と同じように実装することを検討する人もいます(たとえばC ++で)。インターフェースが継承可能なインスタンスメソッドを提示できないため、他の人は同意しません。これは、複数の継承がある場合にあることです。C#では、複数の基本クラスを拡張できないため、複数のクラスからインスタンスメソッド実装を継承できません。そのため、すべてのボイラープレートが必要です...
Erik Eidt
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.