每当我想将某个逻辑的实现与需要它的位置分离开来,比如这种“收集多少数据才够”的知识,我就会想到一个回调函数。
假设你的类能够收集的所有可能数据都是已知的(例如在你的示例中,
name
、
age
、
sex
和
location
),那么所有客户端都可以(可能)知道它,而不增加耦合和依赖关系。
我的解决方案是创建一个“评估器”类来封装这个逻辑。客户端创建该类的子类实例,并在请求数据时将其传递给数据收集器;此对象负责决定(并告诉“收集器”),何时收集足够的数据。
#include <string>
class evaluator
{
public:
virtual ~evaluator() {};
virtual evaluator& name_collected() { return *this; }
virtual evaluator& age_collected() { return *this; }
virtual evaluator& sex_collected() { return *this; }
virtual evaluator& location_collected() { return *this; }
virtual bool enough() = 0;
};
class collector
{
public:
void collect_data( evaluator& e )
{
bool enough = false;
while ( !enough )
{
if ( )
{
name =
enough = e.name_collected().enough();
}
else if ( )
{
age =
enough = e.age_collected().enough();
}
}
}
std::string name;
int age;
char sex;
std::string location;
};
在您的示例中,您希望特定客户能够指定age
和sex
的组合是足够的。因此,您可以像这样子类化evaluator
:
class age_and_sex_required : public evaluator
{
public:
age_and_sex_required()
: got_age( false )
, got_sex( false )
{
}
virtual age_and_sex_required& age_collected() override
{
got_age = true;
return *this;
}
virtual age_and_sex_required& sex_collected() override
{
got_sex = true;
return *this;
}
virtual bool enough() override
{
return got_age && got_sex;
}
private:
bool got_age;
bool got_sex;
};
客户端在请求数据时传递此类的实例:
collector c;
c.collect_data( age_and_sex_required() );
collect_data
方法在
age_and_sex_required
实例报告数据收集量“足够”且您没有将任何逻辑、知识、枚举等构建到
collector
类中时退出并返回。此外,“足够”所包含的逻辑是无限可配置的,无需对
collector
类进行进一步更改。
----- 编辑 -----
另一个版本将不使用具有
..._collected()
方法的类,而仅使用接受
collector
作为参数并返回
boolean
的单个(typedef'd)函数:
#include <functional>
typedef std::function< bool( collector const& ) > evaluator_t;
collector::collect_data(...)
中的代码仅仅会调用
enough = e( *this )
每次收集数据时,这将消除对单独的“evaluator”抽象接口的必要性,但会增加对“collector”类本身的依赖,因为作为“evaluator_t”函数传递的对象将负责检查“collector”对象的状态以评估是否已收集足够的数据(并且需要“collector”具有足够的公共接口来查询其状态)。
bool age_and_sex_required( collector const& c )
{
// Assuming "age" and "sex" are initialized to -1 and 'X' to indicate "empty"
// (This could be improved by changing the members of "collector" to use
// boost::optional<int>, boost::optional<char>, etc.)
return (c.age >= 0) && (c.sex != 'X');
}