设计模式 c++版(7)——原型模式

定义:

用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。


示例一:原型模式(通用版)

1. 类图13-3

 

2.  代码清单13-3:

class PrototypeClass;
class Cloneable
{
public:
    virtual PrototypeClass clone() = 0;
};

class PrototypeClass:public Cloneable
{
public:
    virtual PrototypeClass clone()
    {
        PrototypeClass proto;
        //copy
        return proto;
    }
};

 

示例二:个性化电子账单


1. 需求说明:

银行发送电子账单的邮件一般是有要求的:
①个性化服务:发过去的邮件上总有一些个人信息,比如姓氏等。
②递送成功率:若大批量地发送邮件会被收房邮件服务器误认为是垃圾邮件,因此在邮件头要增加一些伪造数据,以规避被反垃圾邮件引擎误认为是垃圾邮件

从这两方面考虑,电子账单系统(电子账单系统一般包括:账单分析、账单生成器、广告信管理、发送队列管理、发送机、退信处理、报表管理等)的一个子功能,现在考虑一下广告新这个模块如何开发。既然是广告信,肯定需要一个模板,然后再从数据库中把客户的信息一个一个地取出,放到模板中生成一份完整的邮件,然后扔给发送机进行发送处理

 

2. 类图13-1

3. 类图说明

AdvTemplate 是广告信的模板,一般都是从数据库取出,生成一个BO或是DTO,我们这里使用一个惊天的值来代表
Mail 类是一封邮件类,发送机发送的就是这个类

 

4. 代码清单13-1:

    **********  1. 发送电子账单,代码清单13-1:***************//


class AdvTemplate
{
public:
    AdvTemplate()
    {
        this->m_advSubject = "The National Day lottery";
        this->m_advContext = "***********";
    }
    QString getAdvSubject()
    {
        return this->m_advSubject;
    }
    QString getAdvContext()
    {
        return this->m_advContext;
    }

private:
    QString m_advSubject;  //title
    QString m_advContext;  //text
};

class Mail
{
public:
    Mail(){}
    Mail(AdvTemplate advTemplate)
    {
        this->m_contxt = advTemplate.getAdvContext();
        this->m_subject = advTemplate.getAdvSubject();
    }
    QString getReceiver()
    {
        return this->m_receiver;
    }
    void    setReceiver(QString receiver)
    {
        this->m_receiver = receiver;
    }
    QString getSubject()
    {
        return this->m_subject;
    }
    void    setSubject(QString subject)
    {
        this->m_subject = subject;
    }
    QString getAppellation()
    {
        return this->m_appellation;
    }
    void    setAppellation(QString appellation)
    {
        this->m_appellation = appellation;
    }
    QString getContxt()
    {
        return this->m_contxt;
    }
    void    setContxt(QString contxt)
    {
        this->m_contxt = contxt;
    }
    QString getTail()
    {
        return this->m_tail;
    }
    void    setTail(QString tail)
    {
        this->m_tail = tail;
    }

private:
    QString m_receiver;
    QString m_subject;
    QString m_appellation;
    QString m_contxt;
    QString m_tail;
};

static int MAX_COUNT = 6;

class Client
{
public:
    Client()
    {
        AdvTemplate temp;
        this->m_mail = Mail(temp);
        this->m_mail.setTail("copyright");     
        this->m_mail.setAppellation(getString(5));
        this->m_mail.setReceiver(getString(5) + "@");       
    }
    void sendMail()
    {
        for (int i = 0; i < MAX_COUNT; ++i)
        {
            this->m_mail.setAppellation(getString(5));
            this->m_mail.setReceiver(getString(5) + "@");
            qDebug() << "title:" + m_mail.getSubject();
            qDebug() << "receiver:" + m_mail.getReceiver();
        }
    }

    static QString getString(int maxLength)
    {
        QString source = "abcdefghijklmn";
        QString str = source.left(maxLength);
        return str;
    }
private:
    Mail m_mail;
};

int main()
{
    Client client;
    client.sendMail();
    return 0;
}

 

5. 代码分析

这是一个线程在运行,也就是发送的是单线程的,那按照一封邮件发出去需要0.02秒,600万封邮件需要33个小时,也就是一整天都发送不完。在此做修改,把sendMail修改为多线程,但是只把sendMail修改为多线程还是有问题,产生第一封邮件对象,放到线程1中运行,还没有发送出去;线程2也启动了,直接把邮件对象mail的收件人地址和称谓修改掉了,线程不安全了。这里我们使用一种新型模式来解决这个问题:通过对象的复制功能来解决。


