首页 > 其他分享 >原型模式解析

原型模式解析

时间:2024-03-08 16:35:00浏览次数:28  
标签:name clone 模式 原型 基类 SubclassA 解析 拷贝 构造函数

本文是什么

在前段时间学习了一些设计模式,里面的一些内容还是需要去值得思考的。其中有一种创建型模式叫做原型模式。在之前看的一些文章中,单例模式写的和拷贝构造函数差不多。但经过几天的思考,感觉还是和拷贝构造函数有着明显的区别,故写下此文表达我的想法。如果有不同的思考,可以在评论区一起互动交换意见。

正文

什么是原型模式

我看过的一个说法是:原型模式是一种创建型设计模式,使你能够复制已有的对象,而无需使得代码依赖他们所属的类。

SubclassA可见,成员为公有变量的情况

现在让我们来思考。当我们看到有一个对象a,他所属的类是SubclassA。我们想要复制这个对象到新的对象aa的时候,该怎么做呢?其实,我们像下面的代码一样,只要看着SubclassA的构造,一个一个地复制对象中的成员变量即可。

SubclassA
{
public:
    std::string name;

    Subclass A()
    {
        this->name = "Subclass A";
    }
}

int main(void)
{
    SubclassA a;
    SubclassA aa;
    aa->name = a->name;
    return 0;
}

SubclassA可见,成员为私有变量的情况

当成员为私有变量的时候,上面的代码就无法工作了。因为封装的特性,我们是无法直接访问私有变量的。这时有懂的小伙伴就会说:只要设置拷贝构造函数就可以了。没错,这个方法可行,拷贝构造函数作为成员方法,可以访问到类内到私有变量,这种场景最合适不过了。

SubclassA
{
private:
    std::string name;

public:
    Subclass A()
    {
        this->name = "Subclass A";
    }

    Subclass A(const SubclassA &a)
    {
        this->name = a.name;
    }
}

int main(void)
{
    SubclassA a;
    SubclassA aa(a);
    return 0;
}

SubclassA不可见的情况

当SubclassA对我们不可见的时候,上面的代码又失效了。因为我们现在没法实例化新的对象aa了,自然我们连拷贝构造函数也无法使用了。但其实也不是完全不可见,这里还需要假设我们知道这个类实现了什么样的接口,不然我们是真的没有办法了。这里就需要使用原型模式了(注意,这里原型模式是库的作者,也就是SubclassA需要提供的,而不是调用的客户)。原型模式需要SubclassA的作者实现它的可见API(也就是基类),拷贝构造函数和核心的clone函数。让我们来看看是如何实现的。

class Prototype
{
protected:
    std::string name;

public:
    Prototype()
    {
        name = "Prototype";
    }

    Prototype(Prototype &p)
    {
        name = p.name;
    }

    virtual void show()
    {
        std::cout << name << std::endl;
    }

    virtual Prototype *clone()
    {
        return new Prototype(*this);
    }
};

class SubclassA : public Prototype
{
private:
    std::string name;

public:
    SubclassA()
    {
        name = "Subclass A";
    }

    SubclassA(SubclassA &p)
    {
        name = p.name;
    }

    virtual void show()
    {
        std::cout << name << std::endl;
    }

    virtual Prototype *clone()
    {
        return new SubclassA(*this);
    }
};

class SubclassB : public Prototype
{
private:
    std::string name;

public:
    SubclassB()
    {
        name = "Subclass B";
    }

    SubclassB(SubclassB &p)
    {
        name = p.name;
    }

    virtual void show()
    {
        std::cout << name << std::endl;
    }

    virtual Prototype *clone()
    {
        return new SubclassB(*this);
    }
};

int main(void)
{
    // SubclassA and SubclassB are invisible to the client
    SubclassA a = SubclassA();
    SubclassB b = SubclassB();

    // Prototype, pa and pb is visible to the client
    Prototype *pa = &a;
    Prototype *pb = &b;

    Prototype p(*pa);
    p.show();
    Prototype *pp = pa->clone();
    pp->show();
    pp = pb->clone();
    pp->show();
    return 0;
}

输出以下信息

Prototype
Subclass A
Subclass B

在上面的例子中,我们按照我们的假设SubclassA类和SubclassB类都是不可见的。我们能看见的只有代表了可见API的基类Prototype类以及基类指针pa和pb。
原型模式的设计很简单,第一是子类SubclassA和SubclassB都要实现其拷贝构造函数,再实现一个clone函数,clone函数调用拷贝构造函数new一个新的对象出来返回即可,是不是很简单。那么为什么clone要包装拷贝构造函数?它和拷贝构造函数有什么区别呢?
最显著的区别就是,clone函数是一个虚函数!它利用了语言的动态绑定性质。如果直接使用基类的拷贝构造函数,那么只能得到一个基类的对象。上面代码中的p使用拷贝构造函数拷贝了pa指向的对象,但输出结果可以看到,只拷贝了基类,没有拷贝任何子类的信息。这不是我们想要的拷贝。
而如果使用clone函数,虽然拿到的是基类的指针,但执行的时候确实会调用到子类的功能(上面代码的输出都可以看到Prototype指针执行虚函数的时候输出的是子类虚函数的结果)。这样看来,我们确实在SubclassA不可见的情况下复制了它。

