29.1 策略模式 VS 桥梁模式
29.1.1 策略模式
【编程实验】用策略模式实现邮件发送
(1)有文本和超文本两种格式的邮件,将这两种格式封装成两种不同的发送策略。
(2)文本邮件和超文本邮件分别是两种不同格式的邮件的封装。MailServer是一个环境角色,它接收一个MailTemplate对象,并通过sendMail方法将邮件发送出去。
//跨战区大PK——策略模式和桥接模式
//实例:用策略模式实现邮件发送
#include
#include
using namespace std;
//抽象邮件
class MailTemplate
{
protected:
string from; //邮件发件人
string to; //收件人
string subject; //邮件标题
protected:
string context; //邮件内容
public:
MailTemplate(string from, string to,
string subject, string context)
{
this->from = from;
this->to = to;
this->subject = subject;
this->context = context;
}
string getFrom(){return from;}
void setFrom(string value){from = value;}
string getTo(){return to;}
void setTo(string value){to = value;}
string getSubject(){return subject;}
void setSubject(string value){subject = value;}
void setContext(string value){context = value;}
virtual string getContext() = 0;
};
//文本邮件
class TextMail : public MailTemplate
{
public:
TextMail(string from, string to,
string subject, string context):MailTemplate(from, to, subject, context)
{
}
string getContext()
{
//文本类型设置邮件的格式为text/plain
context = "\n Context-type: text/plain;charset=GB2312\n"
+ context;
//同时对邮件进行base64编码处理,这里用一句话代替
context += "\n邮件格式为:文本格式";
return context;
}
};
//超文本邮件
class HtmlMail : public MailTemplate
{
public:
HtmlMail(string from, string to,
string subject, string context):MailTemplate(from, to, subject, context)
{
}
string getContext()
{
//超文本类型设置邮件的格式为multipart/mixed
context = "\n Context-type: multipart/mixed;charset=GB2312\n"
+ context;
//同时对邮件进行HTML检查,是否有类似未关闭的标答
context += "\n邮件格式为:超文本格式";
return context;
}
};
//邮件服务器(环境角色类)
class MailServer
{
private:
MailTemplate* mail;
public:
MailServer(MailTemplate& mt)
{
mail = &mt;
}
//发送邮件
void sendMail()
{
cout << "====正在发送的邮件信息====" << endl;
//发件人
cout << "发件人:" << mail->getFrom()<< endl;
//收件人
cout << "收件人:" << mail->getTo()<< endl;
//邮件标题
cout << "邮件标题:" << mail->getSubject()<< endl;
//邮件内容
cout << "邮件内容:" << mail->getContext()<< endl;
}
};
int main()
{
//创建一封超文本格式的邮件
MailTemplate* txtMail = new HtmlMail("[email protected]", "[email protected]",
"外星人攻击地球了", "结局是外星人被地球人打败了!");
//创建邮件服务器
MailServer mailServ(*txtMail);
//发送邮件
mailServ.sendMail();
return 0;
};
/*输出结果:
====正在发送的邮件信息====
发件人:[email protected]
收件人:[email protected]
邮件标题:外星人攻击地球了
邮件内容:
Context-type: multipart/mixed;charset=GB2312
结局是外星人被地球人打败了!
邮件格式为:超文本格式
*/
29.1.2 桥梁模式
【编程实验】用桥梁模式实现邮件发送
(1)增加了SendMail和Postfix两种邮件服务器的实现类,他们都从MailServer继承。
(2)这样邮件服务器与邮件模板就可以独立变化。
//跨战区大PK——策略模式和桥接模式
//实例:用桥接模式实现邮件发送
#include
#include
using namespace std;
//抽象邮件
class MailTemplate
{
protected:
string from; //邮件发件人
string to; //收件人
string subject; //邮件标题
protected:
string context; //邮件内容
public:
MailTemplate(string from, string to,
string subject, string context)
{
this->from = from;
this->to = to;
this->subject = subject;
this->context = context;
}
string getFrom(){return from;}
void setFrom(string value){from = value;}
string getTo(){return to;}
void setTo(string value){to = value;}
string getSubject(){return subject;}
void setSubject(string value){subject = value;}
void setContext(string value){context = value;}
virtual string getContext() = 0;
//允许增加邮件发送标志
void add(string sendInfo)
{
context =sendInfo + context;
}
};
//文本邮件
class TextMail : public MailTemplate
{
public:
TextMail(string from, string to,
string subject, string context):MailTemplate(from, to, subject, context)
{
}
string getContext()
{
//文本类型设置邮件的格式为text/plain
context = "\nContext-type: text/plain;charset=GB2312\n"
+ context;
//同时对邮件进行base64编码处理,这里用一句话代替
context += "\n邮件格式为:文本格式";
return context;
}
};
//超文本邮件
class HtmlMail : public MailTemplate
{
public:
HtmlMail(string from, string to,
string subject, string context):MailTemplate(from, to, subject, context)
{
}
string getContext()
{
//超文本类型设置邮件的格式为multipart/mixed
context = "\nContext-type: multipart/mixed;charset=GB2312\n"
+ context;
//同时对邮件进行HTML检查,是否有类似未关闭的标答
context += "\n邮件格式为:超文本格式";
return context;
}
};
//邮件服务器(相当于桥接模式的抽象化角色)
class MailServer
{
protected:
MailTemplate* mail;
public:
MailServer(MailTemplate& mt)
{
mail = &mt;
}
//发送邮件
virtual void sendMail()
{
cout << "====正在发送的邮件信息====" << endl;
//发件人
cout << "发件人:" << mail->getFrom()<< endl;
//收件人
cout << "收件人:" << mail->getTo()<< endl;
//邮件标题
cout << "邮件标题:" << mail->getSubject()<< endl;
//邮件内容
cout << "邮件内容:" << mail->getContext()<< endl;
}
};
//Postfix邮件服务器
class Postfix : public MailServer
{
public:
Postfix(MailTemplate& mt):MailServer(mt){}
//修正邮件发送程序
void sendMail()
{
//增加邮件服务器信息
string context = "Received: from XXXX(unknow[xxx.xxx.xxx.xxx]) by ";
context += "aaa.aaa.com(Postfix) with ESMTP id 8DBCD172B8\n";
mail->add(context);
MailServer::sendMail();
}
};
//SendMail邮件服务器
class SendMail : public MailServer
{
public:
SendMail(MailTemplate& mt):MailServer(mt){}
//修正邮件发送程序
void sendMail()
{
//增加邮件服务器信息
string context = "Received: (sendmail);7 Nov 2016 10:40:00 +100\n";
mail->add(context);
MailServer::sendMail();
}
};
int main()
{
//创建一封超文本格式的邮件
MailTemplate* txtMail = new HtmlMail("[email protected]", "[email protected]",
"外星人攻击地球了", "结局是外星人被地球人打败了!");
//创建邮件服务器
MailServer* mailServ = new Postfix(*txtMail);
//发送邮件
mailServ->sendMail();
return 0;
};
/*输出结果:
====正在发送的邮件信息====
发件人:[email protected]
收件人:[email protected]
邮件标题:外星人攻击地球了
邮件内容:
Context-type: multipart/mixed;charset=GB2312
Received: from XXXX(unknow[xxx.xxx.xxx.xxx]) by aaa.aaa.com(Postfix) with ESMTP
id 8DBCD172B8
结局是外星人被地球人打败了!
邮件格式为:超文本格式
*/
29.1.3 小结
(1)策略模式是一个行为模式,旨在封装一系列的行为。可以把邮件的必要信息封装成一个对象,也就是一个行为,封装的格式不同,行为也就不同。
(2)桥梁模式则是解决在不破坏封装的情况下将抽象和实现部分分离,让他们可以独立变化。
(3)策略模式是使用继承和多态建立一套可以自由切换算法的模式,而桥梁模式必然有两个“桥墩”——抽象化角色和实现化角色,只要桥墩搭建好了,桥就有了。策略模式只有一个抽象角色,可以没有实现,也可以有很多实现。
29.2 外观模式 VS 中介者模式
29.2.1 中介者模式实现工资计算
(1)工资与职位、税收有关,职位提升工资就会增加,同时税收也增加,反之也成立。
(2)当税收比率增加,工资自然就减少,这三者之间两两都有关系,很适合用中介者模式
【编程实验】中介者模式实现工资计算
//跨战区大PK——中介者模式和外观模式
//实例:用中介者模式实现工资计算
#include
#include
using namespace std;
//***********************************************辅助接口类***************************
//职位接口
class IPosition
{
public:
virtual void promote() = 0; //升职
virtual void demote() = 0; //除职
virtual ~IPosition(){}
};
//工资接口
class ISalary
{
public:
//加薪
virtual void increaseSalary() = 0;
//减薪
virtual void decreaseSalary() = 0;
virtual ~ISalary(){}
};
//税收接口
class ITax
{
public:
//税收上升
virtual void raise() = 0;
//税收降低
virtual void drop() = 0;
virtual ~ITax(){}
};
//*****************************************中介者************************************************
//抽象中介者
class AbsMediator
{
public:
virtual void up(ISalary* salary) = 0; //工资增加
virtual void up(ITax* tax) = 0; //税收增加
virtual void up(IPosition* position) = 0; //职位上升
virtual void down(ISalary* salary) = 0; //工资减少
virtual void down(ITax* tax) = 0; //税收降低
virtual void down(IPosition* position) = 0; //职位降低
};
//*********************************************同事类**********************************
//抽象同事类
class AbsColleague
{
protected:
//每个同事类都对中介者非常了解
AbsMediator* mediator;
public:
AbsColleague(AbsMediator* mediator)
{
this->mediator = mediator;
}
};
//职位
class Position : public AbsColleague, public IPosition
{
public:
Position(AbsMediator* mediator): AbsColleague(mediator){}
//职位上升
void promote()
{
mediator->up(this);
}
//职位下降
void demote()
{
mediator->down(this);
}
};
//工资
class Salary : public AbsColleague, public ISalary
{
public:
Salary(AbsMediator* mediator):AbsColleague(mediator){}
void increaseSalary()
{
mediator->up(this);
}
void decreaseSalary()
{
mediator->down(this);
}
};
//税收
class Tax : public AbsColleague, public ITax
{
public:
Tax(AbsMediator* mediator): AbsColleague(mediator){}
void raise()
{
mediator->up(this);
}
void drop()
{
mediator->down(this);
}
};
//中介者
class Mediator : public AbsMediator
{
private:
void upSalary()
{
cout << "工资翻倍,乐翻天" << endl;
}
void upTax()
{
cout << "税收上升,为国家做贡献" << endl;
}
void upPosition()
{
cout << "职位上升一级,狂喜" << endl;
}
void downSalary()
{
cout << "经济不景气,降低工资" << endl;
}
void downTax()
{
cout << "税收减少,国家收入减少" << endl;
}
void downPosition()
{
cout << "官降三级,比自杀还痛苦" << endl;
}
public:
//工资增加了
void up(ISalary* salary)
{
upSalary();
upTax();
}
void up(ITax* tax)
{
upTax();
downSalary();
}
void up(IPosition* position)
{
upPosition();
upSalary();
upTax();
}
void down(ISalary* salary)
{
downSalary();
downTax();
}
void down(ITax* tax)
{
downTax();
upSalary();
}
void down(IPosition* position)
{
downPosition();
downSalary();
downTax();
}
};
int main()
{
//定义中介者
Mediator mediator;
//定义各个同事类
IPosition* position = new Position(&mediator);
ISalary* salary = new Salary(&mediator);
ITax* tax = new Tax(&mediator);
//职位上升了
position->promote();
cout << endl;
//职位下降了
position->demote();
delete position;
delete salary;
delete tax;
return 0;
};
/*输出结果:
职位上升一级,狂喜
工资翻倍,乐翻天
税收上升,为国家做贡献
官降三级,比自杀还痛苦
经济不景气,降低工资
税收减少,国家收入减少
*/
29.2.2 外观模式实现工资计算
(1)工资计算是对基本工资、月奖金、绩效、考勤、税收等因素综合计算的结果。
(2)对于高管理层,是不希望看到中间的计算过程,他只要求传递一个人员和月份即可查询到某员工的工资,而不用关心其中复杂的计算过程,这很适合用外观模式来实现。
【编程实验】外观模式实现工资计算
//跨战区大PK——中介者模式和外观模式
//实例:用外观模式实现工资查询
#include
#include
#include
using namespace std;
//考勤情况
class Attendance
{
int days;
public:
Attendance()
{
srand((int)time(NULL));
days = rand() & 31;
}
//得到出勤天数
int getWorkDays(){return days;}
};
//奖金计算
class Bonus
{
private:
Attendance atte; //考勤情况
public:
//奖金
int getBonus()
{
//获得出勤情况
int workDays = atte.getWorkDays();
//奖金计算模型
int bonus = workDays * 1800 / 30;
return bonus;
}
};
//基本工资
class BasicSalary
{
public:
int getBasicSalary()
{
return 2000;
}
};
//绩效
class Performance
{
private:
BasicSalary basicSalary;
public:
int getPerformanceValue()
{
//随机绩效
int perf = rand() % 101;
return basicSalary.getBasicSalary() * perf /100;
}
};
//税收
class Tax
{
public:
//交纳税收多少
int getTax()
{
//随机数量
return rand() & 300;
}
};
//总工资计算
class SalaryProvider
{
private:
BasicSalary salary; //基本工资
Bonus bonus; //奖金
Performance perf; //绩效
Tax tax; //税收
public:
//获得用户的总收入
int totalSalary()
{
return salary.getBasicSalary() + bonus.getBonus() +
perf.getPerformanceValue() - tax.getTax();
}
};
//外观类
class HRFacade
{
private:
//总工资情况
SalaryProvider salaryProvider;
//考勤情况
Attendance attendance;
public:
//查询一个人的总收入
int querySalary(string name)
{
return salaryProvider.totalSalary();
}
//查询一个员工一个月工作了多少天
int queryWorkDays(string name)
{
return attendance.getWorkDays();
}
};
int main()
{
//定义外观
HRFacade facade;
cout << "====外系统查询总收入====" << endl;
int salary = facade.querySalary("张三");
cout <<"张三1月的总收放为:" << salary <
29.2.3 小结
(1)功能区别
①外观模式只是增加了一个门面,它对子系统来说没有增加任何功能,子系统可以脱离门面而独立存在,它是以封装和隔离为主要任务。
②中介者模式则增加了业务功能,它把各个同事类中的原有耦合关系移到了中介者,同事类不可能脱离中介者而独立存在。
(2)知晓状态不同
对外观模式来说,子系统不知道有门面的存在,而对中介者来说,每个同事类都知道中介者存在,因为要依靠中介者调和同事之间的关系,它们对中介者非常了解。
(3)封装程序不同
外观模式是一种简单的封装,所有的请求处理者委托给子系统完成,而中介者模式则需要有一个中心,由中心协调同事类完成,并且中心本身也完成部分业务,它属于更进一步的业务功能封装。
29.3 包装模式群大PK
(1)包装模式群包括:装饰模式、适配器模式、外观模式、代理模式、桥接模式。他们中的有些角色基本自己不干活,都是通过委托的方式对一个对象或任务转发给其它类去做。
(2)代理模式:主要用在不希望展示一个对象内部细节的场景中,比如一个远程服务不需要把远程连接的所有细节暴露给外部模式,通过增加一个代理类,可以轻松地实现被代理类的功能封装。
(3)装饰模式:它倡导在不改变接口的前提下为对象增强功能或添加额外的职责,就扩展性而言,它比增加子类更加灵活。
(4)适配器模式:主要意图是转换接口,把一个对象的接口转换成另一个接口。
(5)桥接模式:在抽象层产生耦合,解决的是自行扩展的问题,它可以使两个有耦合关系的对象互不影响地扩展。
(6)外观模式:是一个粗粒度的封装,它提供一个方便访问子系统的接口,不具有任何的业务逻辑,仅仅是一个访问复杂系统的快速通道。没有它,子系统照样运行,有了它,只是更方便访问而己。