工厂方法模式
所有的工厂方法都是用来封装和创建对象,先看下工厂方法的定义:
工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法把类的实例化推迟到子类。
在开始下面的代码故事之前,我还是希望你看一下我的上一篇,简单工厂,看一下我现在店铺的规模和经营方式。
我这里还是想要开一家披萨店,上海必败客披萨(SHBBK),来自纽约的一个有钱人想要加盟我的披萨店,他希望同时提供纽约风味的披萨。按照我已经实现的简单工厂方式,我就需要增加一个店铺:NYBBKPizza,这个店铺的运营方式同BBKPizza一样:
同时,我还修改简单工厂,增加对纽约口味披萨的判断。到目前为止,店铺还能勉强经营。2个月后,纽约的店铺私自修改的orderPizza和Pizza,纽约人不喜欢把披萨切片,所以他们删掉了cut()方法,他们还在披萨中加入了本地的特产的披萨,这显然是我不能容忍的。我需要控制披萨本身,同时我还要控制生产和订购披萨的过程。必须快速解决这个问题,因为来自巴黎的一个有钱人又要加盟我的店铺了。
相比于讲故事,我觉得画图还是更快的
先看Pizza,各种风味的pizza全部在国内生产:
对于工厂,我这里改变了方式
PizzaStore规定的生产披萨和订购披萨的方式,允许各个工厂生产自己的特色披萨,比如上海必败客生成上海本地的披萨。
来看PizzaStore
<?php
namespace FactoryMethod;
/**
* 所有工厂模式都是用来封装对象的创建。工厂方法,让子类决定创建的具体对象
*
* Class PizzaFactory
* @package FactoryMethod
*/
abstract class PizzaFactory
{
/**
* 不希望被重写,防止被子类覆盖
*
* @param $type
*/
public final function orderPizza($type){
$pizza = $this->createPizza($type);
// 解耦,orderPizza并不知道具体哪个Pizza进来了
$pizza->prepare();
$pizza->cut();
$pizza->box();
}
/**
* 工厂方法,把创建具体对象的过程延迟到子类
*
* @param $type
* @return Pizza
*/
abstract function createPizza($type):Pizza;
}
SHBBK工厂是这样的:
<?php
namespace FactoryMethod;
/**
* 上海的必败客披萨工厂
*
* Class BBkFactorySH
* @package FactoryMethod
*/
class BBkFactorySH extends PizzaFactory
{
public function createPizza($type): Pizza {
$pizza = null;
if (0==strcmp($type, 'shang_hai')) {
$pizza = new PizzaShangHai();
// 不喜欢构造函数,所以没用
$pizza->setName('上海杏花楼');
}elseif(0==strcmp($type,'niu_yue')){
$pizza=new PizzaNYStyle();
$pizza->setName('上海纽约风味');
}
return $pizza;
}
}
还有Pizza本身:
<?php
namespace FactoryMethod;
/**
* 披萨类
*
* Class Pizza
* @package FactoryMethod
*/
class Pizza
{
protected $name;
public function setName($name) {
$this->name = $name;
}
public function prepare() {
echo $this->name . '正在准备披萨...';
echo '<br>';
}
public function cut() {
echo $this->name . '披萨切片...';
echo '<br>';
}
public function box() {
echo $this->name . '披萨打包完毕';
echo '<br>';
}
}
上海本地特色披萨:PizzaShangHai
<?php
namespace FactoryMethod;
/**
* 具有上海特色的披萨
*
* Class PizzaShangHai
* @package FactoryMethod
*/
class PizzaShangHai extends Pizza
{
}
纽约本地特色披萨:PizzaNYStyle
<?php
namespace FactoryMethod;
/**
* 具有纽约特色的披萨
*
* Class PizzaNYStyle
* @package FactoryMethod
*/
class PizzaNYStyle extends Pizza
{
}
好了,是时候来点个披萨了:
<?php
include 'Pizza.php';
include 'PizzaFactory.php';
include 'BBkFactorySH.php';
include 'PizzaNYStyle.php';
include 'PizzaShangHai.php';
use FactoryMethod\BBkFactorySH;
$bbk_sh = new BBkFactorySH();
$bbk_sh->orderPizza('shang_hai');
设计原则:
要依赖抽象,不要依赖具体类
它有一个响亮的名字:依赖倒置原则
这个很像之前提到的:
针对接口编程,而不是针对实现编程
这里更强调的抽象,即不能让高层组件依赖底层组件,不管高层组件和底层组件,他们都应该依赖抽象(这里的PizzaFactory是高层组件PizzaShangHai就是底层的组件)。披萨店铺需要提供披萨,芝士披萨、大虾披萨都是披萨,所以所有的披萨都应该共享一个Pizza入口,芝士披萨,大虾披萨都讲依赖于Pizza,而店铺也不需理会具体的Pizza,它只需要依赖Pizza即可,这就是依赖倒置。