モッキングにより、運用コードでの処理が導入されます


15

IReaderインターフェイス、IReaderインターフェイスReaderImplementationの実装、およびリーダーからのデータを消費して処理するクラスReaderConsumerを想定しています。

public interface IReader
{
     object Read()
}

実装

public class ReaderImplementation
{
    ...
    public object Read()
    {
        ...
    }
}

消費者:

public class ReaderConsumer()
{
    public string location

    // constructor
    public ReaderConsumer()
    {
        ...
    }

    // read some data
    public object ReadData()
    {
        IReader reader = new ReaderImplementation(this.location)
        data = reader.Read()
        ...
        return processedData    
    }
}

ReaderConsumerと処理のテストには、IReaderのモックを使用します。ReaderConsumerは次のようになります。

public class ReaderConsumer()
{
    private IReader reader = null

    public string location

    // constructor
    public ReaderConsumer()
    {
        ...
    }

    // mock constructor
    public ReaderConsumer(IReader reader)
    {
        this.reader = reader
    }

    // read some data
    public object ReadData()
    {
        try
        {
            if(this.reader == null)
            {
                 this.reader = new ReaderImplementation(this.location)
            }

            data = reader.Read()
            ...
            return processedData    
        }
        finally
        {
            this.reader = null
        }
    }
}

このソリューションでは、モック作成コンストラクターのみがインターフェイスのインスタンスを提供するため、モック作成には実動コードのif文が導入されます。

これを書いている間、try-finallyブロックは、アプリケーションの実行中に場所を変更するユーザーを処理するために存在するため、やや無関係であることがわかりました。

全体的に臭いが感じられますが、どうすればより良く処理できますか?


16
通常、これは問題ではありません。なぜなら、依存関係が注入されたコンストラクターが唯一のコンストラクターになるからです。ReaderConsumer自立することは問題外ReaderImplementationですか?
クリスウォーラート

現在、依存関係を削除するのは難しいでしょう。それをもう少し見ると、ReaderImplemenatationへの依存だけでなく、より深い問題を抱えています。ReaderConsumerは工場からの起動中に作成されるため、アプリケーションの存続期間中持続し、ユーザーからの変更を受け入れます。これには、追加のマッサージが必要です。おそらく、構成/ユーザー入力がオブジェクトとして存在し、代わりにReaderConsumerとReaderImplementationがその場で作成される可能性があります。与えられた両方の答えは、より一般的なケースをかなりよく解決します。
クリスチャンMO

3
はい。これは、ある時点 TDDの:書き込みテストを有する最初の意味よりデカップリングのデザインを(そうでなければ、書き込むことはできませんユニットテストを...)。これにより、コードの保守性と拡張性が向上します。
バクリウ

依存性注入で解決できる臭いを検出する良い方法は、キーワード「new」を探すことです。依存関係を更新しないでください。代わりにそれらを注入してください。
Eternal21

回答:


67

メソッドからリーダーを初期化する代わりに、この行を移動します

{
    this.reader = new ReaderImplementation(this.location)
}

デフォルトのパラメーターなしのコンストラクターに。

public ReaderConsumer()
{
    this.reader = new ReaderImplementation(this.location)
}

public ReaderConsumer(IReader reader)
{
    this.reader = reader
}

「モックコンストラクター」のようなものはありません。クラスが動作するために必要な依存関係を持っている場合、コンストラクターはそのモノを提供するか、作成する必要があります。


3
score ++ ... DIの観点を除き、デフォルトのコンストラクタは間違いなくコードの匂いです。
マチューギンドン

3
@ Mat'sMugはデフォルトの実装では何も問題はありません。俳優連鎖の欠如は、ここでのにおいです。=;)-–
ラバーダック

ああ、そうです -もしあなたが本を持っていなければ、この記事ろくでなしのアンチパターンをかなりよく説明しているように見えます(しかし、ざっと目を通しました)。
マチューギンドン

3
@ Mat'sMug:DIを独断的に解釈しています。デフォルトのコンストラクタを注入する必要がない場合は、必要ありません。
ロバートハーヴェイ

3
リンクされた本@ Mat'sMugは、依存関係が「許容可能なデフォルト」であることについても述べています(ただし、プロパティのインジェクションを参照しています)。このようにしましょう。2人構成のアプローチは、1人構成のアプローチよりもシンプルさと保守性の点でコストかかり、明確にするために質問を単純化しすぎているため、コストは正当化されませんが、場合によっては。
カールレス

54

必要なコンストラクタは1つだけです。

public class ReaderConsumer()
{
    private IReader reader = null

    public string location

    // constructor
    public ReaderConsumer(IReader reader)
    {
        this.reader = reader;
    }

本番コードで:

var rc = new ReaderConsumer(new ReaderImplementation(0));

あなたのテストで:

var rc = new ReaderConsumer(new MockImplementation(0));

13

依存性注入と制御の反転を調べる

ユアンとラバーダックの両方に優れた答えがあります。しかし、依存性注入(DI)と制御の反転(IoC)を調べる別の領域に言及したかったです。これらのアプローチは両方とも、あなたが経験している問題をフレームワーク/ライブラリに移動するので、心配する必要はありません。

あなたの例は単純で、すぐに省かれますが、必然的にあなたはそれに基づいて構築するつもりであり、あなたは次のようなたくさんのコンストラクタまたは初期化ルーチンのいずれかになるでしょう:

var foo = new Foo(new Bar(new Baz()、new Quz())、new Foo2());

DI / IoCでは、インターフェイスを実装に一致させるための規則を綴るライブラリを使用し、「Goo me a Foo」と言うだけで、すべてを接続する方法がわかります。

非常に友好的なIoCコンテナ(と呼ばれる)がたくさんありますので、検討することをお勧めしますが、すばらしい選択肢がたくさんあるので、探ってください。

最初に簡単なものは次のとおりです。

http://www.ninject.org/

探索するリストは次のとおりです。

http://www.hanselman.com/blog/ListOfNETDependencyInjectionContainersIOC.aspx


7
EwanとRubberDuckの両方の答え DIを示しています。DI / IoCはツールやフレームワークではなく、制御が反転し依存関係が注入されるような方法でコードを設計および構造化することです。Mark Seemannの本は、IoCフレームワークなしでDI / IoCを完全に達成する方法、およびIoCフレームワークが良いアイデアになる理由タイミング(ヒント:。 。)=)
マチューギンドン

また... Ninjectが素晴らしいので+1。最初の段落は、EwanとRubberDuckの答えがDIについてではないように少し読みますが、これが私の最初のコメントを促しました。
マチューギンドン

1
@ Mat'sMug間違いなくポイントを獲得してください。私は、OPが他のポスターが実際に使用しているDI / IoCを調べるよう奨励しようとしていましたが(EwanはPoor ManのDIです)、言及しませんでした。もちろん、フレームワークを使用することの利点は、DIの世界にユーザーを没入させる具体的な例がたくさんあることです。:-)そして、もちろん、私はマーク・シーマンの本が大好きでした。私のお気に入りの一つです。
レジナルドブルー

DIフレームワークについてのヒントをありがとう、それはこの混乱を適切にリファクタリングする方法のようです:)
クリスチャンmo
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.