2017-02-14 17 views
2

私が作業しているコードでは、コードの一部が現在のソフトウェアセッションに依存する構造があります。ソフトウェアセッションには、合成によって依存性が注入される複数のヘルパーオブジェクトが含まれます。C#での合成インターフェイスの簡略化

1つの例では、データリポジトリへのアクセスを含むIRepositoryが注入されています。 IRepositoryには、IDbContextを介してデータベースに書き込むDatabaseContextが含まれています。

SoftwareSessionは、ゲートウェイとして機能するデータベースへのすべてのアクセスに使用される、唯一の注入インフラストラクチャです。つまり、WriteCarなどのオブジェクトをデータベースに書きたい場合は、3つのインタフェース、2つの関数を作成オブジェクトに委譲し、1つの関数を実装で実装する必要があります。以下のコード部分で明確になっています。 WriteCarシグネチャは、3つのインタフェース(IRepository、ISoftwareSession、IDbContext)、複合オブジェクト関連関数と実際の実装(IDbContext)を単に呼び出す実装されていない2箇所(Repository、SoftwareSession)で同じに定義されています

これは、私がリファクタリング、コードの移動、機能の追加、または機能シグネチャの変更をしたいとき、私は常に1つの機能に対して6つの場所を変更する必要があることを意味します。

これは、テスト容易性を向上させるための最良の環境を提供し、ソフトウェアセッションがリポジトリへのアクセスをラップし、リポジトリがデータコンテキストへのアクセスをラップするベストプラクティスに従っていると思います。 、または私は下のコードでいくつかの概念の誤解を持っていますか?

これを実装するための、構造的にメンテナンス可能な方法は何ですか?たぶん、新機能ごとに書かれたコードの量を減らすために、ラムダやデリゲートのいくつかの巧妙な方法を使用していますか?または、Visual Studio、Resharperなどを使用して、ある種のテンプレートメカニズムからこのコードを簡単に生成するためのライブラリ(オートマッパのようなDTOを簡略化する)やツールもあります。

コンセプトの混乱がある場合は、私にお知らせください。私の同僚の中には、似たような見解がある人もいます。その場合、他者の誤解を明確にするのに役立つかもしれません。一つには

public class SoftwareSession : ISoftwareSession 
{ 
    ... 
    IRepository repository; 
    public void WriteCar(Car car){ 
     repository.WriteCar(car); 
    } 
    ... 
} 

public interface ISoftwareSession{ 
    ... 
    void WriteCar(Car car); 
    ... 
} 


public class Repository : IRepository{ 
    ... 
    IDbContext context; 
    public void WriteCar(Car car){ 
     context.WriteCar(car); 
    } 
    ...   
} 

public interface IRepository{ 
    ... 
    void WriteCar(Car car); 
    ... 
} 

public class MyDbContext : IDbContext{ 
    ... 
    public void WriteCar(Car car){ 
     //The Actual Implementation here. 
     ... 
    } 
    ... 
} 

public interface IDbContext{ 
    ... 
    void WriteCar(Car car); 
    ... 
} 
+0

インタフェース:そして、ここ

public class Repository : IRepository { IDbContext context; public Repository(IDbContext dbContext) { context = dbContext; } public void WriteCar(Car car) { context.WriteCar(car); } } 

は、コンポジションのルートであります継承することもできます!なぜ、ICarWriterインターフェースを持たず、次にIRepository:ICarWriter、ISoftwareSession:ICarWriterなどを持っているのでしょうか。 –

+0

私はあなたのソフトウェアセッションの実装に問題があると考えています。これは、あなたのフレームワークにはデータベース、writeCar、writeCustomerなどに行くものがすべて表示されるため、Single Responsibilitiesに違反します。 – Dys1

+0

@ BarryO'Kane私は同意します - これはインターフェイスをリファクタリングする方法であり、重複したコードの見方を減らすことに意味があります。私はこれを単純化する他の可能な方法を待っても構わない。 – user3141326

答えて

1

、あなたのIDbContextIRepositoryは同じです。 IDbContextを削除するか、少なくともIRepositoryで宣言されているメソッドを削除することをお勧めします。

