公开 API 和处理 DLL 的依赖关系并遵守 SOLID 的正确方法是什么?

What's the proper way to expose the API and handle dependencies of DLL and adhere to SOLID?

公开我的可分发 DLL 的 API 并处理其依赖关系的最佳方法是什么,考虑到这些依赖关系不应由客户端处理,而应由 DLL 本身处理,同时仍符合 SOLID 和其他良好做法(可测试等)?

我能想到的能够做到这一点的唯一方法是通过静态工厂使用 Poor Man's Injection 公开无参数构造函数,如下所示:

public class MyService
{
    public MyService()
        : this(DependencyFactory.CreateObjectA(), DependencyFactory.CreateObjectB())
    {
    }

    internal MyService(IObjectA objectA, IObjectB objectB)
    {
    }
}

internal static class DependencyFactory
{
    internal static IObjectA CreateObjectA()
    {
        return new ObjectA();
    }

    internal static IObjectB CreateObjectB()
    {
        return new ObjectB();
    }
}

这条路正确吗?

我建议您为组件保留一个 public 构造函数,如下所示:

public class MyService
{
    public MyService(IObjectA objectA, IObjectB objectB)
    {
    }
}

然后创建一个默认工厂,您的图书馆的客户可以像这样方便地使用它:

public static class MyConvenientFactory
{
    public static MyService CreateDefaultMyService()
    {
        return new MyService(new ObjectA(), new ObjectB());
    }
}

客户会像这样创建您的组件:

var service = MyConvenientFactory.CreateDefaultMyService();

或者更高级的客户端会做这样的事情:

var service =
    new MyService(
        new CachingDecoratorForA(
            new ObjectA()),
        new LoggingDecoratorForB(
            new PerformanceRecorderForB(
                new ObjectB())));

其中 CachingDecoratorForALoggingDecoratorForBPerformanceRecorderForB 是由您作为库提供者或客户自己创建的装饰器。

这允许客户通过不同的组合来自定义您的组件。这是应用 SOLID 原则的好处之一。有关对象组合和 SOLID 的一些讨论,请参阅 this article

如果出于任何原因,您不希望这样的高级客户端通过组合来自定义您的组件,请将构造函数的访问修饰符更改为 internal,如下所示:

public class MyService
{
    internal MyService(IObjectA objectA, IObjectB objectB)
    {
    }
}

郑重声明,多亏了 Yacoub Massad 的出色回答和一篇关于 fluent builders 的文章,我能够流畅地使用构建器模式和装饰器模式实现构建器 API。使用方法如下:

var myService = MyServiceBuilder
    .Create()
    .WithCachingForA()
    .WithLoggingForB()
    .WithPerformanceRecorderForB()
    .Build();

实施:

public class MyServiceBuilder
{
    private IObjectA ObjectA;
    private IObjectB ObjectB;

    private MyServiceBuilder(IObjectA objectA, IObjectB objectB)
    {
        ObjectA = objectA;
        ObjectB = objectB;
    }

    public static MyServiceBuilder Create()
    {
        return new MyServiceBuilder(new ObjectA(), new ObjectB());
    }

    public MyServiceBuilder WithCachingForA()
    {
        ObjectA = new CachingDecoratorForA(ObjectA);
        return this;
    }

    public MyServiceBuilder WithLoggingForB()
    {
        ObjectB = new LoggingDecoratorForB(ObjectB);
        return this;
    }

    public MyServiceBuilder WithPerformanceRecorderForB()
    {
        ObjectB = new PerformanceRecorderForB(ObjectB);
        return this;
    }

    public MyService Build()
    {
        return new MyService(ObjectA, ObjectB);
    }
}