设计模式 原型模式_现代c的原型设计模式

设计模式 原型模式

Prototype Design Pattern is a Creational Design Pattern that helps in the prototyping(creating/copying cheaply) of an object using separate methods or polymorphic classes. You can consider the prototype as a template of an object before the actual object is constructed. In this article of the Creational Design Patterns, we’re going to take a look at why we need a Prototype Design Pattern in C++ i.e. motivation, prototype factory & leveraging prototype design pattern to implement virtual copy constructor.

原型设计模式是一种创新设计模式,它可以使用单独的方法或多态类帮助对对象进行原型设计(廉价地创建/复制) 。 您可以在构造实际对象之前将原型视为对象的模板 。 在本文的创新设计模式中,我们将了解为什么我们需要C ++中的原型设计模式,即动机,原型工厂和利用原型设计模式来实现虚拟副本构造函数

/!\: This article has been originally published on my blog. If you are interested in receiving my latest articles, please sign up to my newsletter.

/!\:本文最初发布在我的博客上。 如果您有兴趣接收我的最新文章, 请注册我的新闻通讯

By the way, If you haven’t check out my other articles on Creational Design Patterns, then here is the list:

顺便说一句,如果您还没有阅读我的其他有关Creational Design Patterns的文章,那么这里是列表:

  1. Factory

  2. Builder

    建造者

  3. Prototype

    原型

  4. Singleton

    辛格尔顿

The code snippets you see throughout this series of articles are simplified not sophisticated. So you often see me not using keywords like override, final, public(while inheritance) just to make code compact & consumable(most of the time) in single standard screen size. I also prefer struct instead of class just to save line by not writing "public:" sometimes and also miss virtual destructor, constructor, copy constructor, prefix std::, deleting dynamic memory, intentionally. I also consider myself a pragmatic person who wants to convey an idea in the simplest way possible rather than the standard way or using Jargons.

您在本系列文章中看到的代码段是简化而不是复杂的。 因此,您经常看到我不使用诸如overridefinalpublic (在继承时)之类的关键字只是为了使代码在大多数标准屏幕尺寸内紧凑且可消耗(大多数时候)。 我也更喜欢使用struct而不是class来节省行,因为有时不编写“ public: ”,并且还会错过虚拟析构函数 ,构造函数, 复制构造函数 ,前缀std:: ,故意删除动态内存。 我也认为自己是一个务实的人,他希望以最简单的方式而不是标准的方式或使用术语来传达想法。

Note:

注意:

  • If you stumbled here directly, then I would suggest you go through What is design pattern? first, even if it is trivial. I believe it will encourage you to explore more on this topic.

    如果您直接在这里偶然发现,那么我建议您经历一下什么是设计模式? 首先,即使是微不足道的。 我相信它将鼓励您探索有关此主题的更多信息。

  • All of this code you encounter in this series of articles are compiled using C++20(though I have used Modern C++ features up to C++17 in most cases). So if you don’t have access to the latest compiler you can use https://wandbox.org/ which has preinstalled boost library as well.

    在本系列文章中遇到的所有这些代码都是使用C ++ 20编译的(尽管在大多数情况下,我使用的是Modern C ++的功能,直到C ++ 17为止)。 因此,如果您无权访问最新的编译器,则可以使用https://wandbox.org/ ,它也已预装了boost库。

意图 (Intent)

To create a new object cheaply with the help of an already constructed or pre-initialized stored object.

在已经构造或预先初始化的存储对象的帮助下廉价地创建新对象。

Image for post
  • The prototype provides flexibility to create complex object cheaply. The concept is to copy an existing object rather than creating a new instance from scratch, something that may include costly operations.

    原型提供了灵活性,可以廉价地创建复杂的对象。 其概念是复制现有对象,而不是从头开始创建新实例,这可能包括昂贵的操作。
  • The existing object then acts as a prototype & newly copied object may change the same properties only if required. This approach saves costly resources and time, especially when the object creation is a heavy process.

    然后,现有对象将充当原型,新复制的对象只有在需要时才可以更改相同的属性。 这种方法节省了昂贵的资源和时间,尤其是在对象创建很繁重的过程中。
  • So essentially the prototype is quite simply a partially or fully initialized object that you make a copy of. And then you subsequently use for your own benefit with variations.

    因此,从本质上讲,原型只是一个复制了一部分或全部初始化的对象。 然后,您可以利用自己的利益进行变更。

