2

短い質問

抽象ファクトリインターフェイスと実際のファクトリはどこに配置すればよいですか?

概要

私は単純なビデオトランスコーディングアプリケーションを作成していて、依存性注入に頭を悩ませようとしています。

VisualStudioでアプリケーションをいくつかのプロジェクトに分割しました。

  • アプリケーションエンジンで使用されるトランスコーダ用の1つのクラスライブラリ
  • GUIまたはコンソールインターフェイスで使用されるアプリケーションエンジン用の1つのクラスライブラリ
  • 今のところメインのユーザーインターフェイスとなる1つのコンソールアプリケーション

DIなし

これは、依存性注入前のすべてがどのように見えるかです

トランスコーダーライブラリ:

namespace SimpleFFmpeg {
    public interface ITranscoder {
        void Transcode(String fileName);

    }

    public class Transcoder:ITranscoder {
        // ...

        public void Transcode(String fileName) {
            // do transcoding stuff
        }

        // ...
    }
}

PusherEngine lib:

using SimpleFFmpeg;
namespace PusherLib {
    public class PusherEngine {
        private readonly List<VideoItem> _items;

        public PusherEngine() {
            _items = new List<VideoItem>();
        }

        // ...

        public void processItems() {
            foreach (VideoItem item in _items) {
                ITranscoder t = new Transcoder();
                t.Transcode(item.FileName);
            }
        }

        // ...
    }
}

実際のアプリケーション:

namespace Pusher {
    class Program {
        static void Main(string[] args) {
            PusherEngine pe = new PusherEngine();
            pe.addVideoItem(new VideoItem(...));
            pe.processItems();
        }
    }
}

DIを使用するためのリファクタリング

この質問で提案されているように、汎用の抽象ファクトリインターフェイスを作成します:依存性注入を使用しながら新しいインスタンスを作成する

public interface IFactory<T> {
    T Get();
}

次に、ITranscoderを作成するファクトリを作成します

public class TranscoderFactory: IFactory<ITranscoder> {
    public ITranscoder Get() {
        return new SimpleFFmpeg.Transcoder();
    }
}

次に、PusherEngineを変更して、コンストラクターにファクトリ依存関係を要求します。

using SimpleFFmpeg;
namespace PusherLib {
    public class PusherEngine {
        private readonly IFactory<ITranscoder> _transcoderFactory;
        private readonly List<VideoItem> _items;

        public PusherEngine(IFactory<ITranscoder> transcoderFactory) {
            _items = new List<VideoItem>();
            _transcoderFactory = transcoderFactory;
        }

        // ...

        public void processItems() {
            foreach (VideoItem item in _items) {
                ITranscoder t = _transcoderFactory.Get();
                t.Transcode(item.FileName);
            }
        }

        // ...
    }
}

最後に、プログラムでは次のようになります。

namespace Pusher {
    class Program {
        static void Main(string[] args) {
            IFactory<ITranscoder> f = new TranscoderFactory();
            PusherEngine pe = new PusherEngine(f);
            pe.addVideoItem(new VideoItem(...));
            pe.processItems();
        }
    }
}

質問

IFactoryインターフェイスはどのlib/projectで定義する必要がありますか?TranscoderFactoryはどのlib/プロジェクトで定義する必要がありますか?

彼らはトランスコーダーライブラリに住んでいますか?PusherLibでは?または実際のフロントエンドアプリケーションで?ベストプラクティスを探しています。

ありがとう!

4

3 に答える 3

1

本当にファクトリが必要な場合(コメントを参照)、MarkSeemannによるこのブログ投稿でこの問題に対処しています。

簡単に言うと、ファクトリでIoCコンテナを使用する場合は、コンポジションルートで使用する必要があります。そうでない場合は、インスタンス化するクラスと同じアセンブリにとどまることに害はありません。

編集

特定のケースでは、この依存関係を解決するために必要なものがすべて揃っているため、ファクトリは必要ありません。

using SimpleFFmpeg;
namespace PusherLib {
    public class PusherEngine {
        private readonly ITranscoder _transcoder;
        private readonly List<VideoItem> _items;

        public PusherEngine(ITranscoder transcoder) {
            _items = new List<VideoItem>();
            _transcoder = transcoder;
        }

        // ...

        public void processItems() {
            foreach (VideoItem item in _items) {
                _transcoder.Transcode(item.FileName);
            }
        }

        // ...
    }
}

この場合、初期化は次のようになります。

namespace Pusher {
    class Program {
        static void Main(string[] args) {
            ITranscoder t = new Transcoder();
            PusherEngine pe = new PusherEngine(t);
            pe.addVideoItem(new VideoItem(...));
            pe.processItems();
        }
    }
}

リンクした回答でファクトリが必要だった理由は、依存関係ではインスタンス化できるように実行時にのみ認識される値が必要であり、依存関係では実行時依存の引数を作成する必要がないためです。

于 2012-04-26T09:53:28.313 に答える
1

私の意見では、それは問題ではありません。私にとって、依存性注入の主なポイントは、テスト中に実際の実装以外のものを注入できることです。単体テストは、テストに使用されるさまざまなモック定義とともに別のプロジェクトに保管しています。実際の実装と「抽象」ロジックはすべて、同じアセンブリ/プロジェクト/名前空間に保持されます。

于 2012-04-25T16:25:44.367 に答える
0

あなたの実際の質問に答えるために、そしてこれが工場の良いユースケースであるかどうかではありません:

この目的のために、私は時々別のプロジェクトに分割しInterfaceImplementationあなたのIFactory<>のようなものがCommon.Iプロジェクトに存在します。これはすべてのシナリオで機能するわけではありませんが、このアプローチの利点の1つは、Implementation基盤となるテクノロジの変更がある場合に、dllをモックまたは新しい実装と交換できることです。
たとえば、最近、ディレクトリからのxmlファイルの解析からサービスからのデータの取得に切り替えました。Implementationインターフェイスがまったく変更されなかったため、クライアントマシンで更新する必要があったのはこの1つのdllだけでした。

しかし、前に述べたように、最終的にはそれは実際には問題ではないと思います。

于 2012-04-26T12:15:27.980 に答える