我正在尝试将一些旧的C ++代码转换为更可测试的形式。为了符合依赖性倒置原则(DIP),我将在很多情况下使用依赖注入。
我的问题是如何最好地实例化具体类。在这段代码中有类似我自己的100个类。在他的书“清洁建筑”中,罗伯特·马丁认为这些课程是不稳定的,应该在工厂实例化。但是(参见本书第90页)这将需要我想要实例化的每个类4个类。这意味着400个班级。
让我们假设为了说明旧代码有一个A类(实例化并使用A1到A5类),B(B1到B10)和C(C1到C3类)。
在新代码中,您建议如何以及在何处实例化所有具体类?对于在大型C ++程序中处理过这类问题的人,我特别感兴趣。感谢。
布鲁斯
答案 0 :(得分:1)
当应用依赖性倒置原则时,我们将协作组件(a.k.a.对象图)的图形构建延迟到最后一个负责时刻。由于我们希望最小化组件甚至单个库之间的耦合,我们将这些对象图的构造移动到系统中最不稳定的部分。这是启动装配。启动程序集中负责编写和构造这些对象图的部分通常称为Composition Root。
在新代码中,您建议如何以及在何处实例化所有具体类?
您应该在组合根中构建所有具体的组件(包含应用程序的有趣行为的类)。
在某些托管环境中,如Java和.NET,使我们能够在运行时查询应用程序的元数据(也称为Reflection)的API的可用性允许在编译时声明抽象和实现之间的关系,以及在运行时使用可重用库动态构建的组件,通常称为 DI容器。
C ++可能没有这种在运行时查询元数据的能力。在这样的环境中,您使用Pure DI,这只是意味着:您在撰写根中手动const lambdaLocal = require('lambda-local');
const AWS = require('aws-sdk');
const options = {
region: 'localhost',
endpoint: 'http://localhost:8000'
}
AWS.config.update(options);
const lambda = require('./index');
describe('Item', () => {
it('returns a list of items', (done) => {
lambdaLocal.execute({
event: {},
lambdaFunc: lambda,
lambdaHandler: 'handle',
profilePath: '~/.aws/credentials',
profileName: 'default',
timeoutMs: 3000,
callback: function(error, data) {
expect(error).toBeFalsy();
expect(data).toBeTruthy();
expect(data.Count).toEqual(5);
expect(data.Items[0].name).toEqual('Item name');
// ...
done();
}
});
});
});
您的课程。
请注意,在我的描述中没有使用“" factory"”这个词。指定返回其他应用程序抽象的工厂抽象的需要不应该是常态,而是the exception。您可能会将Composition Root视为一个大工厂,但应用程序中的任何内容都不依赖于Composition Root。另一方面,组合根取决于一切。
因此,您的应用程序类几乎不需要依赖工厂抽象。如果一个类需要不同的服务,它应该直接将它注入到它的构造函数中,而不是注入一个可以解析该服务的工厂。