29.1 策略模式 VS 桥梁模式
29.1.1 策略模式
【编程实验】用策略模式实现邮件发送
(1)有文本和超文本两种格式的邮件,将这两种格式封装成两种不同的发送策略。
(2)文本邮件和超文本邮件分别是两种不同格式的邮件的封装。MailServer是一个环境角色,它接收一个MailTemplate对象,并通过sendMail方法将邮件发送出去。
//跨战区大PK——策略模式和桥接模式 //实例:用策略模式实现邮件发送 #include <iostream> #include <string> 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("a@a.com", "b@b.com", "外星人攻击地球了", "结局是外星人被地球人打败了!"); //创建邮件服务器 MailServer mailServ(*txtMail); //发送邮件 mailServ.sendMail(); return 0; }; /*输出结果: ====正在发送的邮件信息==== 发件人:a@a.com 收件人:b@b.com 邮件标题:外星人攻击地球了 邮件内容: Context-type: multipart/mixed;charset=GB2312 结局是外星人被地球人打败了! 邮件格式为:超文本格式 */
29.1.2 桥梁模式
【编程实验】用桥梁模式实现邮件发送
(1)增加了SendMail和Postfix两种邮件服务器的实现类,他们都从MailServer继承。
(2)这样邮件服务器与邮件模板就可以独立变化。
//跨战区大PK——策略模式和桥接模式 //实例:用桥接模式实现邮件发送 #include <iostream> #include <string> 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("a@a.com", "b@b.com", "外星人攻击地球了", "结局是外星人被地球人打败了!"); //创建邮件服务器 MailServer* mailServ = new Postfix(*txtMail); //发送邮件 mailServ->sendMail(); return 0; }; /*输出结果: ====正在发送的邮件信息==== 发件人:a@a.com 收件人:b@b.com 邮件标题:外星人攻击地球了 邮件内容: 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 <iostream> #include <string> 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 <iostream> #include <string> #include <ctime> 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 <<endl; //再查询出勤天数 cout << "====外系统查询出勤天数====" << endl; int workDays = facade.queryWorkDays("张三"); cout <<"张三1月的出勤天数:" << workDays <<endl; return 0; }; /*输出结果: ====外系统查询总收入==== 张三1月的总收放为:4000 ====外系统查询出勤天数==== 张三1月的出勤天数:29 */
29.2.3 小结
(1)功能区别
①外观模式只是增加了一个门面,它对子系统来说没有增加任何功能,子系统可以脱离门面而独立存在,它是以封装和隔离为主要任务。
②中介者模式则增加了业务功能,它把各个同事类中的原有耦合关系移到了中介者,同事类不可能脱离中介者而独立存在。
(2)知晓状态不同
对外观模式来说,子系统不知道有门面的存在,而对中介者来说,每个同事类都知道中介者存在,因为要依靠中介者调和同事之间的关系,它们对中介者非常了解。
(3)封装程序不同
外观模式是一种简单的封装,所有的请求处理者委托给子系统完成,而中介者模式则需要有一个中心,由中心协调同事类完成,并且中心本身也完成部分业务,它属于更进一步的业务功能封装。
29.3 包装模式群大PK
(1)包装模式群包括:装饰模式、适配器模式、外观模式、代理模式、桥接模式。他们中的有些角色基本自己不干活,都是通过委托的方式对一个对象或任务转发给其它类去做。
(2)代理模式:主要用在不希望展示一个对象内部细节的场景中,比如一个远程服务不需要把远程连接的所有细节暴露给外部模式,通过增加一个代理类,可以轻松地实现被代理类的功能封装。
(3)装饰模式:它倡导在不改变接口的前提下为对象增强功能或添加额外的职责,就扩展性而言,它比增加子类更加灵活。
(4)适配器模式:主要意图是转换接口,把一个对象的接口转换成另一个接口。
(5)桥接模式:在抽象层产生耦合,解决的是自行扩展的问题,它可以使两个有耦合关系的对象互不影响地扩展。
(6)外观模式:是一个粗粒度的封装,它提供一个方便访问子系统的接口,不具有任何的业务逻辑,仅仅是一个访问复杂系统的快速通道。没有它,子系统照样运行,有了它,只是更方便访问而己。