动机 (Motivation)

struct Office {
string m_street;
string m_city;
int32_t m_cubical; Office(string s, string c, int32_t n):m_street(s), m_city(c), m_cubical(n){}
};struct Employee {
string m_name;
Office m_office; Employee(string n, Office o):m_name(n), m_office(o){}
};int main() {
Employee john{ "John Doe", Office{"123 East Dr", "London", 123} };
Employee jane{ "Jane Doe", Office{"123 East Dr", "London", 124} };
Employee jack{ "jack Doe", Office{"123 ORR", "Bangaluru", 300} };
return EXIT_SUCCESS;
}
  • This is not the right approach as you have to write the main office address again & again for each employee detail. This is cumbersome & become more when you want to create an employee list. Moreover, consider the situation when your main office moved to another address.

    这是不正确的方法,因为您必须为每个员工详细信息再次写总办公室地址。 这很麻烦,并且在您要创建员工列表时会变得更加麻烦。 此外,请考虑您的总公司搬到另一个地址的情况。

C ++中的原型设计模式示例 (Prototype Design Pattern Examples in C++)

  • A more pragmatic approach would be like this :

    更加实用的方法是这样的:
struct Employee {
string m_name;
const Office* m_office; Employee(string n, Office *o):m_name(n), m_office(o){}
};static Office LondonOffice{"123 East Dr", "London", 123};
static Office BangaluruOffice{"RMZ Ecoworld ORR", "London", 123};int main() {
Employee john{ "John Doe", &LondonOffice };
Employee jane{ "Jane Doe", &LondonOffice };
Employee jack{ "jack Doe", &BangaluruOffice };
return EXIT_SUCCESS;
}
  • Above solution is suitable for our use case but sometimes we want to customize that office address. And when it comes to pointers & references and any sort of indirection, ordinary copying using operator equals quite simply does not work.

    以上解决方案适用于我们的用例,但有时我们想要自定义该办公地址。 当涉及到指针和引用以及任何形式的间接访问时,使用运算符equals进行的普通复制非常不起作用。
  • A standard way to implement this is by implementing the copy constructor

    一种标准的实现方法是通过实现复制构造函数

原型工厂 (Prototype Factory)

  • So in the previous example of the Prototype Design Pattern, we basically had a global object for office addresses and used their address for creating prototypes.

    因此,在前面的原型设计模式示例中,我们基本上有一个用于办公地址的全局对象 ,并使用其地址创建原型。

  • Now, this isn’t particularly convenient to the consumers of your API because you might want to give them a prototype to work with. And you should explicit enough in terms of letting people know there is the only a unified way by which they create instances from a prototype and so that they cannot make individual instances by themselves.

    现在,这对于API的使用者来说并不是特别方便,因为您可能想给他们提供一个可以使用的原型。 而且,您应该让人们知道,从原型创建实例的唯一统一方法是足够明确,这样他们就不能自己创建单个实例。
  • And in this case, what you would build is off-course is a Prototype Factory:

    在这种情况下,您将要构建的是原型工厂:
