Dependency Injection: The Top Level
For many people, dependency injection implies the use of tools like Swing which allow configuration files to be used by a framework to sever the application code proper from direct dependency on concrete types.
Dependency injection is not restricted to that model. C++, like any language which supports polymorphism, can do dependency injection. (See Dependency Injection Principles, Practices, and Patterns, by Steven van Deursen and Mark Seemann, for extensive treatment of this general application of the term and for much useful coverage of details.)The top level of the simple breviary app is, more or less, what dependency injection looks like.
int main(int argc, const char **argv)
{
JSBUtil::CommandLineOptions options(argc, argv);
if (options.isSingleArg("?"))
{
printUsage(argv[0]);
return 0;
}
std::unique_ptr<BreviaryLib::IDateCalculator> dateCalculator
= BreviaryLib::DateCalculatorFactory(options).create();
std::unique_ptr<BreviaryLib::IConfigSource> config
= ConfigFileName(options).generateConfig();
Log::ConsoleLogger logger;
std::unique_ptr<BreviaryLib::IPsalter> psalter
= BreviaryLib::PsalterFactory(*config).create(logger);
BreviaryLib::Kalendar kalendar(
*dateCalculator,
BreviaryLib::SaintsDaySetFactory(*dateCalculator, *config, logger)
.create());
BreviaryLib::FileHymnSource hymnSource(*config, logger);
BreviaryLib::CommandLineArgs args(options);
auto [day, month, ymd] = args.getOfficeDateInfo();
std::unique_ptr<BreviaryLib::IDayRecordSource> records
= BreviaryLib::DayRecordSourceFactory(*config, *dateCalculator, day,
month)
.create(*psalter);
std::unique_ptr<BreviaryLib::IEncapsulatedOfficeFormatter> formatter
= BreviaryLib::OfficeFormatterFactory(*config, options).create();
std::string name = kalendar.getFeast(day, month).name;
if (name.empty())
name = dateCalculator->getFerialName(day, month).first;
formatter->formatDayTitle(name);
BreviaryLib::FullOfficeGenerator generator(
name, dateCalculator->getDay(day, month),
dateCalculator->getSeason(day, month), config->getUse(), *formatter,
*psalter, hymnSource, args.isPriest());
BreviaryLib::FullOfficeAssembler(*records, *dateCalculator, generator,
kalendar, *psalter, config->getUse())
.assembleOffice(ymd, logger);
return 0;
}
The top level's functionality is almost all factories. (The FileHymnSource is an exception, but actually hides a delegation mechanism which makes it, essentially, an interface wrapper around a changeable delegate. It's a different way of doing the same thing, but the idiom is not that of dependency injection.)
Resources get set up based on configuration and command line. The actual types selected by those options are not visible at the top level: they are buried in the factories.
Once all the resources are set up, the last two explicit lines do all the real work, having the resources passed into then to use.
Note that this is, essentially, a demo program. It is fairly light on error reporting controls: normal output goes to the console, and so do any errors which generate exceptions. In a more production-style case there would be more error handling, and though the console application is genuinely useful a more user-friendly one would use a GUI to capture options and to display the resulting text. But that just makes the role that simple setup plays more visible, because there's much less to distract attention from it.
Comments
Post a Comment