公开 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())));
其中 CachingDecoratorForA
、LoggingDecoratorForB
和 PerformanceRecorderForB
是由您作为库提供者或客户自己创建的装饰器。
这允许客户通过不同的组合来自定义您的组件。这是应用 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);
}
}
公开我的可分发 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())));
其中 CachingDecoratorForA
、LoggingDecoratorForB
和 PerformanceRecorderForB
是由您作为库提供者或客户自己创建的装饰器。
这允许客户通过不同的组合来自定义您的组件。这是应用 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);
}
}