struct Office {
string m_street;
string m_city;
int32_t m_cubical;
};class Employee {
string m_name;
Office* m_office; // Private constructor, so direct instance can not be created except for `class EmployeeFactory`
Employee(string n, Office *o) : m_name(n), m_office(o) {}
friend class EmployeeFactory;public:
Employee(const Employee &rhs) : m_name{rhs.m_name}, m_office{new Office{*rhs.m_office}}
{ } Employee& operator=(const Employee &rhs) {
if (this == &rhs) return *this;
m_name = rhs.m_name;
m_office = new Office{*rhs.m_office};
return *this;
} friend ostream &operator<<(ostream &os, const Employee &o) {
return os << o.m_name << " works at "
<< o.m_office->m_street << " " << o.m_office->m_city << " seats @" << o.m_office->m_cubical;
}
};class EmployeeFactory {
static Employee main;
static Employee aux;
static unique_ptr<Employee> NewEmployee(string n, int32_t c, Employee &proto) {
auto e = make_unique<Employee>(proto);
e->m_name = n;
e->m_office->m_cubical = c;
return e;
}public:
static unique_ptr<Employee> NewMainOfficeEmployee(string name, int32_t cubical) {
return NewEmployee(name, cubical, main);
}
static unique_ptr<Employee> NewAuxOfficeEmployee(string name, int32_t cubical) {
return NewEmployee(name, cubical, aux);
}
};// Static Member Initialization
Employee EmployeeFactory::main{"", new Office{"123 East Dr", "London", 123}};
Employee EmployeeFactory::aux{"", new Office{"RMZ Ecoworld ORR", "London", 123}};int main() {
auto jane = EmployeeFactory::NewMainOfficeEmployee("Jane Doe", 125);
auto jack = EmployeeFactory::NewAuxOfficeEmployee("jack Doe", 123);
cout << *jane << endl << *jack << endl;
return EXIT_SUCCESS;
}
/*
Jane Doe works at 123 East Dr London seats @125
jack Doe works at RMZ Ecoworld ORR London seats @123
*/
  • The subtle thing to note here is the private constructor of Employee & friend EmployeeFactory. This is how we enforce the client/API-user to create an instance of Employee only through EmployeeFactory .

    这里要注意的细微事情是Employee和其friend EmployeeFactory的私有构造函数。 这就是我们强制客户端/ API用户仅通过EmployeeFactory创建Employee实例的方式。

利用原型设计模式实现虚拟副本构造器 (Leveraging Prototype Design Pattern to Implement Virtual Copy Constructor)

  • In C++, Prototype is also useful to create a copy of an object without knowing its concrete type. Hence, it is also known as Virtual Copy Constructor.

    在C ++中,原型也可用于创建对象的副本而无需知道其具体类型。 因此,它也称为虚拟副本构造器。

问题 (Problem)

  • C++ has the support of polymorphic object destruction using it’s base class’s virtual destructor. Equivalent support for creation and copying of objects is missing as С++ doesn’t support virtual constructor & virtual copy constructors.

    C ++使用基类的虚拟析构函数来支持多态对象销毁 。 由于С++不支持虚拟构造函数和虚拟副本构造函数,因此缺少对对象创建和复制的等效支持。

  • Moreover, you can’t create an object unless you know its static type, because the compiler must know the amount of space it needs to allocate. For the same reason, copy of an object also requires its type to known at compile-time.

    此外,除非知道对象的静态类型,否则您将无法创建对象,因为编译器必须知道其需要分配的空间量。 出于相同的原因,对象的副本还需要在编译时将其类型设为已知。
  • Consider the following example as problem statement:

    考虑以下示例作为问题陈述:
struct animal {
virtual ~animal(){ cout<<"~animal\n"; }
};struct dog : animal {
~dog(){ cout<<"~dog\n"; }
};struct cat : animal {
~cat(){ cout<<"~cat\n"; }
};void who_am_i(animal *who) { // not sure whether dog would be passed here or cat
// How to `create` the object of same type i.e. pointed by who ?
// How to `copy` object of same type i.e. pointed by who ?
delete who; // you can delete appropriate object pointed by who, thanks to virtual destructor
}

(Solution)

  • The Virtual Constructor/Copy-Constructor technique allows polymorphic creation & copying of objects in C++ by delegating the act of creation & copying the object to the derived class through the use of virtual methods.

    虚拟构造器/复制构造器技术通过使用虚拟方法委托创建和将对象复制到派生类的行为,来允许C ++中对象的多态创建和复制。
  • Following code is not only implements virtual copy constructor (i.e. clone()) but also implement virtual constructor(i.e. create()).

    以下代码不仅实现虚拟副本构造函数 (即clone() ),而且还实现虚拟构造函数(即create() )。

