欢迎大家关注我的公众号,以后发布的文章会同步在csdn与公众号上!
上一篇文章我们介绍了设计模式的创建型模式中单例与多例模式,本篇文章我们将介绍设计模式中创建型模式的原型模式。
原型模式
原型模式的定义及特点
原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。
原型模式的优点
-
性能优良 原型模式是在内存二进制流的拷贝,要比直接new一个对象性能好很多,特别是要在一个循环体内产生大量的对象时,原型模式可以更好地体现其优点。
-
逃避构造函数的约束 这既是它的优点也是缺点,直接在内存中拷贝,构造函数是不会执行的。优点就是减少了约束,缺点也是减少了约束,需要大家在实际应用时考虑。
原型模式的缺点
-
需要为每一个类配置一个克隆方法,而且该克隆方法位于类的内部,当对已有类进行改造的时候,需要修改代码,违反了开闭原则。
-
在实现深克隆时需要编写较为复杂的代码,而且当对象之间存在多重签到引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来会比较麻烦。
-
由于使用原型模式复制对象时不会调用类的构造方法,所以原型模式无法和单例模式组合使用。
浅克隆和深克隆
-
浅克隆:当原型对象被复制时,只复制它本身和其中包含的值类型的成员变量,而引用类型的成员变量并没有复制。
-
深克隆:除了对象本身被复制外,对象所包含的所有成员变量也将被复制。
-
深克隆与浅克隆的区别就是,浅克隆不会克隆原对象中的引用类型,仅仅拷贝了引用类型的指向。深克隆则拷贝了所有。也就是说深克隆能够做到原对象和新对象之间完全没有影响。
浅克隆和深克隆
利用序列化来做深拷贝,把对象写到流里的过程是序列化的过程,这一过程称为“冷冻”或“腌咸菜”,反序列化对象的过程叫做“解冻”或“回鲜”。
原型模式的使用场景
-
资源优化场景。
-
类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。
-
性能和安全要求的场景(通过new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式)。
-
通过 new 产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。
-
一个对象多个修改者的场景。
-
一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。
-
对对象状态进行记录。
-
创建对象成本j较大。
-
在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过 clone 的方法创建一个对象,然后由工厂方法提供给调用者。
原型模式的注意事项
-
构造函数不会被执行
注意浅拷贝 -
深拷贝和浅拷贝建议不要混合使用,特别是在涉及类的继承时,父类有多个引用的情况就非常复杂,建议的方案是深拷贝和浅拷贝分开实现。
-
深拷贝还有一种实现方式就是通过自己写二进制流来操作对象,然后实现对象的深拷贝,这个大家有时间自己实现一下
-
使用原型模式时,引用的成员变量必须满足两个条件才不会被拷贝:一是类的成员变量,而不是方法内变量;二是必须是一个可变的引用对象,而不是一个原始类型或不可变对象。
简单实现
<?php
abstract class ProductPrototype
{
/**
* @var string
*/
protected $title;
/**
* @var string
*/
protected $category;
abstract public function __clone();
public function getTitle(): string {
return $this->title;
}
public function setTitle($title) {
$this->title = $title;
}
}
class BarProductPrototype extends ProductPrototype
{
/**
* @var string
*/
protected $category = 'Bar';
public function __clone() {
}
}
class FooProductPrototype extends ProductPrototype
{
/**
* @var string
*/
protected $category = 'Foo';
public function __clone() {
}
}
$fooPrototype = new FooProductPrototype();
$barPrototype = new BarProductPrototype();
for ($i = 0; $i < 10; $i++) {
$foo = clone $fooPrototype;
$foo->setTitle('Foo Book No ' . $i);
if ($foo instanceof FooProductPrototype) {
echo "ok" . $foo->getTitle();;
}
}
for ($i = 0; $i < 5; $i++) {
$bar = clone $barPrototype;
$bar->setTitle('Bar Book No ' . $i);
if ($foo instanceof BarProductPrototype) {
echo "ok" . $bar->getTitle();;
}
}
以上我们介绍了创建型模式中的原型模式,下一篇将会介绍创建型模式中的工厂方法模式。
感谢阅读!
喜欢本文的朋友,欢迎关注“isevena”