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

时间:2012-04-25 15:56:34

标签: c# .net dependency-injection instantiation

简短的问题

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

概述

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

我已将我的应用程序分离到visual studio中的几个项目中。

  • 应用程序引擎使用的代码转换器的一个类库
  • 将由gui或控制台界面使用的应用程序引擎的一个类库
  • 一个控制台应用程序,它将成为现在的主要用户界面

没有DI

这是依赖注入之前的所有内容

代码转换器lib:

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

我创建了一个通用的抽象工厂界面,就像这个问题中提到的那样: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();
        }
    }
}

问题

应该在哪个lib / project中定义IFactory接口? 在哪个lib /项目中应该定义TranscoderFactory?

他们是否住在Transcoder lib中?在PusherLib中?或者在实际的前端应用中? 我正在寻找最佳实践。

谢谢!

3 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分:1)

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

简而言之,如果您在工厂中使用IoC Container,则需要在合成根目录中使用它。如果没有,那么它与实例化的类保持在同一个程序集中是没有害处的。

修改

对于您的特定情况,您不需要工厂,因为您已经拥有解决此依赖关系所需的一切。

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();
        }
    }
}

您链接的答案中需要工厂的原因是,只有在运行时才知道依赖关系所需的值才能实例化,而您的依赖关系不需要创建与运行时相关的参数。 / p>

答案 2 :(得分:0)

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

为此,我有时会将InterfaceImplementation分成不同的项目,例如您的IFactory&lt;&gt;将住在Common.I项目中。 这在每种情况下都不起作用,但这种方法对我来说的一个优点是,当有潜在的技术变化时,我可以用模拟或新实现替换Implementation dll。
例如,我们最近从解析目录中的xml文件到从服务获取数据。我在客户端计算机上唯一需要更新的就是这个Implementation dll,因为界面根本没有变化。

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