2

简短的问题

我应该把抽象工厂接口和实际工厂放在哪里?

概述

我正在编写一个简单的视频转码应用程序,我正试图围绕依赖注入来解决问题。

我已将我的应用程序分成 Visual Studio 中的几个项目。

  • 一个用于转码器的类库,由应用程序引擎使用
  • 一个用于应用程序引擎的类库,将由 gui 或控制台界面使用
  • 目前将作为主要用户界面的一个控制台应用程序

无 DI

这是依赖注入之前的一切

转码器库:

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

    }

    public class Transcoder:ITranscoder {
        // ...

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

        // ...
    }
}

PusherEngine 库:

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

我创建了一个通用抽象工厂接口,就像这个问题中建议的那样:Creating new instances while still using Dependency Injection

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

接下来我创建一个创建 ITranscoders 的工厂

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 接口应该在哪个库/项目中定义?TranscoderFactory 应该在哪个库/项目中定义?

他们住在转码器库中吗?在 PusherLib 中?还是在实际的前端应用程序中?我正在寻找最佳实践。

谢谢!

4

3 回答 3

1

在我看来,没关系。对我来说,依赖注入的要点是能够在测试时注入不同于实际实现的东西。我将单元测试与用于测试的各种模拟定义一起保存在一个单独的项目中。真正的实现以及“抽象”逻辑都保存在同一个程序集/项目/命名空间中。

于 2012-04-25T16:25:44.367 回答
1

如果你真的需要工厂(见评论),那么Mark Seemann 的这篇博客文章解决了这个问题。

简而言之,如果您在工厂中使用 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 回答
0

要回答您的实际问题,而不是这是否是工厂的好用例:

为了这个目的,我有时会分成不同InterfaceImplementation项目,像你的 IFactory<> 这样的东西会存在于 Common.I 项目中。这并非在所有情况下都有效,但这种方法对我来说的一个优点是,Implementation当底层技术发生变化时,我可以用模拟或新实现替换 dll。
例如,我们最近从解析目录中的 xml 文件切换到从服务中获取数据。我唯一需要在客户端机器上更新的是这个Implementationdll,因为界面根本没有改变。

但我想最后这并不重要,如前所述。

于 2012-04-26T12:15:27.980 回答