从0开始学PHP面向对象内容之常用设计模式(建造者,原型)
一、创建型设计模式
3、建造者模式(Builder)
建造者模式(Builder Pattern)是一种创建型设计模式,他将一个复杂对象的构建过程与其表示分离,是的同样的构建过程可以创建不同的表示。
这种模式尤其适合创建复杂对象(包含多个组件),并且允许按照步骤逐步构建的对象
结构
1、Product(产品):要创建的复杂对象,由多个部件组成。
2、Builder(抽象建造者):定义创建产品步骤和接口
3、ConcreteBuilder(具体建造者):实现Builder接口,具体化构建的每一步,最终返回产品
4、Director(指挥官):控制建造过程,将复杂对象的构造步骤封装起来
场景说明
比如我们要生产一辆汽车,汽车的部分部件有车轮,引擎,车门等
那么汽车就是产品,
生产车轮等零件的工人是建造者,
分配具体的人员(部门)是具体建造者
决定是先造车轮还是造车门的人就是指挥者负责控制建造顺序
以下是代码实例
第一步:定义产品(Product)
class Car {
public $wheels;
public $engine;
public $doors;
public function showParts() {
return "Car with {$this->wheels} wheels, {$this->engine} engine, {$this->doors} doors.";
}
}
第二步:定义建造者接口(Builder)
interface CarBuilder {
public function addWheels();
public function addEngine();
public function addDoors();
public function getCar();
}
第三步:创建具体建造者(ConcreteBuilder)
class SportsCarBuilder implements CarBuilder {
private $car;
public function __construct() {
$this->car = new Car();
}
public function addWheels() {
$this->car->wheels = "4 sports wheels";
}
public function addEngine() {
$this->car->engine = "V8 engine";
}
public function addDoors() {
$this->car->doors = "2 doors";
}
public function getCar() {
return $this->car;
}
}
class SUVCarBuilder implements CarBuilder {
private $car;
public function __construct() {
$this->car = new Car();
}
public function addWheels() {
$this->car->wheels = "4 off-road wheels";
}
public function addEngine() {
$this->car->engine = "V6 engine";
}
public function addDoors() {
$this->car->doors = "4 doors";
}
public function getCar() {
return $this->car;
}
}
第四步:定义指挥者(Director)
class CarDirector {
private $builder;
public function __construct(CarBuilder $builder) {
$this->builder = $builder;
}
public function buildCar() {
$this->builder->addWheels();
$this->builder->addEngine();
$this->builder->addDoors();
return $this->builder->getCar();
}
}
下面我们就可以造车了
// 构建一辆跑车
$sportsCarBuilder = new SportsCarBuilder();
$director = new CarDirector($sportsCarBuilder);
$sportsCar = $director->buildCar();
echo $sportsCar->showParts(); // 输出:Car with 4 sports wheels, V8 engine, 2 doors.
// 构建一辆SUV
$suvCarBuilder = new SUVCarBuilder();
$director = new CarDirector($suvCarBuilder);
$suvCar = $director->buildCar();
echo $suvCar->showParts(); // 输出:Car with 4 off-road wheels, V6 engine, 4 doors.
是不是觉得上述有点冗长了?但是标准化的,对于简单的构建来说,我们可以使用建造者模式的变体:链式调用
示例:
class SimpleCar {
private $wheels;
private $engine;
private $doors;
public function setWheels($wheels) {
$this->wheels = $wheels;
return $this;
}
public function setEngine($engine) {
$this->engine = $engine;
return $this;
}
public function setDoors($doors) {
$this->doors = $doors;
return $this;
}
public function showParts() {
return "SimpleCar with {$this->wheels} wheels, {$this->engine} engine, {$this->doors} doors.";
}
}
// 使用
$simpleCar = (new SimpleCar())
->setWheels('4 basic wheels')
->setEngine('Basic engine')
->setDoors('4 doors');
echo $simpleCar->showParts(); // 输出:SimpleCar with 4 basic wheels, Basic engine, 4 doors.
总结
建造者模式是一种非常灵活且结构化的模式,适合用于复杂对象的创建,尤其当对象包含多个部分且构造顺序很重要时。
通过将构造细节隐藏在 Builder 和 Director 内部,建造者模式为开发者提供了更高层次的代码抽象,极大地提升了代码的维护性和可扩展性。
优点:
1、清晰的构建过程:将复杂对象的创建步骤分开,逻辑清晰。
2、高度可扩展:可以通过添加新的具体建造者来构造不同的产品。
3、提高复用性:构建步骤可以被不同的建造者复用。
缺点:
增加了类的数量:每种具体产品需要对应一个具体建造者。
不适合简单对象:对于属性较少的对象,建造者模式显得多余。
但是我个人感觉它和工厂模式有点像,但是还是有区别的,具体的看下图,设计模式是死的,大家灵活运用
举个例子
1、如果需要创建的对象简单,且关注类型切换,选择工厂模式。比如生产不同类型的商品,创建数据库连接对象。
2、如果需要构造复杂对象,且关注步骤或构造细节,选择建造者模式。比如创建一辆汽车、一座房子,或生成复杂文档。
当然两者可以结合使用,比如工厂模式用于选择具体的建造者,建造者模式完成对象的详细构造过程。
4、原型模式(Prototype)
定义:
原型模式(Prototype Pattern) 是一种创建型设计模式,它允许通过复制现有对象来创建新对象,而不是重新实例化类。这种模式对创建成本高或初始化复杂的对象非常有用。
核心概念:
原型模式的目标:通过复制一个已有的对象(称为原型)来创建新对象,而无需了解创建的细节
原型模式的关键:使用现有对象作为蓝本,并对其进行克隆。
结构
1、 Prototype(原型接口):定义一个克隆方法,让对象能够复制自身。
2、 ConcretePrototype(具体原型类):实现克隆方法,返回自身的深拷贝或浅拷贝。
3、Client(客户端):通过调用克隆方法创建新对象,而不直接依赖构造函数。
深拷贝与浅拷贝的区别
浅拷贝:只复制对象本身,而不复制内部引用的对象。可以通过模式方法__clone实现
深拷贝:不仅复制对象本身,还递归复制内部引用的对象。通常需要手动实现深拷贝逻辑。
class PrototypeExample {
public $value;
public function __construct($value) {
$this->value = $value;
}
public function __clone() {
// 深拷贝逻辑
$this->value = clone $this->value;
}
}
$original = new PrototypeExample((object)["data" => 123]);
$shallowClone = clone $original; // 浅拷贝
$shallowClone->value->data = 456;
echo $original->value->data; // 输出 456(浅拷贝影响原对象)
// 深拷贝的场景可以通过自定义 __clone 实现
代码示例
第一步:定义抽象原型
abstract class Prototype {
public $name;
public function __construct($name) {
$this->name = $name;
}
// 定义抽象克隆方法
abstract public function __clone();
}
第二步:创建具体原型类
class ConcretePrototype extends Prototype {
public $data;
public function __construct($name, $data) {
parent::__construct($name);
$this->data = $data;
}
// 实现克隆方法
public function __clone() {
// 深拷贝或浅拷贝逻辑
$this->data = clone $this->data;
}
}
第三步:客户端
// 初始化一个具体原型对象
$original = new ConcretePrototype("Original Object", (object)["value" => 123]);
// 克隆对象
$cloned = clone $original;
// 修改克隆对象的属性
$cloned->name = "Cloned Object";
$cloned->data->value = 456;
// 输出
echo $original->name . " - " . $original->data->value . "\n"; // Original Object - 123
echo $cloned->name . " - " . $cloned->data->value . "\n"; // Cloned Object - 456
总结
原型模式的核心是通过复制现有对象快速创建新对象,而非重新实例化。
优点:
1、性能优化:复制现有对象比重新初始化更快,适合资源消耗大的对象创建场景。
2、简化对象创建:隐藏复杂的构造逻辑,客户端只需调用克隆方法。
3、动态对象创建:无需提前知道对象的具体类型,适合多变场景。
缺点:
1、实现复杂:深拷贝的实现可能较复杂,尤其是对象内部嵌套多层引用时。
2、内存管理问题:如果对象过大,频繁克隆会增加内存开销。
3、副作用风险:浅拷贝可能导致修改克隆对象时影响原对象。
所以适用场景:
1、对象创建成本高:例如数据库连接池、配置加载等场景。
2、需要创建对象的不同版本:例如游戏中的多个角色模板、文档生成等。
3、需要隐藏复杂的对象初始化过程。