php进阶教程设计模式之原型模式(PHP进阶教程-设计模式之建造者模式)
php进阶教程设计模式之原型模式(PHP进阶教程-设计模式之建造者模式)没有使用建造者模式的代码:通过建造者模式来创建一家公司,在没有使用建造者模式的时候我们通常使用基本上都是如下:在本文例子表示以组建公司为背景来编写代码,在创建一间公司我们通常按照不同分工来分成不同的部门,在这里身为公司领导(Director) 并不需要去管理每个部门处理的详细细节,只要跟部门负责人沟通就好了。例如组建一个科技公司大概需要的部门:技术部门、销售部门、人事部门、财务部门
造者模式(Builder Pattern):将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
建造者模式是一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的具体构建细节。建造者模式属于对象创建型模式。
将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象,每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者,符合“开闭原则”,还可以更加精细地控制产品的创建过程;其主要缺点在于由于建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,因此其使用范围受到一定的限制,如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。
UML图
该模式中包含的角色及其职责
抽象工厂模式中存在四种角色,分别是:抽象建造者、具体建造者、指挥者、产品角色
- 抽象建造者:定义一个抽象接口,规范产品各个组成成分的建造(即规范具体建造者的方法实现)。其中所规范的方法中必须包括建造方法和结果返回方法
- 具体建造者:实现抽象建造者角色所定义的方法。具体建造者与业务逻辑关联性较大,应用程序最终会通过调用此角色中所实现的建造方法按照业务逻辑创建产品,在建造完成后通过结果返回方法返回建造的产品实例。一般在外部由客户或一个抽象工厂创建。
- 指挥者:此角色的作用是调用具体的建造者角色建造产品。导演者与产品类没有直接关系,与产品类交谈的是具体抽象角色。
- 产品角色:一个具体的产品对象。
在本文例子表示
主要解决的问题
- 需要生成的产品对象有复杂的内部结构,这些产品对象通常包含多个成员属性。
- 需要生成的产品对象的属性相互依赖,需要指定其生成顺序。
- 对象的创建过程独立于创建该对象的类。在建造者模式中引入了指挥者类,将创建过程封装在指挥者类中,而不在建造者类中。
- 隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品。
- 代码复用性 & 封装性(将对象构建过程和细节进行封装 & 复用)
优缺点
优点
- 客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。
- 每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者, 用户使用不同的具体建造者即可得到不同的产品对象 。
- 可以更加精细地控制产品的创建过程 。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程。
- 增加新的具体建造者无须修改原有类库的代码,指挥者类针对抽象建造者类编程,系统扩展方便,符合“开闭原则”。
缺点
- 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。
- 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。
使用场景
- 套餐组合,例如一个商品套餐,一个公司组成
- 适合于一个具有较多的零件(属性)的产品(对象)的创建过程。
- 隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品。
示例代码
以组建公司为背景来编写代码,在创建一间公司我们通常按照不同分工来分成不同的部门,在这里身为公司领导(Director) 并不需要去管理每个部门处理的详细细节,只要跟部门负责人沟通就好了。
例如组建一个科技公司大概需要的部门:技术部门、销售部门、人事部门、财务部门
通过建造者模式来创建一家公司,在没有使用建造者模式的时候我们通常使用基本上都是如下:
没有使用建造者模式的代码:
$company = new Company();
$company->setTechnology("技术部门");
$company->setSales("销售部门");
$company->setPersonnel("人事部门");
$Company->setFinance("财务部门");
这样的代码看起来没有什么问题,只不过在调用端就需要多写本多代码了,而且如果针对技术部门设置一些要求或加工,那么所有调用的地方都需要加工,而使用建造者模式都可以在建造者方法进行处理。
BuilderInterface.php
interface BuilderInterface
{
public function technology($str):BuilderInterface;
public function sales($str):BuilderInterface;
public function personnel($str):BuilderInterface;
public function finance($str):BuilderInterface;
public function get();
}
CompanyBuilder.php
class CompanyBuilder implements BuilderInterface
{
/** @var Company $company */
private $company;
/**
* CompanyBuilder constructor.
*/
public function __construct()
{
$this->company = new Company();
return $this;
}
public function technology($str): BuilderInterface
{
$this->company->setTechnology($str);
return $this;
}
public function sales($str): BuilderInterface
{
$this->company->setSales($str);
return $this;
}
public function personnel($str): BuilderInterface
{
$this->company->setPersonnel($str);
return $this;
}
public function finance($str): BuilderInterface
{
$this->company->setFinance($str);
return $this;
}
public function get(): Company
{
return $this->company;
}
}
Company.php
class Company
{
private $technology;//技术
private $sales;//销售
private $personnel;//人事
private $finance;//财务
/**
* @return mixed
*/
public function getTechnology()
{
return $this->technology;
}
/**
* @param mixed $technology
*/
public function setTechnology($technology): void
{
$this->technology = $technology;
}
/**
* @return mixed
*/
public function getSales()
{
return $this->sales;
}
/**
* @param mixed $sales
*/
public function setSales($sales): void
{
$this->sales = $sales;
}
/**
* @return mixed
*/
public function getPersonnel()
{
return $this->personnel;
}
/**
* @param mixed $personnel
*/
public function setPersonnel($personnel): void
{
$this->personnel = $personnel;
}
/**
* @return mixed
*/
public function getFinance()
{
return $this->finance;
}
/**
* @param mixed $finance
*/
public function setFinance($finance): void
{
$this->finance = $finance;
}
}
Director.php
class Director
{
public function create(BuilderInterface $builder): Company
{
// 指挥者使用了具体建造者提供的方法创建对象
return $builder->technology("技术部门")
->sales("销售人员")
->personnel("人事部门")
->finance("招到财务了")
->get();
}
}
调用代码:
// 创建指挥者
$director = new Director();
// 通过指挥者传递一个建造具体对象
$company = $director->create(new CompanyBuilder());
print_r($company);
输出结果:
Company Object
(
[technology:Company:private] => 技术部门
[sales:Company:private] => 销售人员
[personnel:Company:private] => 人事部门
[finance:Company:private] => 招到财务了
)
省略指挥者的建造者模式
主要有三个角色:抽象建造者、具体建造者、产品
UML图
Builder.php
interface Builder
{
public function watch($brand):Builder;
public function phone($brand):Builder;
}
Product.php
class Product
{
private $watch = "华为手表";
private $phone = "小米手机";
/**
* @return string
*/
public function getWatch(): string
{
return $this->watch;
}
/**
* @param string $watch
*/
public function setWatch(string $watch): void
{
$this->watch = $watch;
}
/**
* @return string
*/
public function getPhone(): string
{
return $this->phone;
}
/**
* @param string $phone
*/
public function setPhone(string $phone): void
{
$this->phone = $phone;
}
}
给套餐设置默认是,默认的手表是华为的,手机是小米的。
ConcreteBuilder.php
class ConcreteBuilder implements Builder
{
private $product;
/**
* ConcreteBuilder constructor.
*/
public function __construct()
{
$this->product = new Product();
}
public function watch($brand): Builder
{
$this->product->setWatch($brand . "手表");
return $this;
}
public function phone($brand): Builder
{
$this->product->setPhone($brand . "手机");
return $this;
}
public function get()
{
return $this->product;
}
}
销售人员可以通过调用 watch 和 phone 修改套餐里面默认的产品。
调用代码:
$concreteBuilder = new ConcreteBuilder();
$concreteBuilder->phone("华为");//把套餐里的小米手机换成华为的
print_r($concreteBuilder->get());
输出结果:
Product Object
(
[watch:Product:private] => 华为手表
[phone:Product:private] => 华为手机
)
这种做法省略了指导者,直接通过具体建造者实现了搭配修改。
文章看再多不如亲手敲代码,只有自己写过多代码才是吸取精髓。
如果觉得文章还不错,请把文章分享给更多的人学习,在文章中发现有误的地方也希望各位指出更正。现有误的地方也希望各位指出更正。
IT不是挨踢