函数适配器
适配器模式是一种常用的设计模式,适配器将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以在一起工作。 简单来说适配器模式就是设计一个适配器,实例化一个实现具体功能的类对象作为自己的成员。然后适配器提供一些方法,这些方法实际上都将转化成对这个成员的方法的调用。在转化过程中,还可以进行一些额外的操作。 STL里面的容器
适配器模式是一种常用的设计模式,适配器将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以在一起工作。
简单来说适配器模式就是设计一个适配器,实例化一个实现具体功能的类对象作为自己的成员。然后适配器提供一些方法,这些方法实际上都将转化成对这个成员的方法的调用。在转化过程中,还可以进行一些额外的操作。
STL里面的容器适配器正是用了适配器模式这种思想,stack(栈)和queue(队列)实际上只是个容器适配器。它们的内部都是用强大的deque(双端队列)实现的,只不过屏蔽和改造了一下它的接口以适用于不用的场合。
在GoF的设计模式中,对适配器模式讲了两种类型,类适配器和对象适配器。其实还有一种函数适配器,比如MFC中的CWnd::Create()和CWnd::CreateEx()。Create()可以用简单少量的参数来创建窗口,CreateEx()虽然参数较多,但提供了更多的定制。跟踪进去发现Create()和CreateEx()并非是完全不同的实现。Create()里面实际上也是补齐参数后调用CreateEx()。这也是一种简单的函数适配器,MFC中类似的还有很多。当我们设计类方法、设计API时,不妨学习这种做法。先实现一个很强大的MethodEx(),然后再根据具体需要提供Method1(),Method2()等方法,当然这些方法最终都是调用MethodEx()实现。如果你还是不太理解的话请看我下面这个例子,虽然可能不太恰当。
问题是这样的:我已经把一个网页html内容获取到了,现在我想从html中提取出那些我需要的信息。该html页面上是一个学生信息表,有学生的学号、姓名、性别、电话、住址、成绩等信息。起初我想把学号和对应的成绩提取出来,组成一对一对的(make_pair),放到map容器里,这样便可以根据学号直接得到成绩。于是我写一个函数,该函数传入包含html内容的CString对象,返回一个map对象。
bool ExtractScore(const CString &strHtml, map<CString,int> &map_StudentScore)
{
CString strID;
int nScore;
map<CString,int> map_Temp;
while (/*没有提取完*/)
{
//...
//把HTML中的学号和对应的成绩提取并赋值到strID和nScore
map_Temp.insert(make_pair(strID, nScore));
}
if (map_Temp.size() > 0)
{
map_StudentScore = move(map_Temp);
return true;
}
else
return false;
}
过了一段时间,我又需要一个功能,从html中提取出学号、姓名和成绩,保存到数据库中。于是我先定义一个数据结构:
struct Score
{
CString strID;
CString strName;
int nScore;
};
然后写一个函数:
bool ExtractScore(const CString &strHtml, vector<Score> &vec_StudentScore)
{
Score score;
vector<Score> vec_Temp;
while (/*没有提取完*/)
{
//...
//把HTML中的学号、姓名、成绩提取并赋值到score中
vec_Temp.push_back(score);
}
if (vec_Temp.size() > 0)
{
vec_StudentScore = move(vec_Temp);
return true;
}
else
return false;
}
写完以后,我就琢磨,不对呀!如果过段时间我不仅要获取学号、姓名和成绩,还要获取性别、电话、住址等信息呢?岂不是又要写一个这样的函数?如果我想只获取姓名和电话,又要写一个这样的函数?而且上面这两个函数中,在while循环中提取学号和成绩的过程完全是相同的。应该把相同的地方提取出来。
不如这样,我写一个很强大的函数,能一次把所有信息都提取出来,再用适配器去包装以适应不同的场合。于是一个强大的函数诞生了:
struct StudentInfo
{
CString strID;
CString strName;
wchar_t wchSex;
CString strTel;
CString strAddress;
int nScore;
};
bool ExtractInfo(const CString &strHtml, vector<StudentInfo> &vec_StudentInfo)
{
StudentInfo info;
vector<StudentInfo> vec_Temp;
while (/*没有提取完*/)
{
//...
//把HTML中的学生信息提取并赋值到info中
vec_Temp.push_back(info);
}
if (vec_Temp.size() > 0)
{
vec_StudentInfo = move(vec_Temp);
return true;
}
else
return false;
}
然后再改造之前的两个函数,都调用这个强大的ExtractInfo获取所有信息后,进行筛选、装配,再返回。
第一个:
bool ExtractScore(const CString &strHtml, map<CString,int> &map_StudentScore)
{
vector<StudentInfo> vec_StudentInfo;
map<CString,int> map_Temp;
if (!ExtractInfo(strHtml, vec_StudentInfo))
return false;
for (size_t i=0; i<vec_StudentInfo.size(); i++)
map_Temp.insert(make_pair(vec_StudentInfo[i].strID, vec_StudentInfo[i].nScore));
if (map_Temp.size() > 0)
{
map_StudentScore = move(map_Temp);
return true;
}
else
return false;
}
第二个:
bool ExtractScore(const CString &strHtml, vector<Score> &vec_StudentScore)
{
vector<StudentInfo> vec_StudentInfo;
vector<Score> vec_Temp;
Score score;
if (!ExtractInfo(strHtml, vec_StudentInfo))
return false;
for (size_t i=0; i<vec_StudentInfo.size(); i++)
{
score.strID = vec_StudentInfo[i].strID;
score.strName = vec_StudentInfo[i].strName;
score.nScore = vec_StudentInfo[i].nScore;
vec_Temp.push_back(score);
}
if (vec_Temp.size() > 0)
{
vec_StudentScore = move(vec_Temp);
return true;
}
else
return false;
}
这样,以后再有不用的需求,我只需要再写一个适配函数,调用我这个强大的函数后,对结果进行筛选、装配再返回即可。比如我要将所有信息组合成一个长长的字符串一次性返回。只需写一个适配函数,该适配函数调用强大函数后要做的事只有组合数据并返回。
也许你会觉得奇怪,这样代码量不是更多了吗?并且效率也降低了。其实代码最多的地方是在从html提取信息的那个循环那里,那里可达300多行,只是在这里我没有写出来。如果那部分重复重复的写在不同的函数中的话,代码量可想而知。关于效率问题,这样做确实会降低效率,毕竟如果我只想提取学号和成绩,实际调用的时候也会提取所有信息,并且还要经过适配器筛选装配。但在实际应用中,这点效率差距其实是可以忽略的,就比如我这个程序,最大的效率瓶颈还是在获取html网页时网速问题上。优点当然也是明显的,如果代码比较复杂,需求经常变化,代码经常需要增改,这无疑是一种相对较好的设计。而且效率问题也不是不可以改进。比如我们这个强大的函数是不是可以增加一个参数,用这个参数来控制它什么内容需要获取,什么内容不需要获取。
更多推荐
所有评论(0)