小结

当一个类是可见的情况下,我们可以直接通过复制对象成员或调用拷贝构造函数来获得一个与被拷贝对象一样的对象。
当一个类是不可见,但是能够通过基类知道子类实现了哪些API的情况下,我们可以通过原型模式来拷贝一个新对象,而且这个新对象和被拷贝的子类对象一样。
原型模式需要不可见类的设计者提供一个基类,实现子类的拷贝构造函数和clone虚函数。而不是客户端来实现,它只需要通过clone函数复制即可。

标签:name,clone,模式,原型,基类,SubclassA,解析,拷贝,构造函数
From: https://www.cnblogs.com/winteryan/p/18061105

相关文章

  • 软件工程开发模式
    软件工程开发模式有多种,以下是一些常见的模式:瀑布模型(WaterfallModel):这是一种线性的软件开发过程,它按照一系列有序的阶段进行,每个阶段都有明确的任务和输出。在瀑布模型中,设计、需求分析、编码、测试和部署等阶段是顺序进行的,每个阶段完成后才能进入下一个阶段。迭代模型(Iter......
  • Axure Cloud如何给每个原型配置私有域名
    需求在原型发布之后,自动给原型生成一个独立访问的域名,类似http://u591bi.axshare.bushrose.cn,应该如何配置呢?准备事项已备案域名如何备案?阿里云备案流程已安装部署AxureCloud如何安装部署,请参考另外一篇文章,AxureCloud私有云最新版破解(AxureCloudforBusinessOn-P......
  • 04_抽象工厂模式
        抽象工厂模式是一种创建型设计模式,它提供一个接口用于创建一系列相关或相互依赖对象的工厂,而不需要指定具体的类。这种模式通过提供一个抽象的工厂接口,使得客户端可以创建一系列产品对象而无需关心具体的实现细节。    在抽象工厂模式中,通常会定义一个抽象工......
  • 03_工厂方法模式
    工厂方法模式是一种创建型设计模式,用于定义一个创建对象的接口,但将实际创建对象的工作延迟到子类中。这样可以使一个类的实例化延迟到其子类中,从而实现解耦和灵活性。在工厂方法模式中,通常包含以下几个角色:抽象工厂(Creator):定义创建对象的接口,通常包含一个抽象的工厂方法,由子类......
  • 02_简单工厂模式
    简单工厂模式(SimpleFactoryPattern)是一种创建型设计模式,属于工厂模式的一种。在简单工厂模式中,通过一个工厂类来负责创建对象实例,而客户端无需直接创建对象,只需要通过工厂类来获取所需的对象。简单工厂模式包含以下几个角色:工厂类(Factory):负责创建对象实例的类。产品类(Produc......
  • 01_单例模式
    单例模式是一种常见的设计模式,用于确保一个类只有一个实例,并提供一个全局访问点。在单例模式中,类会提供一个静态方法来获取其唯一实例,如果该实例不存在则会创建一个新实例,否则返回已有的实例。publicsealedclassCounter{publicCounter(){......
  • 前端设计模式大全
    1.工厂模式工厂模式(FactoryPattern):将对象的创建和使用分离,由工厂类负责创建对象并返回。在前端开发中,可以使用工厂模式来动态创建组件。前端中的工厂模式是一种创建对象的设计模式,它可以让我们封装创建对象的细节,我们使用工厂方法而不是直接调用new关键字来创建对象,使得......
  • Sharding-JDBC源码解析与vivo的定制开发
    作者:vivoIT平台团队-XiongHuanxinSharding-JDBC是在JDBC层提供服务的数据库中间件,在分库分表场景具有广泛应用。本文对Sharding-JDBC的解析、路由、改写、执行、归并五大核心引擎进行了源码解析,并结合业务实践经验,总结了使用Sharding-JDBC的一些痛点问题并分享了对应的定......
  • 理解Saga模式:分布式事务的优雅解决方案
    理解Saga模式:分布式事务的优雅解决方案在微服务架构中,系统通常被拆分成多个独立的服务,每个服务管理着自己的数据和逻辑。这种拆分带来了灵活性和可扩展性,但同时也引入了分布式事务管理的挑战。传统的事务管理方法,如数据库的ACID(原子性、一致性、隔离性、持久性)事务,不再适用于跨多......
  • 观察者模式
    定义观察者模式(ObserverPattern):定义对象间一种一对多的依赖关系,使得当每一个对象改变状态,则所有依赖于它的对象都会得到通知并自动更新。观察者模式是一种对象行为型模式。观察者模式包含两个角色:主题(Subject):被观察的对象,它维护了一个观察者列表,可以添加、删除观察者,以及......