PHP 设计模式 - 工厂模式原创

May 28, 2020
haiqing
134
工厂模式是一种创建对象的模式。主要特点在于把创建对象的过程封装了起来,从而减少了不同类之间的耦合。

工厂模式是一种创建对象的模式。

它的特点在于把创建对象的过程封装了起来,从而减少了不同类之间的耦合。

例如:不使用工厂模式,很多地方调用类 A,那么就要实例化一个对象:new A()。如果某天需要修改 A 的名称或者参数,意味着这些代码都要修改。而使用了工厂模式,我们的对象都是从工厂类中产生的,这时我们只要修改工厂类的代码就行。

工厂模式常见的应用场景是这样的:有一个基类(抽象商品)以及多个继承基类的子类(具体商品),例如多个数据库,多种支付方式等。

根据应用场景和复杂度的不同,工厂模式可以分成以下 3 种:简单工厂模式、工厂方法模式和抽象工厂模式。


简单工厂模式(Simple Factory Pattern)

模式定义:又称为静态工厂方法(Static Factory Method)模式,之所以可以这么说,是因为简单工厂模式是通过一个静态方法来创建对象的。

它属于类创建型模式。在简单工厂模式中,可以根据参数的不同返回返回不同类的实例。

提供一个创建对象实例的功能,而无需关心其具体实现。被创建实例的类型可以是接口、抽象类,也可以是具体的类。

模式结构

  1. Factory 工厂类: 工厂类负责实现创建所有实例的内部逻辑
  2. Product 抽象类产品: 抽象类产品是所创建的所有对象的父类,负责描述所有实例所共有的公共接口
  3. ConcreteProduct 具体产品:具体产品是创建目标,所有创建的对象都充当这个角色的某个具体类的实例。

示例:

interface animals // 抽象类产品(动物)
{
    public function name();
}
class Cat implements animals // 具体产品(猫)
{
    public function name()
    {
        return "猫猫";
    }
}
class Dog implements animals // 具体产品(狗)
{
    public function name()
    {
        return "狗狗";
    }
}
//todo 扩展更多的具体产品

// 工厂类
class Factory
{
    public static function createAnimal($param)
    {
        $result = null;
        switch($param)
        {
            case 'cat':
                $result = new Cat();
                break;
            case 'dog':
                $result = new Dog();
                break;
        }
        return $result;
    }
}
// 在实例中调用工厂方法,注意简单工厂必须使用静态方法
echo  Factory::createAnimal("cat")->name(); // 猫
echo  Factory::createAnimal("dog")->name(); // 狗

优缺点:

  • 通过使用工厂类,外界可以从直接创建具体产品对象的尴尬局面摆脱出来,仅仅需要负责“消费”对象就可以了。
  • 简单工厂模式对商品的生产过程(具体类的实例化)进行了封装,将它们放在一个工厂类中。使用者不必管这些对象究竟如何创建及如何组织的。明确了各自的职责和权利,有利于整个软件体系结构的优化。
  • 当我们想要增加新产品类时,需要修改工厂类,增加 switch 中的一个 case(或者增加一个静态方法),当产品类很多时,效率不高。所以工厂类适合负责创建比较少的对象,由于创建的对象较少,不会造成工厂方法中的业务逻辑太过复杂。
  • 客户端只知道传入工厂类的参数,对于如何创建对象不关心:客户端既不需要关心创建细节,甚至连类名都不需要记住,只需要知道类型所对应的参数。

工厂方法模式(Factory Method Pattern)

模式定义: 也叫虚拟构造器(Virtual Constructor)模式或者多态工厂(Polymorphic Factory)模式,它属于类创建型模式。

在工厂方法模式中,工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,这样做的目的是将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类。

模式结构

  1. Product:抽象产品,定义工厂方法所创建的对象的接口,也就是实际需要使用的对象的接口
  2. ConcreteProduct:具体产品,具体的Product接口的实现对象
  3. Factory:工厂接口,也可以叫 Creator(创建器),申明工厂方法,通常返回一个 Product 类型的实例对象
  4. ConcreteFactory:工厂实现,或者叫 ConcreteCreator(创建器对象),覆盖 Factory 定义的工厂方法,返回具体的 Product 实例

示例:

abstract class animals // 抽象产品
{
    abstract public function name();
}
class Cat extends animals // 具体产品
{
    public function name()
    {
        return "猫猫";
    }
}
class Dog extends animals // 具体产品
{
    public function name()
    {
        return "狗狗";
    }
}
interface Factory   // 抽象工厂类, 将对象的创建抽象成一个接口
{
    public function create();
}
class CatFactory implements Factory  // 继承工厂类, 用于实例化产品
{
    public function create()
    {
        return new Cat();
    }
}
class DogFactory implements Factory  // 继承工厂类, 用于实例化产品
{
    public function create()
    {
        return new Dog();
    }
}

