C++ 策略技术中的算法策略
C++ 策略技术中的算法策略
策略模式是一种设计模式,允许在运行时选择算法的行为。在C++中,这可以通过模板和多态实现。策略类通常定义一个接口,各种不同的策略类实现这个接口,以提供具体的算法实现。
1. 常规范例:普通策略类
考虑一个基本任务:对数组元素执行某种操作,如求和、求乘积或找到最大值。首先,定义一个简单的求和函数,然后逐步引入策略模式以增加灵活性。
示例:基本求和函数
下面的 funcsum() 函数模板中,实现了对数组元素的求和运算。
#include <iostream>
// SumFixedTraits 类模板定义,用于不同类型的累加操作。
template<typename T>
struct SumFixedTraits;
// 特化对于 char 类型的 traits。
template<>
struct SumFixedTraits<char>
{
using sumT = int; // 为 char 类型定义累加结果为 int 类型
static sumT initValue() // 提供初始值
{
return 0;
}
};
// 特化对于 int 类型的 traits。
template<>
struct SumFixedTraits<int>
{
using sumT = __int64; // 使用更大的整数类型 __int64,以支持大数的运算
static sumT initValue() // 提供初始值
{
return 0;
}
};
// 特化对于 double 类型的 traits。
template<>
struct SumFixedTraits<double>
{
using sumT = double; // 使用 double 类型以保持浮点精度
static sumT initValue() // 提供初始值
{
return 0.0;
}
};
// funcsum 函数模板,使用 Trait 类模板 U 来确定累加和的类型和初始值
template<typename T, typename U = SumFixedTraits<T>>
auto funcsum(const T* begin, const T* end)
{
typename U::sumT sum = U::initValue(); // 使用 Trait 提供的初始值初始化 sum
for (; begin != end; ++begin) // 遍历给定范围
{
sum += *begin; // 累加每个元素
}
return sum;
}
int main()
{
int myintarray1[] = {10, 15, 20}; // 较小的整数数组
int myintarray2[] = {1000000000, 1500000000, 2000000000}; // 大整数数组
char mychararray[] = "abc"; // 字符串数组,将计算 ASCII 值的和
// 输出不同数组的累加结果
std::cout << "Sum of myintarray1: " << funcsum(&myintarray1[0], &myintarray1[3]) << std::endl;
std::cout << "Sum of myintarray2: " << funcsum(&myintarray2[0], &myintarray2[3]) << std::endl;
std::cout << "Sum of mychararray: " << static_cast<int>(funcsum(&mychararray[0], &mychararray[3])) << std::endl;
return 0;
}
引入策略类
上面的 funcsum() 函数模板中,已经将数组元素的求和算法固定写在了程序代码中,为了灵活地将求和算法调整为求乘积、求最大值等算法,可以通过引入一个策略(policy)类 SumPolicy 达到目的。
// 求和策略类以实现求和算法
struct SumPolicy
{
// 静态成员函数模板
template<typename sumT, typename T> // sumT 是和值类型,T 是数组元素类型
static void algorithm(sumT &sum, const T &value)
{
sum += value; // 求和
}
};
接着,为 funcsum() 函数模板增加一个新的类型模板参数,这个模板参数的默认值就是这个策略类。修改 funcsum() 函数模板:
template<typename T, typename U = SumFixedTraits<T>, typename V = SumPolicy>
auto funcsum(const T* begin, const T* end)
{
typename U::sumT sum = U::initValue(); // 使用 trait类 提供的初始值初始化 sum
while (begin != end)
{
V::algorithm(sum, *begin);
++begin;
}
return sum;
}
计算最小值
如果要计算一个整型数组中元素的最小值,如何实现?写一个新的策略类,如这里写一个 MinPolicy 类
template<typename T>
struct MinFixedTraits;
// 特化对于 int 类型的 traits。
template<>
struct MinFixedTraits<int>
{
using sumT = int;
static sumT initValue()
{
return std::numeric_limits<int>::max(); // 使用最大的 int 值作为初始值
}
};
// 计算一个整型数组中元素的最小值
struct MinPolicy
{
// 静态成员函数模板
template<typename minT, typename T> // sumT 是和值类型,T 是数组元素类型
static void algorithm(minT &min, const T &value)
{
if (min > value)
min = value;
}
};
int myintarray1[] = {10, 15, 20}; // 较小的整数数组
std::cout << "Min of myintarray1: " << funcsum<int, MinFixedTraits<int>, MinPolicy>(&myintarray1[0], &myintarray1[3]) << std::endl;
2. 常规范例:策略类模板
为了进一步提升策略模式的灵活性和通用性,可以将策略类转换成类模板。这种转换允许将算法相关的类型参数直接集成到策略类的模板参数中,进一步提高代码的可重用性和模块化。
策略类模板的定义
在前面的代码中,策略类 SumPolicy 和 MinPolicy 都是普通的类,其中包含的是一个静态成员函数模板 algorithm(),该函数模板包含两个类型模板参数。其实,也可以把 SumPolicy 和 MinPolicy 类写成类模板—直接把 algorithm() 中的两个类型模板参数搬到类定义上面作为类模板的模板参数就可以了。类模板 SumPolicy 和 MinPolicy 的实现代码如下。
// 求和策略类以实现求和算法
template<typename sumT, typename T> // sumT 是和值类型,T 是数组元素类型
struct SumPolicy
{
static void algorithm(sumT &sum, const T &value)
{
sum += value; // 求和
}
};
// 计算一个整型数组中元素的最小值
template<typename minT, typename T>
struct MinPolicy
{
static void algorithm(minT &min, const T &value)
{
if (min > value)
min = value;
}
};
修改 funcsum() 函数以适应策略类模板
当然,同样要修改 funcsum() 函数模板,该函数模板的第3个类型模板参数要作出改变,当前第3个类型模板参数的默认值是 SumPolicy,这是一个类型,但是,修改后的 SumPolicy 已经是一个类模板了,所以 uncsum() 函数模板的第3个模板参数必须是一个模板模板参数。修改后的 funcsum() 函数模板如下。
template<typename T, typename U = SumFixedTraits<T>, template<class,class> class V = SumPolicy>
auto funcsum(const T* begin, const T* end)
{
typename U::sumT sum = U::initValue(); // 使用 trait类 提供的初始值初始化 sum
while (begin != end)
{
// V::algorithm(sum, *begin);
V<typename U::sumT,T>::algorithm(sum, *begin);
++begin;
}
return sum;
}
3. 萃取技术与策略技术的比较
相似性
- 中介角色:萃取技术和策略技术都扮演中介角色,连接各种功能模块,增加系统的灵活性。通过抽象和封装,它们允许代码之间的交互更加灵活,实现解耦合。
- 模板使用:两者都广泛使用模板技术,以提供在编译时决定具体实现的能力。这使得代码更加通用和可配置。
差异性
-
关注点不同:
- 萃取技术:主要关注从类型到类型或值的转换,通过提供类模板的泛化和特化版本来定义和调整类型特性或属性。
- 策略技术:更侧重于行为和算法的封装,通过定义不同的策略类或策略类模板来实现具体的行为逻辑。
-
功能实现:
- 萃取技术中,通常一个类模板负责一种类型或值的转换,使用者通过特化这些模板以适应不同的数据类型或需求。
- 策略技术中,类或类模板包含成员函数,这些函数具体定义了如何执行一个操作或行为,允许在执行同一功能时有不同的行为表现。
-
实现形式:
- 萃取技术一般通过类模板实现,包括泛化和特化版本,这样可以根据不同类型提供不同的行为或属性。
- 策略技术可以使用普通类或类模板实现,策略类通常包含一或多个静态函数或成员函数,这些函数具体实施策略行为。
实际应用中的融合
虽然萃取技术和策略技术在概念上有明显区分,但在实际应用中,两者的界限有时可能模糊。例如,一个萃取类可能包含实现特定行为的方法,这在功能上类似于策略类。相反,策略类模板也可能用于调整或提供关于数据处理或行为决策的类型信息。