C++ 中的服务定位器模式实现






4.20/5 (3投票s)
服务定位器可以用来解耦类,从而改善整体设计,并显著帮助单元测试。
引言
本文假定读者了解服务定位器模式。本文使用来自 Martin Fowler 的 控制反转容器和依赖注入模式的术语和代码示例。另请参阅 CodeProject 文章 服务定位器模式的基本介绍。
背景
典型的例子是 C# 和 Java。 本文提供了 C++ 中一个简单但完整的服务定位器框架。
服务定位器背后的基本思想是能够使用存储库注册一个或多个类,然后要求存储库使用 ID 而不是实际的类名来创建这些类的实例。通过这样做,它使客户端代码与类的实际实现解耦,允许客户端注册相同接口的不同实现,从而能够在不更改使用该类的代码的情况下交换不同的功能集。这也有助于单元测试,因为对通常会被硬编码的类的引用不再需要了。
最后,这也减少了构建依赖关系。
用法预览
在本文中,使用了 Martin Fowler 的示例类:MovieFinder
是一个接口(抽象纯虚类),而ColonDelimitedMovieFinder
是一个实现MovieFinder
的类。
服务定位器代码将允许我们使用字符串 ID(例如“finder”)注册ColonDelimitedMovieFinder
,然后通过仅使用字符串名称“finder”创建ColonDelimitedMovieFinder
类的实例,将引用分配给一个MovieFinder
指针。
注册类的示例,将其存储在字符串 ID "finder" 下
locator.register_class<ColonDelimitedMovieFinder>( "finder" );
创建此类的实例的示例
MovieFinder* finder = locator.get_single_instance<MovieFinder>( "finder" );
Using the Code
(你只需要一个头文件,servicelocator.h。使用命名空间sl
。)
从interface_t
派生你的接口(纯虚抽象类)。例子
struct MovieFinder : sl::interface_t { virtual vector<Movie> findAll() = 0; };
从member_t
派生你的模板类,将类名作为模板参数传递。例子
class ColonDelimitedMovieFinder : public sl::member_t<ColonDelimitedMovieFinder> { ...
创建一个servicelocator_t
的实例。例子
sl::servicelocator_t locator;
使用register_class
方法注册类。要注册的类是模板参数。 函数参数是表示类的字符串 ID。例子
locator.register_class<ColonDelimitedMovieFinder>( "finder" );
现在,可以使用ColonDelimitedMovieFinder
的实例创建上面的字符串 ID(例如“finder”),而无需按名称引用ColonDelimitedMovieFinder
。假设ColonDelimitedMovieFinder
被重新定义为实现MovieFinder
接口,如下所示
class ColonDelimitedMovieFinder : public sl::member_t<ColonDelimitedMovieFinder>, public MovieFinder
然后,有两种创建ColonDelimitedMovieFinder
实例的方法。一种作为单例(以便对创建的所有后续调用都返回相同的实例),另一种作为瞬时实例,每次调用都会创建一个新实例。
创建单例实例的调用是get_single_instance
,它看起来像这样
MovieFinder* finder = locator.get_single_instance<MovieFinder>( "finder" );
(再次注意,没有引用类ColonDelimitedMovieFinder
,但这就是创建的内容,因为字符串 ID "finder" 之前已使用ColonDelimitedMovieFinder
注册了。)请注意,从get_single_instance
返回的对象不需要释放。 仅创建一个实例,并且由服务定位器拥有。当locator
对象超出范围或被释放时,所有单例都会被释放。
创建实例的第二种方法是通过get_new_instance
,它看起来像这样
auto_ptr<MovieFinder> finder = locator.get_new_instance<MovieFinder>( "finder" );
为了方便起见,get_new_instance
返回一个auto_ptr
,它比原始指针更安全。但是,如果客户端不需要auto_ptr
对象,则只需在auto_ptr
上调用release()
并获取原始对象,然后客户端可以根据需要进行管理。但在这种情况下,客户端有责任最终在原始对象上调用delete
。