その後、MyDbContextRepository両方がIRepositoryRepositoryクラスがちょうどMyDbContextラッパーだろう実装します。

RepositoryMyDbContextへの転送のみを転送している場合、おそらくそのクラスは必要ありません。

さらに、含まれているリポジトリへの呼び出しを転送する以外に、SoftwareSessionで何かをしていることはありません。 SoftwareSessionが本当に必要ですか、またはIRepositoryをセッションオブジェクトを呼び出している人に直接渡すのは意味がありますか?

最終的には、この実装は複製と転送で急増しています。これを削除すると、モデル全体がシンプルになります。

+0

ありがとうZoran。 IRepositoryは、複数のDbContextのために追加されます(たとえば、道路標識を見ているDrivingContextと、メンテナンスに関連する機能を実装するメンテナンスまたは管理コンテキストなどがあります)。その場合、リポジトリは私の実装で異なるコンテキストのアグリゲータとして機能します。私がソフトウェアセッションを使用しなかった場合、これらの関数が呼び出される他のすべてのクラスにリポジトリまたはDbContextsを注入する必要があります。これは追加のレプリケーションを引き起こさず、カプセル化も中断しませんか? – user3141326

0

あなたのコンポジションルートを見ることなく、私はあなたの実装がどのように動作するか完全にはわかりませんが、Inversion of Control(IoC)コンテナの使用を検討することをお勧めします。 ISoftwareSessionの実装はIRepositoryインスタンスにのみ依存しているため、そのインスタンスをクラスのコンストラクタに挿入する必要があります。 IRepositoryの実装にも同じことが言えます。IDbContextをコンストラクタに注入するだけです。

IoCコンテナを使用すると、アプリケーションの起動時(コンポジションルート内)にインターフェイスを実装にワイヤリングし、依存関係を解決するときにコンテナが必要なインスタンスを作成します。それでは、コンテナからSoftwareSessionのインスタンスを取得し、離れて行くだけです。

public class SoftwareSession : ISoftwareSession 
{ 
    IRepository repository; 

    public SoftwareSession(IRepository repository) 
    { 
     this.repository = repository; 
    } 

    public void WriteCar(Car car) 
    { 
     repository.WriteCar(car); 
    } 
} 

そして、このようなあなたのRepository実装:

だから、あなたはこのようなあなたのSoftwareSessionの実装を変更することができ

var ioc = new MyIocContainer(); 

// register your interfaces and their associated implementation types with the IoC container 
ioc.Register<ISoftwareSession, SoftwareSession>(); 
ioc.Register<IRepository, Repository>(); 
ioc.Register<IDbContext, MyDbContext>(); 

// resolve the IoC container 
ioc.Resolve(); 

// get your `ISoftwareSession` instance 
var session = ioc.GetConcrete<ISoftwareSession>(); 

var newCar = new Car(); 

session.WriteCar(newCar); 
+0

あなたの答えはRoryに感謝します。私はすでに問題のすべての項目に対してコンストラクタインジェクションを使ってNinjectを使用しています。この問題は、実際の注入ではなく、WriteCarインターフェイスと実装代理をこのアーキテクチャで関数ごとに6か所で使用することです。残りのすべてのメインラインクラスは、コンストラクタを介して注入されたSoftwareSessionのみを持ち、例えば、そのソフトウェアセッションでWriteCar関数を呼び出して呼び出します。しかし、私は実際の実装に落とすために署名を6回書いたり複製しなければなりません。 – user3141326

+0

「関数あたり6つの場所でWriteCarインターフェイスと実装の委任を使用する」という意味を理解できません。私は 'WriteCar'インターフェースを見ません。 「実装委任」とは何ですか?あなたが話している関数(メソッド?)の中の「6つの場所」はどこですか?あなたが提供したコードではわかりませんので、私は混乱しています... –

+0

このロリーを明確にしないと申し訳ありませんが、私は問題でも修正します。 WriteCarシグネチャは、3つのインターフェイス(IRepository、ISoftwareSession、IDbContext)、実装されていない2つの場所(Repository、SoftwareSession)で定義され、合成されたオブジェクト関連の関数と実際の実装(IDbContext) – user3141326