1

创建型设计模式

包括以下五种:

  • 抽象工厂

  • 生成器

  • 工厂方法

  • 原型

  • 单例

我们选择工厂方法和原型模式作为将用PHP实现的创建型设计的例子
工厂方法模式是这5个设计模式中唯一的一种类设计模式
原型模式属于对象类模式,可以使用PHP_clone方法实现。首先基于原型实例化(创建)一个对象,然后由这个实例化对象进一步克隆其他对象
使用创建型模式时,最有意思的是,当程序和系统越来越依赖于对象组合而不是依赖于类继承时,创建型模式中的程序变成由对象构成的系统,而对象又由其他对象组合而成,所以任何单个对象的创建都不应该依赖于创建者

工厂方法模式

概念:工厂方法模式就是要创建某种东西,对于工厂方法模式,要创建的东西是一个产品,这个产品与创建它的类之间不存在绑定,为了保持这种松耦合,客户会通过一个工厂发出请求,再由工厂创建所请求的产品。利用工厂方法模式,请求者发出请求,而不具体创建产品。
何时使用:如果实例化对象的子类可能变化,就要使用工厂方法模式
Why:对象的数目和类型都是未知的,一个类无法预计它要创建的对象数目,所以你不希望类与它要创建的类紧密绑定

案例模型:
clipboard.png

步骤:

第一步是建立工厂:Creator接口

<?php
//Creator.php
abstract class Creator{
    protected abstract function factoryMethod();
    public function startFactory(){
        $mfg = $this->factoryMethod();
        return $mfg;
    }
}
?>

注意到,伪代码注释提示startFactory()方法需要返回一个产品(product),在实现中startFactory()希望factoryMethod()返回一个产品对象,所以,factoryMethod()的具体实现要构建并返回由一个按Product接口实现的产品对象

下面有两个工厂类扩展了Creator,并实现了factoryMethod()方法,factoryMethod()实现通过一个Product方法(getProperties())返回一个文本或图像产品,TextFactory和GraphicFactory实现中加入了这些内容

<?php
//TextFactory.php
include_once('Creator.php');
include_once('TextProduct.php');
class TextFactory extends Creator{
    protexted function factoryMethod(){
        $product = new TextProduct();
        return ($product->getProperties());
    }
}
?>
<?php
//TextFactory.php
include_once('Creator.php');
include_once('GraphicProduct.php');
class GraphicFactory extends Creator{
    protexted function factoryMethod(){
        $product = new GraphicProduct();
        return ($product->getProperties());
    }
}
?>

工厂方法设计模式中的第二个接口是Product。由于这是第一个实现,也是最简单的实现,所有文本和图像属性都只实现一个方法getProperties():建立方法而无属性,我们可以明确想要用这个方法做什么(比如可以有个返回值),只要方法名和可见性与签名一致就不会有问题

<?php
//Product.php
interface Product{
    public function getProperties();
}
?>

可以利用这个实现,使得同一个方法getProperties()多态,分别返回图像和文字,如下所示

<?php
//TextProduct.php
include_once('Prouduct.php');
class TextProduct implements Product{
    private $mfgProduct;
    public function getProperties(){
        $this->mfgProduct = "This is a Text<br";
        return $this->mfgProduct;
    }
}
?>
<?php
//Graphic.php
include_once('Product.php');
class GraphicProduct implements Product{
    private $mfgProduct;
      public function getProperties(){
        $this->mfgProduct = "This is a Graphic<br>";
        return $this->mfgProduct;
    }
}
?>

上面你看到的This is a Graphic以及This is text,可以替换成你想放入的其它东西,工厂设计会创建这个对象,并把它返回给Client来使用。
这两个工厂和产品分别覆盖了抽象方法,来创建两个不同的工厂和产品,它们都符合所实现的接口

客户
这个模式最后一个参与者是隐含的(上面模型图中颜色较淡的框):客户。我们并不希望Client类直接做出产品请求。实际上,我们希望能够通过Creator接口做出请求,这样一来,如果以后我们增加产品或者工厂,客户可以做同样的请求来得到更多类型的产品,而不会破坏这个应用

