【问题标题】:In which library should an abstract factory interface and an actual factory be defined?应该在哪个库中定义抽象工厂接口和实际工厂?
【发布时间】:2012-04-25 15:56:34
【问题描述】:

小问题

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

概述

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

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

它们是否存在于 Transcoder 库中?在 PusherLib 中?还是在实际的前端应用程序中? 我正在寻找最佳做法。

谢谢!

【问题讨论】:

  • +1 表示结构良好且书面问题...
  • 也许我没抓住重点,或者这个例子被过度简化了,但是如果你不需要在运行时决定实例化什么,为什么还需要工厂呢?你可以只注入 ITranscoder。
  • @filpen 引擎需要为每个项目实例化一个新的 ITranscoder。如果有办法在没有工厂的情况下做到这一点,那么在这种特殊情况下这很可能是一个解决方案。
  • @luddet 好的,这几乎清除了它。我编辑了我的答案,希望能让你走上正轨。

标签: c# .net dependency-injection instantiation


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    如果您真的需要工厂(见评论),那么 Mark Seemann 的 this blog post 解决了这个问题。

    简而言之,如果您在工厂中使用 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();
            }
        }
    }
    

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

    【讨论】:

    • 我需要创建转码器的新实例,因为我计划使处理成为多线程。 (一系列全新的头痛:))所以我相信工厂是要走的路。无论如何,这是寻找的原则。您提供给 Mark Seemanns 博客的链接很有帮助!谢谢!
    • 我没有足够的“声誉”来将您的答案标记为有用,否则我会的。
    【解决方案3】:

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

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

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

    【讨论】:

      猜你喜欢
      • 2011-01-05
      • 1970-01-01
      • 2018-08-12
      • 2020-02-20
      • 1970-01-01
      • 1970-01-01
      • 2014-01-14
      • 1970-01-01
      • 2012-04-08
      相关资源
      最近更新 更多