什么是正确的方式来暴露API和处理DLL的依赖关系,并坚持SOLID

本文关键字:关系 依赖 DLL 处理 坚持 SOLID API 方式 什么 暴露 | 更新日期: 2023-09-27 18:06:21

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

我能想到的唯一方法是通过使用带有静态工厂的穷人注入暴露无参数构造函数,如下所示:

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

这条路对吗?

什么是正确的方式来暴露API和处理DLL的依赖关系,并坚持SOLID

我建议您为您的组件保留一个像这样的公共构造函数:

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

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

允许客户端通过不同的组合来定制你的组件。这是应用SOLID原则的好处之一。有关对象组合和SOLID的一些讨论,请参阅本文。

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

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

声明一下,多亏了Yacoub Massad出色的回答和一篇关于流畅构建器的文章,我才能够使用builder模式和Decorator模式实现一个带有流畅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);
    }
}