第29章 跨战区大PK

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("[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 <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("[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 <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)外观模式:是一个粗粒度的封装,它提供一个方便访问子系统的接口,不具有任何的业务逻辑,仅仅是一个访问复杂系统的快速通道。没有它,子系统照样运行,有了它,只是更方便访问而己。

猜你喜欢

转载自blog.csdn.net/CherishPrecious/article/details/84058787