阶梯模式实现

Stairway pattern implementation

我在 "Adaptive code via C#" 书中看到了 "Stairway" 模式描述,但我真的不明白这应该如何实现:

(source)

所以我有客户端组件:

using ServiceInterface;

namespace Client
{
    class Program
    {
        static void Main(string[] args)
        {
            // Have to create service implementation somehow
            // Where does ServiceFactory belong?
            ServiceFactory serviceFactory = new ServiceFactory();
            IService service = serviceFactory.CreateService();
            service.Do();
        }
    }
}

服务接口组件:

namespace Service
{
    public interface IService
    {
        void Do();
    }
}

和服务实现组件:

using ServiceInterface;

namespace ServiceImplementation
{
    public class PrintService : IService
    {
        public void Do()
        {
            Console.WriteLine("Some work done");
        }
    }
}

问题是:如何在 Client 命名空间中获取 IService 对象?我应该在哪里放置实际的 new PrintService() 对象创建?这不能是 ServiceInterface 的一部分,因为接口组件不依赖于 ServiceImplementation。但它也不能是 ClientServiceImplementation 的一部分,因为 Client 应该只依赖于 ServiceInterface

我找到的唯一解决方案是在它上面安装 Application 程序集,它引用了所有三个(ClientServiceInterfaceServiceImplementation)和将 IService 注入 Client。我错过了什么吗?

我会把它放在 ServiceFactory 中。您需要一些参数,例如传入工厂构造函数或从配置等中检索,确定工厂创建哪个 IService 实现。

在那种情况下,Client 项目应该包含对 ServiceServiceImplementation 的引用。这些引用将仅用于创建将用作 DI 的 IoC 容器。在应用程序启动时,您需要在 IoC 容器中注册所有接口实现。

如果您将针对 Service 接口实现 ServiceImplementation,并且您将根据 Service 接口编写 Client,那么将不会依赖于 ServiceImplementation .

您还可以在 "Adaptive Code via C#":

的样本中看到阶梯模式是如何实现的

https://github.com/garymcleanhall/AdaptiveCode/tree/master/Sprints/sample-sprint2-markdown

根据 Mark Seemann 关于依赖注入的优秀书籍,应用程序入口点应该是组合根。在这里,问题更多地与依赖倒置有关,即客户端和实现都应该依赖于抽象。

该图没有显示,但希望本书的其他部分能说明的是,入口点自然且必然引用构建任何内容所需的一切是您的解析根(控制器、服务等),但这是 唯一 拥有此类知识的地方。

请记住,有时客户端 'own' 它们所依赖的接口是可以的:接口 ISecurityService 可能存在于 Controllers 程序集中,IUserRepository 可能存在于 ServiceImplementations 程序集中,等等。当然,当 >1 个客户端需要访问接口时,这是不切实际的。

如果你关注 SOLID,你自然会发现依赖注入是必要的,但控制反转容器的优先级较低。我发现自己越来越频繁地使用纯依赖注入(手动构建解析根)。