应在哪个库中定义抽象工厂接口和实际工厂

本文关键字:工厂 接口 抽象 定义 | 更新日期: 2023-09-27 18:32:25

简短的问题

我应该把抽象的工厂界面和实际的工厂放在哪里?

概述

我正在编写一个简单的视频转码应用程序,并试图围绕依赖注入进行思考。

我已经在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 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();
}

接下来,我创建一个创建 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 接口?应该在哪个库/项目中定义转码器工厂?

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

谢谢!

应在哪个库中定义抽象工厂接口和实际工厂

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

如果你真的需要一个工厂(见评论),那么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();
        }
    }
}

您链接的答案中需要工厂的原因是,依赖项需要的值仅在运行时已知才能实例化,而依赖项不需要创建运行时相关参数。

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

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

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