示例三:修改后的发送电子账单

1. 类图13-2

 

2. 代码清单13-2:


    **********  2. 修改后的发送电子账单,代码清单13-2:***************//

class Mail;
class Cloneable
{
public:
    virtual Mail clone() = 0;
};

class AdvTemplate
{
public:
    AdvTemplate()
    {
        this->m_advSubject = "The National Day lottery";
        this->m_advContext = "***********";
    }
    QString getAdvSubject()
    {
        return this->m_advSubject;
    }
    QString getAdvContext()
    {
        return this->m_advContext;
    }

private:
    QString m_advSubject;  //title
    QString m_advContext;  //text
};

class Mail:public Cloneable
{
public:
    Mail(){}
    Mail(AdvTemplate advTemplate)
    {
        this->m_contxt = advTemplate.getAdvContext();
        this->m_subject = advTemplate.getAdvSubject();
    }
    virtual Mail clone()
    {
        Mail mail;
        mail.setAppellation(this->m_appellation);
        mail.setContxt(this->m_contxt);
        mail.setReceiver(this->m_receiver);
        mail.setSubject(this->m_receiver);
        mail.setTail(this->m_tail);
        return mail;
    }

    QString getReceiver()
    {
        return this->m_receiver;
    }
    void    setReceiver(QString receiver)
    {
        this->m_receiver = receiver;
    }
    QString getSubject()
    {
        return this->m_subject;
    }
    void    setSubject(QString subject)
    {
        this->m_subject = subject;
    }
    QString getAppellation()
    {
        return this->m_appellation;
    }
    void    setAppellation(QString appellation)
    {
        this->m_appellation = appellation;
    }
    QString getContxt()
    {
        return this->m_contxt;
    }
    void    setContxt(QString contxt)
    {
        this->m_contxt = contxt;
    }
    QString getTail()
    {
        return this->m_tail;
    }
    void    setTail(QString tail)
    {
        this->m_tail = tail;
    }

private:
    QString m_receiver;
    QString m_subject;
    QString m_appellation;
    QString m_contxt;
    QString m_tail;
};

static int MAX_COUNT = 6;

class Client
{
public:
    Client()
    {
        AdvTemplate temp;
        this->m_mail = Mail(temp);
        this->m_mail.setTail("copyright");     
        this->m_mail.setAppellation(getString(5));
        this->m_mail.setReceiver(getString(5) + "@");       
    }
    void sendMail()
    {
        for (int i = 0; i < MAX_COUNT; ++i)
        {
            Mail mail = this->m_mail.clone();
            mail.setAppellation(getString(5));
            mail.setReceiver(getString(5) + "@");
            qDebug() << "title:" + mail.getSubject();
            qDebug() << "receiver:" + mail.getReceiver();
        }
    }

    static QString getString(int maxLength)
    {
        QString source = "abcdefghijklmn";
        QString str = source.left(maxLength);
        return str;
    }
private:
    Mail m_mail;
};

int main()
{
    Client client;
    client.sendMail();
    return 0;
}

 

3.代码说明:

在这里即使sendMail即使是多线程也没有关系。在Client类中,mail.clone() 这个方法,把对象复制一份,产生一个新的对象,和原有对象一样,然后再修改细节的数据。不通过new关键字来产生一个对象,而是通过对象复制来实现的模式叫做原型模式。

 

四、原型模式的应用

1. 优点:

 性能优良。原型模式是在内存二进制流的拷贝,比直接new一个对象性能好很多,特别死要在一个循环体内产生大量的对象时,原型模式可以更好地体现其优点
 逃避构造函数的约束。直接在内存中拷贝,构造函数是不会执行的。

 

2. 使用场景:

 资源优化场景。类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。
 性能和安全要求的场景。通过 new 产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。
 一个对象多个修改者的场景。一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。


3. 注意事项:

对象拷贝时构造函数没有被执行,Object类的clone 方法的原理是从内存中(具体地说就是堆内存)以二进制流的方式进行拷贝,重新分配一个内存块,那构造函数没有被执行也很正常了。


五、最佳实践

原型模式先产生出一个包含大量共有信息的类,然后可以拷贝出副本,修正细节信息,建立了一个完整的个性对象。

 


参考文献《秦小波. 设计模式之禅》(第2版) (华章原创精品) 机械工业出版社

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值