31.1 规格模式(Specification Pattern)
31.1.1 查询筛选条件
(1)场景:根据条件从数据库的用户表中筛选出对象
(2)存在问题
①findUserByAgeThan和findUserByName两个方法中除了if后面的判断条件不同,其他地方完全一样
②这两个程序中唯一的变化点就是条件语句,可以将这里封装起来,后面的例子会设计一个规格类,用于封装整个条件语句。
【编程实验】根据条件筛选用户表数据
//新设计模式——规格模式
//实例:实现不同条件的查询(不使用规格模式)
//存在问题:
//1. findUserByAgeThan和findUserByName两个方法中除了if后面的判断条件
//不同,其他地方完全一样
//2. 这两个程序中唯一的变化点就是条件语句,可以将这里封装起来,后面的
// 例子会设计一个规格类,用于封装整个条件语句
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
using namespace std;
//用户类
class User
{
private:
string name; //姓名
int age; //年龄
public:
User(string name, int age)
{
this->name = name;
this->age = age;
}
string getName(){return name;}
void setName(string value){name = value;}
int getAge(){return age;}
void setAge(int value){age = value;}
string toString()
{
ostringstream oss;
oss << age;
string ret = "用户名:" + name + "\t年龄:" + oss.str();
return ret;
}
};
//用户操作对象接口
class IUserProvider
{
public:
virtual vector<User*>* findUserByName(string name) = 0;
virtual vector<User*>* findUserByAgeThan(int age) = 0;
};
//用户操作类
class UserProvider : public IUserProvider
{
private:
vector<User*>* userList;
public:
UserProvider(vector<User*>* userList)
{
this->userList = userList;
}
//年龄大于指定年龄
vector<User*>* findUserByAgeThan(int age)
{
vector<User*>* ul = new vector<User*>();
vector<User*>::iterator iter = userList->begin();
for(;iter != userList->end(); ++iter)
{
if((*iter)->getAge() > age) //与findUserByName唯一的不同
{
ul->push_back((*iter));
}
}
return ul;
}
//姓名等于指定姓名的用户
vector<User*>* findUserByName(string name)
{
vector<User*>* ul = new vector<User*>();
vector<User*>::iterator iter = userList->begin();
for(;iter != userList->end(); ++iter)
{
if((*iter)->getName() == name) //与findUserByAgeThan唯一的不同
{
ul->push_back((*iter));
}
}
return ul;
}
};
void createUsers(vector<User*>& userList)
{
userList.push_back( new User("苏三",3) );
userList.push_back( new User("牛二",8) );
userList.push_back( new User("张三",10) );
userList.push_back( new User("李四",15) );
userList.push_back( new User("王五",18) );
userList.push_back( new User("赵六",20) );
userList.push_back( new User("马七",25) );
userList.push_back( new User("杨八",30) );
userList.push_back( new User("侯九",35) );
userList.push_back( new User("布十",40) );
}
void delUsers(vector<User*>& userList)
{
vector<User*>::iterator iter = userList.begin();
while (iter != userList.end())
{
delete (*iter);
++iter;
}
userList.clear();
}
void showUsers(vector<User*>& userList)
{
vector<User*>::iterator iter = userList.begin();
while (iter != userList.end())
{
cout << (*iter)->toString() <<endl;
++iter;
}
}
int main()
{
//首先初始化一批用户
vector<User*> userList;
createUsers(userList);
//定义一个用户查询类
IUserProvider* userProvider = new UserProvider(&userList);
//查询并打印年龄大于20岁的用户
vector<User*>* ul = userProvider->findUserByAgeThan(20);
showUsers(*ul);
ul->clear();
delete ul;
delUsers(userList);
return 0;
};
/*输出结果:
用户名:马七 年龄:25
用户名:杨八 年龄:30
用户名:侯九 年龄:35
用户名:布十 年龄:40
*/
31.1.2 解决方案
(1)规格模式
(2)类图
①规格书接口中增加了与或非操作,返回值为规格书类型这样设计的目的是为了连续调用。
②由于与或非是不可扩展的操作,这部分是不可能发生变化的部分。因此,这里出现了父类对子类的依赖,这种情况只有在非常明确不会发生变化的场景中。
【编程实验】
//User.h
#pragma once
#include <string>
#include <sstream>
using namespace std;
//************************************************辅助类*****************************************
//用户类
class User
{
private:
string name; //姓名
int age; //年龄
public:
User(string name, int age)
{
this->name = name;
this->age = age;
}
string getName(){return name;}
void setName(string value){name = value;}
int getAge(){return age;}
void setAge(int value){age = value;}
string toString()
{
ostringstream oss;
oss << age;
string ret = "用户名:" + name + "\t年龄:" + oss.str();
return ret;
}
};
//Specification.h
#pragma once
#include "User.h"
//**************************************定义规格接口*****************************
//规格书接口(支持与或非)
class ISpecification
{
public:
//候选者是否满足要求
virtual bool isSatisfiedBy(User* user) = 0;
//and操作
virtual ISpecification* andOp(ISpecification* spec) = 0;
//or操作
virtual ISpecification* orOp(ISpecification* spec) = 0;
//not操作
virtual ISpecification* notOp() = 0;
virtual ~ISpecification(){}
};
//组合规格书
class CompositeSpecification : public ISpecification
{
public:
ISpecification* andOp(ISpecification* spec);
ISpecification* orOp(ISpecification* spec);
ISpecification* notOp();
};
//and操作
class AndSpecification : public CompositeSpecification
{
private:
ISpecification* left;
ISpecification* right;
public:
AndSpecification(ISpecification* left, ISpecification* right);
//进行and运算
bool isSatisfiedBy(User* user);
};
//and操作
class OrSpecification : public CompositeSpecification
{
private:
ISpecification* left;
ISpecification* right;
public:
OrSpecification(ISpecification* left, ISpecification* right);
//进行or运算
bool isSatisfiedBy(User* user);
};
//not操作
class NotSpecification : public CompositeSpecification
{
private:
ISpecification* spec;
public:
NotSpecification(ISpecification* spec);
//进行not运算
bool isSatisfiedBy(User* user);
};
//姓名相同的规格书
class UserByNameEqual : public CompositeSpecification
{
private:
string name; //姓名
public:
UserByNameEqual(string name);
//检验用户是否满足条件
bool isSatisfiedBy(User* user);
};
//年龄大于20岁的规格书
class UserByAgeThan : public CompositeSpecification
{
private:
int age; //年龄
public:
UserByAgeThan(int age);
//检验用户是否满足条件
bool isSatisfiedBy(User* user);
};
//like规格书
class UserNameLike: public CompositeSpecification
{
private:
string name; //姓名
public:
UserNameLike(string name);
//检验用户是否满足条件
bool isSatisfiedBy(User* user);
};
//Specification.cpp
#include "Specification.h"
//****************************CompositeSpecification类***********************
//and操作
ISpecification* CompositeSpecification::andOp(ISpecification* spec)
{
return new AndSpecification(this, spec);
};
//or操作
ISpecification* CompositeSpecification::orOp(ISpecification* spec)
{
return new OrSpecification(this, spec);
};
//not操作
ISpecification* CompositeSpecification::notOp()
{
return new NotSpecification(this);
};
//****************************AndSpecification***********************
//and操作
AndSpecification::AndSpecification(ISpecification* left, ISpecification* right)
{
this->left = left;
this->right = right;
}
//进行and运算
bool AndSpecification::isSatisfiedBy(User* user)
{
return left->isSatisfiedBy(user) && right->isSatisfiedBy(user);
}
//****************************OrSpecification***********************
//and操作
OrSpecification::OrSpecification(ISpecification* left, ISpecification* right)
{
this->left = left;
this->right = right;
}
//进行or运算
bool OrSpecification::isSatisfiedBy(User* user)
{
return left->isSatisfiedBy(user) || right->isSatisfiedBy(user);
}
//****************************OrSpecification***********************
//not操作
NotSpecification::NotSpecification(ISpecification* spec)
{
this->spec = spec;
}
//进行not运算
bool NotSpecification::isSatisfiedBy(User* user)
{
return ! spec->isSatisfiedBy(user);
}
//****************************UserByNameEqual***********************
//姓名相同的规格书
UserByNameEqual::UserByNameEqual(string name)
{
this->name = name;
}
//检验用户是否满足条件
bool UserByNameEqual::isSatisfiedBy(User* user)
{
return (user->getName() == name);
}
//****************************UserByNameEqual***********************
//年龄大于20岁的规格书
UserByAgeThan::UserByAgeThan(int age)
{
this->age = age;
}
//检验用户是否满足条件
bool UserByAgeThan::isSatisfiedBy(User* user)
{
return (user->getAge() >= age);
}
//****************************UserNameLike***********************
//like规格书
UserNameLike::UserNameLike(string name)
{
this->name = name;
}
//检验用户是否满足条件
bool UserNameLike::isSatisfiedBy(User* user)
{
int nPos = (user->getName()).find(name);
return (nPos >=0);
}
//main.cpp
//新设计模式——规格模式
//实例:实现不同条件的查询(使用规格模式)
//说明: 本例除了可以进行简单条件语句的查询,也可以复合语
// 包含与、或、非等
#include <iostream>
#include <string>
#include <vector>
#include "Specification.h"
#include "User.h"
using namespace std;
//用户操作对象接口
class IUserProvider
{
public:
virtual vector<User*>* findUser(ISpecification* spec) = 0;
virtual ~IUserProvider(){}
};
//用户操作类
class UserProvider : public IUserProvider
{
private:
vector<User*>* userList;
public:
UserProvider(vector<User*>* userList)
{
this->userList = userList;
}
//查询指定规格的用户
vector<User*>* findUser(ISpecification* spec)
{
vector<User*>* ul = new vector<User*>();
vector<User*>::iterator iter = userList->begin();
for(;iter != userList->end(); ++iter)
{
if(spec->isSatisfiedBy(*iter)) //注意,条件语句被规格书所替代
{
ul->push_back((*iter));
}
}
return ul;
}
};
//**********************************************辅助函数***********************************
void createUsers(vector<User*>& userList)
{
userList.push_back( new User("苏三",3) );
userList.push_back( new User("牛二",8) );
userList.push_back( new User("张三",10) );
userList.push_back( new User("李四",15) );
userList.push_back( new User("王五",18) );
userList.push_back( new User("赵六",20) );
userList.push_back( new User("马七",25) );
userList.push_back( new User("杨八",30) );
userList.push_back( new User("侯九",35) );
userList.push_back( new User("布十",40) );
userList.push_back( new User("苏国庆",23) );
userList.push_back( new User("国庆牛",82) );
userList.push_back( new User("张国庆三",10) );
}
void delUsers(vector<User*>& userList)
{
vector<User*>::iterator iter = userList.begin();
while (iter != userList.end())
{
delete (*iter);
++iter;
}
userList.clear();
}
void showUsers(vector<User*>& userList)
{
vector<User*>::iterator iter = userList.begin();
while (iter != userList.end())
{
cout << (*iter)->toString() <<endl;
++iter;
}
}
int main()
{
//首先初始化一批用户
vector<User*> userList;
createUsers(userList);
//定义一个用户查询类
IUserProvider* userProvider = new UserProvider(&userList);
//查询并打印年龄大于20岁的用户
ISpecification* spec1 = new UserByAgeThan(20);
ISpecification* spec2 = new UserNameLike("国庆");
ISpecification* spec3 = spec1->andOp(spec2);
vector<User*>* ul = userProvider->findUser(spec3);
showUsers(*ul);
ul->clear();
delete spec1;
delete spec2;
delete spec3;
delete ul;
delete userProvider;
delUsers(userList);
return 0;
};
/*输出结果:
用户名:苏国庆 年龄:23
用户名:国庆牛 年龄:82
*/
31.1.3 小结
(1)基类代表所有规格书,它的目的是描述一个完整的、可组合的规格书,它代表的是一个整体,其下的And、Or、Not规格书、年龄大于基准年龄等规格书是一个个真实的实现,也就是一个局部,这是组合模式的一种特殊应用。
(2)每个规格书又是一个个策略,它完成一系列逻辑的封装,这些策略可以相互替换。
(3)规格模式可巧妙实现对象筛选功能。在类似于多个对象中筛选查找,或者业务规则不适于放在任何己有实体或值对象中,而且规则变化和组合会掩盖那些领域对象的基本含义,可以考虑该模式
(4)规格模式中有个很严重的问题就是父类依赖子类,这种情景只有在非常明确不会发生变化的场景中存在,它不具备扩展性,是一种固化而不可变化的结构。一般面向对象设计中应该尽量避免。