struct animal {
virtual ~animal() = default;
virtual std::unique_ptr<animal> create() = 0;
virtual std::unique_ptr<animal> clone() = 0;
};struct dog : animal {
std::unique_ptr<animal> create() { return std::make_unique<dog>(); }
std::unique_ptr<animal> clone() { return std::make_unique<dog>(*this); }
};struct cat : animal {
std::unique_ptr<animal> create() { return std::make_unique<cat>(); }
std::unique_ptr<animal> clone() { return std::make_unique<cat>(*this); }
};void who_am_i(animal *who) {
auto new_who = who->create();// `create` the object of same type i.e. pointed by who ?
auto duplicate_who = who->clone(); // `copy` object of same type i.e. pointed by who ?
delete who;
}

原型设计模式的好处 (Benefits of Prototype Design Pattern)

  1. Prototypes are useful when the object instantiation is expensive, thus avoid expensive “creation from scratch”, and support cheap cloning of a pre-initialized prototype.

    当对象实例化很昂贵时,原型很有用,从而避免了昂贵的“从头创建”,并支持廉价地克隆预初始化的原型。
  2. The prototype provides the flexibility to create highly dynamic systems by defining new behaviour through object composition & specifying values for an object’s data members at the time of instantiation unlike defining new classes.

    该原型通过定义对象组成的新行为并在实例化时为对象的数据成员指定值而提供了创建高度动态系统的灵活性,这与定义新类不同。

  3. You can simplify the system by producing complex objects more conveniently.

    您可以通过更方便地生成复杂的对象来简化系统。
  4. Especially in C++, Prototype Design Pattern is helpful in creating copy of an object without even knowing its type.

    尤其是在C ++中,原型设计模式在不知道对象类型的情况下有助于创建对象的副本。

常见问题汇总 (Summary by FAQs)

Q. What’s the point of using the Prototype Design Pattern?

问:使用原型设计模式有什么意义?

  • To create an object rapidly based on cloning a pre-configured object.

    基于克隆预配置对象快速创建对象。
  • Useful to remove a bunch of boilerplate code.

    删除一堆样板代码很有用。
  • Handly while working with object without knowing its type.

    在不知道对象类型的情况下处理对象时会很费力。
  • Prototype Design Pattern is an obvious choice while you are working with the Command Design Pattern. For example, in HTTP request most of the time header & footer content remains the same, what changes are data. In such a scenario, you should not create an object from scratch. Rather leverage Prototype Design Pattern.

    使用Command Design Pattern时, Prototype Design Pattern是一个显而易见的选择。 例如,在HTTP请求中,大多数时间的页眉和页脚内容保持不变,什么是数据更改。 在这种情况下,您不应从头开始创建对象。 而是利用原型设计模式。

Q. Is the Prototype Design Pattern Really Just Clone?

问:原型设计模式真的只是克隆吗?

It isn’t if you combine it with the Factory Design Pattern.

不是将它与Factory Design Pattern结合使用。

Q. Prototype design pattern to be used when creation is costly, but we do create in the clone.

问:创建成本很高时要使用的原型设计模式,但我们确实在克隆中创建。

You must be wondering that in Prototype Factory we show above, we are creating instances in the copy constructor. Isn’t that expensive. Yes, it is. But just think about HTTP request, its header consist version, encoding type, content type, server-type, etc. Initially, you need a find out these parameters using respective function calls. But once you got these, these are not going to change until connection closed. So there is no point in doing function calls to extract these params over & over. What cost us here is not parameters but their functions to extract value.

您一定想知道,在上面显示的Prototype Factory中,我们在复制构造函数中创建实例。 没那么贵。 是的。 但是考虑一下HTTP请求,它的标头包括版本,编码类型,内容类型,服务器类型等。最初,您需要使用相应的函数调用来找出这些参数。 但是一旦获得这些,这些都不会改变,直到关闭连接。 因此,进行函数调用一遍又一遍地提取这些参数毫无意义。 我们在这里付出的代价不是参数,而是它们提取价值的功能。

Have Any Suggestions, Query or Wants to Say Hi? Take the Pressure Off, You Are Just a Click Away.🖱️

有任何建议,查询或想要说 Hi 减轻压力,您只需单击即可。 🖱️

翻译自: https://medium.com/dev-genius/prototype-design-pattern-in-modern-c-49a27cbf4b1

设计模式 原型模式

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值