业务场景
用户需要设定规则,我们根据用户输入的数值判断是否符合规则。
示例一:
用户认为满足如下条件的属于合格品:
13.25<width<62.15 &&69.35> height>9.278 && area>5.86
(数字我随便敲的)
现在来了一批样品,得到了属性width等的数值。要判断样品是否合规
设计
我的思路:针对每个属性都定义一个函数,由用户设定其取值范围,输入数值判断是否合格。类似于
初始化一个函数: widthfun(double min, double max)
输入数值判断 是否 min<value<max
有多少个属性就需要多少函数
用一个工厂类创建需要的属性函数,存储到map里,等数值来了之后,由工厂函数判断是否合格。
接口类
IDefectFilterInterface
假设有三个属性,肯定不能直接写三个属性类。因为我希望用户输入数值,根据数值的属性,知道调用哪个属性函数。所以有一个指针p,能调用所有的属性函数。
那么就需要一个基类,判断数值的函数设置为虚函数。我在工厂类中能用基类的指针p,调用派生类的函数判断数值。
判断数值是否合格的函数定义为
virtual bool IsFilter(QVariant varValue) = 0;
由于该函数必须被子类实现,所以这里定义为纯虚函数(不实现这个类就没有意义)
接口 IDefectFilterInterface就定义好了
IDefectRangeFilterInterface
初始化属性类还需要用户输入取值范围:min<value<max
这里新建一个IDefectRangeFilterInterface 继承IDefectFilterInterface
在IDefectRangeFilterInterface 新加一个设置范围的纯虚函数
virtual void SetDefectFilterRange(QVariant minValue, QVariant maxValue) = 0;
因为不确定用户需要的数据类型是 double 还是int 还是其他,这里就使用qt自带的QVariant ,它可以转化为基础类型。
还需要两个成员变量存储最大最小值
QVariant m_minValue;
QVariant m_maxValue;
派生类
CDefectRangeFilter
继承IDefectRangeFilterInterface
因为使用QVariant 所以设置取值范围时,还不需要考虑数据类型的问题,直接赋值即可
void CDefectRangeFilter::SetDefectFilterRange(QVariant minValue, QVariant maxValue)
{
m_minValue = minValue;
m_maxValue = maxValue;
}
CDefectINTRangeFilter
最后因为我的属性目前都是 int型,所以定义一个 int型的属性类 CDefectINTRangeFilter
这里只需要继承CDefectRangeFilter,并实现
bool CDefectINTRangeFilter::IsFilter(QVariant varValue)
{
bool bIsFilter = true;
qint32 iValue = varValue.toInt();
qint32 iMinValue = m_minValue.toInt();
qint32 iMaxValue = m_maxValue.toInt();
if(iValue > iMinValue && iValue <iMaxValue )
{
return true;
}
else
return false;
}
工厂类
我们要怎么使用呢?
在类中定义一个map用来存放属性类
QMap<QString, IDefectFilterInterface*> m_mapFilter;
这里使用基类IDefectFilterInterface 指针,为了方便调用IsFilter函数
用户给每个属性类设置取值范围
//初始化map
void CDefectFilterFactory::init()
{
//1. 创建宽度类
IDefectFilterInterface* pClassWidth = new CDefectINTRangeFilter();
// 添加到Map
m_mapFilter.insert("width", pClassWidth);
//2. 创建高度类
IDefectFilterInterface* pClassHeight = new CDefectINTRangeFilter();
// 添加到Map
m_mapFilter.insert("height", pClassHeight);
//3. 创建面积类
IDefectFilterInterface* pClassArea = new CDefectINTRangeFilter();
// 添加到Map
m_mapFilter.insert("area", pClassArea);
}
void CDefectFilterFactory::setRange(QMap<QString,QPair<QVariant ,QVariant >> ranges)
{
....
pClass = m_mapFilter[type];
// 转换类指针
IDefectRangeFilterInterface* pDefectRangeFilterClass = dynamic_cast<IDefectRangeFilterInterface*>(pClass);
...
pDefectRangeFilterClass->SetDefectFilterRange(varMin, varMax);
}
这里虚函数的好处就出来了
我们可以把基类IDefectFilterInterface指针,用dynamic_cast转成IDefectRangeFilterInterface指针,调用虚函数SetDefectFilterRange就可以设置取值范围
查看是否合格同样的只需要传入 属性 "width"和数值 23.45
在m_mapFilter中找到对应的属性类即可
IDefectFilterInterface* pClass = m_mapFilter[type];
return pClass->IsFilter(varValue);
虚函数原理
C++虚函数的实现机制
C++多态的底层原理
(巨详细 + 图解) C++多态的机制原理
总结
我们用派生类CDefectINTRangeFilter new一个对象出来时 ,该指针的虚表是派生类的虚表
IDefectFilterInterface* pClassWidth = new CDefectINTRangeFilter();
当我们用 基类指针指向派生类时,虚表没有改变
// 转换类指针
IDefectRangeFilterInterface* pDefectRangeFilterClass = dynamic_cast<IDefectRangeFilterInterface*>(pClass);
我们再做一次类型转换时,虚表还是原来的样子,所以它可以调用子类的虚函数