<?php
//Client.php
include_once('GraphicFactory.php');
include_once('TextFactory.php');
class Client{
    private $someGraphicObject;
    private $someTextObject;
    public function __construct(){
        $this->someGraphicObject = new GraphicFactory();
        echo $this->someGraphicObject->startFactory();
         $this->someTextObject = new TextFactory();
        echo $this->someTextObject->startFactory();
    }
}
$worker = new Client();
?>

注意Client对象并没有向产品直接做出请求,而是通过工厂来请求,重要的是,客户并不实现产品特性,而留给产品实现来体现。


在本章有个例子,是对上面代码的改进,也可以称作是在工厂中修改产品,但是本猿以为,例子似乎举的不是很好,它把Html代码嵌套在php代码中,放在了产品类下的getProperties方法中,以供客户调用的时候返回,所以我个人觉得这种方法不是很好,一大段代码放在php中,也不方便编辑器编辑。设计模式是为了加快开发速度,这样的方法我适得其反。作者是专家,可能也有出于其它考虑。不过里面有一个观点我还是同意的,其中有一个辅助类的观点。
所谓辅助类,就是把一些任务给一个单独的对象来处理,而不是结合到某个参与者中。类似地,如果需要重用一组HTML标记,可以把它们打包到另一个对象中以便重用。下面是一个例子

<?php
class FormatHelper{
    private $topper;
    private $bottom;
    public function addTop(){
        $this->topper = "<!doctype html><html><head>
        <link rel='stylesheet' type='text/css' href='products.css'/>       
        <meta charset='UTF-8'>
        <title>Map Factory</title>
        </head> <body>";
        return $this->topper;
    }
    public function closeUp(){
        $this->bottom = "</body></html>";
        return $this->bottom;
    }
}
?>

增加新产品和参数化请求

clipboard.png

上图与之前的类图不同,它们完成同样的目标,不过它们的实现有所不同。这就是参数化工厂方法设计模式(上图)与一般的工厂方法设计模式(原类图)的主要区别之一,即客户包含工厂和产品的引用。在参数化请求中,Client类必须指定产品,而不只是产品工厂,factoryMethod()操作中的参数是由客户传入的一个产品,所以客户必须指出它想要的具体产品,不过,这个请求仍然通过Creator接口发出,所以,尽管客户包含一个产品引用,但通过Creator,客户仍与产品分离

一个工厂多个产品

对于大多数请求,参数化工厂方法更为简单,因为客户只需要处理一个具体工厂,工厂方法操作有一个参数,指示需要创建的产品。而在原来的设计中,每个产品都有自己的工厂,不需要另外传递参数,产品实现依赖于各个产品的特定工厂。

要从参数化工厂方法设计模式实现多个产品,只需使用Product接口实现多个具体产品,另外,由于具体产品要同时包含文本和图像,所以在这个例子中,并不是分别有这两个单独的产品,可以建立一个类,将文本和图像作为一个同意的实体来处理,这并不违反单一职责原则,即每个类应当只有一个职责。对于这个类来说,这个单一职责就是显示描述一个区域的文本和图像

新工厂

新工厂与原来的工厂类似,不过它们还包含一个参数和代码提示。

<?php
//Creator.php
abstract class Creator{
    protected abstract function factoryMethod(Product $product);
    
    public function doFactory($productNow){
        $countryProduct = $productNow;
        $mfg = $this->factoryMethod($countryProduct);
        return $mfg;
    }
}
?>

新的Creator抽象类中可以看到,factoryMethod()和startFactory操作都需要一个参数。另外代码提示只是了一个Product对象,而不是Prodcut一个特定实现,所以可以接受Product的任何具体实例,下面来看看具体的创建者CountryFactory

<?php
//CountryFactory.php
include_once('Creator.php');
inlcude_once('Product.php');
class CountryFactory extends Creator{
    private $country;
    protected function factoryMethod(Product $product){
        $this->country = $product;
        return ($this->country->getProperties());
    }
}
?>

这个具体创建者包含一个私有变量$country,其中包含客户请求的特定产品,它再使用Product方法getProperties()将产品返回给客户

与试图让任意数目的类和对象都保持不变相比,保持接口不变要容易得多。正是因为这个原因,使用工厂方法模式可以简化复杂的创建过程,关键就在于它在维持一个公共接口

本文参考书籍:《Learnig PHP设计模式》第2部分第5章


R_Jeff
405 声望21 粉丝

坚持拍黄片的大四网页狗要成为PHPer