原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。
例如,一个对象需要在一个高代价的数据库操作之后被创建。
我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。
我们相当于使用原型模式对一个类的实例创建一个一摸一样的副本,而我们可以重新对这个副本进行修改。
UML图
该模式中包含的角色及其职责
抽象原型类:规定了具体原型对象必须实现的接口。
具体原型类:实现抽象原型类的 clone() 方法,它是可被复制的对象。
角色实例生活例子抽象原型类PrototypeInterface手机模具抽象具体原型类Phone具体模具
主要解决的问题
高性能创建多个相同或者类似的对象实例。
使用场景
- 资源优化场景。
- 类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。
- 性能和安全要求的场景。
- 通过 new 产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。
- 一个对象多个修改者的场景。
- 一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。
- 在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过 clone 的方法创建一个对象,然后由工厂方法提供给调用者。
优缺点
优点
- 扩展性好,由于原型模式提供了抽象原型类,在客户端针对抽象原型类进行编程,而将具体原型类写到配置文件中,增减或减少产品对原有系统都没有影响。
- 当创建对象的实例较为复杂的时候,使用原型模式可以简化对象的创建过程,通过复制一个已有的实例可以提高实例的创建效率。
- 可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份并将其状态保存起来,以便在需要的时候使用(例如恢复到历史某一状态),可辅助实现撤销操作。
- 创建多个相同或者类似的对象实例clone的性能比new更高
缺点
- 实现原型模式每个派生类都必须实现 Clone接口
- 需要为每一个类配置一个克隆方法,而且该克隆方法位于类的内部,当对已有类进行改造的时候,需要修改代码,违反了开闭原则。
- 在实现深克隆时需要编写较为复杂的代码,而且当对象之间存在多重签到引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来会比较麻烦。
实现思路
定义一个抽象类,所有具体类都需要继承或者实现这个抽象类都方法,里面都方法调用clone进行拷贝副本然后再返回。
代码背景
一家工厂生产手机套,在没有的时候需要把一个对应的手机型号模具做出来(相当于我们创建实例的new),所有这个手机型号的手机壳再根据这个模具来制作。
但是当我们有了一个模具之后其他的手机壳只要使用这个模具就可以快速的制造出来一个新的手机壳。
示例代码:
PrototypeInterface.php
interface PrototypeInterface
{
public function copy();
}
P30.php
class P30 implements PrototypeInterface
{
private $model;
private $color;
/**
* Sunny constructor.
* @param $model
* @param $color
*/
public function __construct($model, $color)
{
$this->model = $model;
$this->color = $color;
}
/**
* @return mixed
*/
public function getModel()
{
return $this->model;
}
/**
* @param mixed $model
*/
public function setModel($model): void
{
$this->model = $model;
}
/**
* @return mixed
*/
public function getColor()
{
return $this->color;
}
/**
* @param mixed $color
*/
public function setColor($color): void
{
$this->color = $color;
}
/**
* 实现原型clone方法
*/
public function copy()
{
return clone $this;
}
}
手机模具定义了两个属性,一个手机型号,一个颜色。可以用来生产不同颜色的手机壳。
调用代码:
$phone = new P30("sunny","黑色");
print_r($phone);
输出结果:
P30 Object
(
[model:P30:private] => sunny
[color:P30:private] => 黑色
)$phone = new P30("sunny","黑色");
echo "---------phone-----------\n";
print_r($phone);
$phone1 = $phone;
$phone1->setColor("白色");
echo "---------phone-----------\n";
print_r($phone);//注意这里打印的是$phone而不是$phone1
这样看代码没有什么问题,我们再来看一个代码。
调用代码:
$phone = new P30("sunny","黑色");
echo "---------phone-----------\n";
print_r($phone);
$phone1 = $phone;
$phone1->setColor("白色");
echo "---------phone-----------\n";
print_r($phone);//注意这里打印的是$phone而不是$phone1
输出结果:
echo "---------phone-----------\n";
P30 Object
(
[model:P30:private] => sunny
[color:P30:private] => 黑色
)
echo "---------phone-----------\n";
P30 Object
(
[model:P30:private] => sunny
[color:P30:private] => 白色
)
看第二个打印输出,我们直接使用赋值的方式来赋值给一个新的变量,原来的那个变量也被改变了。为了解决这样的问题我们可以调用copy方法解决。
$phone = new P30("sunny","黑色");
echo "---------phone-----------\n";
print_r($phone);
$phone1 = $phone->copy();
$phone1->setColor("白色");
echo "---------phone-----------\n";
print_r($phone);
echo "---------phone1-----------\n";
print_r($phone1);
输出结果:
---------phone-----------
P30 Object
(
[model:P30:private] => sunny
[color:P30:private] => 黑色
)
---------phone-----------
P30 Object
(
[model:P30:private] => sunny
[color:P30:private] => 黑色
)
---------phone1-----------
P30 Object
(
[model:P30:private] => sunny
[color:P30:private] => 白色
)
调用了copy之后里面的属性全部都一致,我们也可以再次进行修改而不会影响之前的对象,我们修改的只是一份一样的副本而已。
如果觉得文章还不错,请把文章分享给更多的人学习,在文章中发现有误的地方也希望各位指出更正。现有误的地方也希望各位指出更正。
相关文章
本站已关闭游客评论,请登录或者注册后再评论吧~