具有可变数据的 C++ 策略设计
C++ policy design with variable data
关于这个主题的信息很多。这更像是一个设计问题,但我会举例说明。
假设我真的想传递一个配置文件 class,它决定了用户的策略。
struct ApplicationAllowedPolicy
{
public:
bool hasAccess() { return true; }
}
struct ApplicationProhibitedPolicy
{
public:
bool hasAccess() { return false; }
}
template<typename ApplicationPolicy>
class Profile : private ApplicationPolicy
{
bool hasAccess() { return ApplicationPolicy::access(); }
}
int main()
{
Profile<ApplicationAllowedPolicy> allowed;
Profile<ApplicationProhibitedPolicy> prohibited;
// do something with allowed & prohibited
}
以上一切都很好,但我们假设有很多政策需要阅读。5 似乎是一个现实世界的数字,尽管它可能更多。然后,假设此配置文件将应用于数百个实例,其中 5 个策略差异很大。要启动,策略行为只会在 运行 时间已知(从文件、数据库等读取)。这很快就会变得不可扩展,除非我完全遗漏了什么。
我想做一个非类型模板 class 作为一项政策。
template<int N>
struct ApplicationPolicy
{
int policy = N;
};
Profile<ApplicationPolicy<1>> allowed;
Profile<ApplicationPolicy<0>> prohibited;
我认为这确实适用于我的情况,但我想知道这是否缺少基于策略的设计要点。我在看到这个的优点时遇到了问题,只是让 Profile 成为一个普通的结构,并根据需要将它的数据成员设置为 true/false。
想法?
当 class 的行为根据策略发生显着变化时,基于策略的设计非常有用。例如,假设您希望 class 在多线程和常规上下文中使用。然后你可以将其实现为线程策略:
class SingleThreadedPolicy { /* ... */ }; // lock() does nothing
class MultiThreadedPolicy { /* ... */ }; // lock() actually locks
template<class T, class ThreadingPolicy>
class Queue {
ThreadingPolicy threadPol_;
// ...
T pop() {
threadPol_.lock();
// remove an element from the queue
threadPol_.unlock();
return element;
}
};
现在,另外两种实现相同结果的方法是使用(多重?)继承或在对象中设置标志并编写大量 ifs。如果您有多个策略(例如存储、所有权等),第一个选项很快就会失效。第二个选项会导致无法维护的代码。
因此,如果您的 class 需要许多正交移动部分,基于策略的设计是一个不错的选择,而且它比其他方法更具可扩展性。
但是,您的示例似乎并不真的需要这种方法。 Access 似乎是它不可或缺的一部分:如果你打算在你的代码中调用 hasAccess()
,你可以安全地用一个布尔变量替换它。
如果您想在运行时改变行为,请使用策略模式而不是基于策略的设计。
关于这个主题的信息很多。这更像是一个设计问题,但我会举例说明。
假设我真的想传递一个配置文件 class,它决定了用户的策略。
struct ApplicationAllowedPolicy
{
public:
bool hasAccess() { return true; }
}
struct ApplicationProhibitedPolicy
{
public:
bool hasAccess() { return false; }
}
template<typename ApplicationPolicy>
class Profile : private ApplicationPolicy
{
bool hasAccess() { return ApplicationPolicy::access(); }
}
int main()
{
Profile<ApplicationAllowedPolicy> allowed;
Profile<ApplicationProhibitedPolicy> prohibited;
// do something with allowed & prohibited
}
以上一切都很好,但我们假设有很多政策需要阅读。5 似乎是一个现实世界的数字,尽管它可能更多。然后,假设此配置文件将应用于数百个实例,其中 5 个策略差异很大。要启动,策略行为只会在 运行 时间已知(从文件、数据库等读取)。这很快就会变得不可扩展,除非我完全遗漏了什么。
我想做一个非类型模板 class 作为一项政策。
template<int N>
struct ApplicationPolicy
{
int policy = N;
};
Profile<ApplicationPolicy<1>> allowed;
Profile<ApplicationPolicy<0>> prohibited;
我认为这确实适用于我的情况,但我想知道这是否缺少基于策略的设计要点。我在看到这个的优点时遇到了问题,只是让 Profile 成为一个普通的结构,并根据需要将它的数据成员设置为 true/false。
想法?
当 class 的行为根据策略发生显着变化时,基于策略的设计非常有用。例如,假设您希望 class 在多线程和常规上下文中使用。然后你可以将其实现为线程策略:
class SingleThreadedPolicy { /* ... */ }; // lock() does nothing
class MultiThreadedPolicy { /* ... */ }; // lock() actually locks
template<class T, class ThreadingPolicy>
class Queue {
ThreadingPolicy threadPol_;
// ...
T pop() {
threadPol_.lock();
// remove an element from the queue
threadPol_.unlock();
return element;
}
};
现在,另外两种实现相同结果的方法是使用(多重?)继承或在对象中设置标志并编写大量 ifs。如果您有多个策略(例如存储、所有权等),第一个选项很快就会失效。第二个选项会导致无法维护的代码。
因此,如果您的 class 需要许多正交移动部分,基于策略的设计是一个不错的选择,而且它比其他方法更具可扩展性。
但是,您的示例似乎并不真的需要这种方法。 Access 似乎是它不可或缺的一部分:如果你打算在你的代码中调用 hasAccess()
,你可以安全地用一个布尔变量替换它。
如果您想在运行时改变行为,请使用策略模式而不是基于策略的设计。