$catResult = new CatFactory();
echo $catResult->create()->name();

$DogResult = new DogFactory();
echo $DogResult->create()->name();

优缺点:

工厂方法模式是简单工厂模式的进一步抽象和推广。

由于使用了面向对象的多态性,工厂方法模式保持了简单工厂模式的优点,而且克服了它的缺点。

在工厂方法模式中,核心的工厂类不再负责所有产品的创建,而是将具体创建工作交给子类去做。这个核心类仅仅负责给出具体工厂必须实现的接口,而不负责产品类被实例化这种细节,这使得工厂方法模式可以允许系统在不修改工厂角色的情况下引进新产品。

  • 一个调用者想创建一个对象,只要知道其名称就可以了。
  • 扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。
  • 屏蔽产品的具体实现,调用者只关心产品的接口。
  • 每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。

抽象工厂模式(Abstract Factory Pattern)

模式定义: 又称工具箱(Kit 或Toolkit)模式,为创建一组相关或相互依赖的对象提供一个接口,而且无需指定他们的具体类。

工厂方法模式通过引入工厂等级结构,解决了简单工厂模式中工厂类职责太重的问题,但由于工厂方法模式中的每个工厂只生产一类产品,可能会导致系统中存在大量的工厂类,势必会增加系统的开销。此时,我们可以考虑将一些相关的产品组成一个“产品族”,由同一个工厂来统一生产,这就是抽象工厂模式的基本思想。

模式结构

  1. AbstractFactory:抽象工厂,用于声明生成抽象产品的方法
  2. ConcreteFactory:具体工厂,实现抽象工厂定义的方法,具体实现一系列产品对象的创建
  3. AbstractProduct:抽象产品,定义一类产品对象的接口
  4. ConcreteProduct:具体产品,通常在具体工厂里,会选择具体的产品实现,来创建符合抽象工厂定义的方法返回的产品类型的对象。
  5. Client:客户端,使用抽象工厂来获取一系列所需要的产品对象

示例:

interface AnimalsFactory // 抽象工厂
{
    public function createCat(); // 生产一尾猫
    public function createDog(); // 生产一头狗 -_-
}


abstract class Cat  // 猫抽象
{
    abstract function getCat();
}
class ForeignCat extends Cat // 猫具体
{
    public function getCat()
    {
        return "外国布偶猫".PHP_EOL;
    }
}
class ChineseCat extends Cat
{
    public function getCat()
    {
        return "华夏猫".PHP_EOL;
    }
}


abstract class Dog
{
    abstract function getDog();
}
class ChineseDog extends Dog
{
    public function getDog()
    {
        return "中华国犬".PHP_EOL;
    }
}
class ForeignDog extends Dog
{
    public function getDog()
    {
        return "外国哈士奇".PHP_EOL;
    }
}


class CreateChineseAnimalFactory implements AnimalsFactory 
{
    public function createCat()
    {
        return new ChineseCat();
    }
    public function createDog()
    {
        return new ChineseDog();
    }
}
class CreateForeignAnimalFactory implements AnimalsFactory
{
    public function createCat()
    {
        return new ForeignCat();
    }
    public function createDog()
    {
        return new ForeignDog();
    }
}


$result = new CreateForeignAnimalFactory();
$ForeignCat = $result->createCat();
echo $ForeignCat->getCat(); // 布偶猫


$ForeignDog = $result->createDog();
echo $ForeignDog->getDog(); // 哈士奇

优缺点:

抽象工厂模式是工厂方法模式的进一步延伸,由于它提供了功能更为强大的工厂类并且具备较好的可扩展性,在软件开发中得以广泛应用,尤其是在一些框架和API类库的设计中。抽象工厂模式也是在软件开发中最常用的设计模式之一。

  • 抽象工厂模式隔离了具体类的生成,使得客户并不需要知道什么被创建。由于这种隔离,更换一个具体工厂就变得相对容易,所有的具体工厂都实现了抽象工厂中定义的那些公共接口,因此只需改变具体工厂的实例,就可以在某种程度上改变整个软件系统的行为。
  • 当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象。
  • 增加新的产品族很方便,无须修改已有系统,符合“开闭原则”。
  • 增加新的产品等级结构麻烦,需要对原有系统进行较大的修改,甚至需要修改抽象层代码,这显然会带来较大的不便,违背了“开闭原则”。

总结

  1. 简单工厂模式(静态方法工厂模式) : 用来生产同一等级结构中的任意产品。(不能增加新的产品)
  2. 工厂模式 :用来生产同一等级结构中的固定产品。(支持增加任意产品)
  3. 抽象工厂 :用来生产不同产品种类的全部产品。(不能增加新的产品,支持增加产品种类)


参考:

https://juejin.im/post/5b0bc2ef518825159a7f63da

https://xie.infoq.cn/article/88c926822394aa1c80847dd2a