工厂方法

未匹配的标注

意图

《设计模式》一书对工厂方法的描述如下

定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延伸到其子类。

定义一个用于创建对象的接口

<?php

abstract class AbstractCreator
{
    abstract public function factoryMethod() : AbstractProduct;
}

让子类决定实例化哪个类

<?php

class ConcreteCreator extends AbstractCreator
{
    public function factoryMethod(): ConcreteProduct
    {
        return new ConcreteProduct();
    }
}

最直观的区别就是在未使用工厂模式时,我们通常在客户端直接对类进行实例化

<?php

function client(){
    $product = new ConcreteProduct();
}

而工厂方法则将实例化对象转移到对应的工厂子类中

<?php

function client(AbstractCreator $creator){
    $creator->someOperation();
}

client(new ContreteCreator());

假如你想要使用不同的产品,如果不使用工厂方法,那么你就要修改客户端的代码。而工厂方法可以让你在工厂类中轻松的重写工厂方法,而不改变客户端的代码。

注意,工厂方法实例化对象不一定需要使用构造函数返回新的实例,也可以结合单例模式来返回之前的实例。而这一切,对客户端都不会造成影响。

由此可见,工厂方法体现了诸多设计原则

  • 单一职责。用专门的工厂类来创建产品。
  • 开闭原则。在不修改客户端的情况下,可以增加新的产品类型。
  • 依赖倒置。客户端只需要依赖抽象的类,而不需要依赖具体的类。

工厂方法针对的粒度是「单个类的创建」,而在这个基础上进行扩展,就变成了其他设计模式,例如,针对多个相关类创建的抽象工厂,针对复杂对象的分步骤生成的生成器模式等等。

实现

image.png

Product - 要创建的产品的接口

<?php

interface Product
{
    public function operation() : string;
}

ConcreteProduct - 具体的产品

<?Php

class ConcreteProduct1 implements Product
{

    public function operation(): string
    {
        return "ConcreteProduct1";
    }
}

class ConcreteProduct2 implements Product
{

    public function operation(): string
    {
        return "ConcreteProduct2";
    }
}

Creator - 抽象的工厂类,定义的工厂方法用来返回 Product

<?php

abstract class Creator
{
    abstract public function factoryMethod() : Product;

    public function someOperation()
    {
        $product = $this->factoryMethod();

        return $product->operation();
    }
}

ConcreteCreator - 具体的工厂类,返回具体的 ConcreteProduct。

<?php

class ConcreteCreator1 extends Creator
{

    public function factoryMethod(): Product
    {
        return new ConcreteProduct1();
    }
}

class ConcreateCreator2 extends Creator
{

    public function factoryMethod(): Product
    {
        return new ConcreteProduct2();
    }
}

应用

我在本地创建了一篇文章,现在要将其发布到网络平台上,对应的平台有两个,Github 和语雀,每个平台都要进行授权、发布文章的操作。如何运用工厂方法来实现呢?

  • 抽象的 Creator - 抽象的博客发布类
  • 具体的 Creator - GIthub 发布类和语雀发布类
  • 抽象的 Product - 博客链接器,用于实现链接平台、发布文章的操作
  • 具体的 Product - Github 链接器和语雀连接器

image.png

博客发布器的主要职责是发布文章,但是 connection 方法相当于工厂方法,返回对应的 Connector 实例。

<?php

// 抽象工厂
abstract class Poster
{        
      // 工厂方法
    abstract public function connection() : Connector;

    public function publish($essay)
    {
        $client = $this->connection();
        $client->init();
        $client->publish($essay);
    }
}

class GithubPoster extends Poster
{
    private $email;
    private $password;

    public function __construct(string $email, string $password)
    {
        $this->email = $email;
        $this->password = $password;
    }

    public function connection() : Connector
    {
        return new GithubConnector($this->email, $this->password);
    }

}

class YuquePoster extends Poster
{
    private $token;

    public function __construct(string $token)
    {
        $this->token = $token;
    }

    public function connection() : Connector
    {
        return new YuqueConnector($this->token);
    }
}

而连接器则相当于 Product

<?php

// 产品
interface Connector
{
    public function init();
    public function publish($content);
}

class GithubConnector implements Connector
{
    private $email;
    private $password;

    public function __construct(string $email, string $password)
    {
        $this->email = $email;
        $this->password = $password;
    }

    public function init()
    {
        echo "使用邮箱{$this->email}和密码进行授权";
    }

    public function publish($content)
    {
        echo "Github 文章发布成功{$content}";
    }
}

class YuqueConnector implements Connector
{
    private $token;

    public function __construct(string $token)
    {
        $this->token = $token;
    }

    public function init()
    {
        echo "使用 Token 进行授权";
    }

    public function publish($content)
    {
        echo "语雀文章发布成功:{$content}";
    }
}

测试

<?php

// 客户端
function client(Poster $poster)
{
    $poster->publish('测试');
}

client(new YuquePoster('token'));

通过这个例子,我们可以发现,虽然博客发布器扮演了创建者的角色,但是它的主要角色是发布文章,而不是创建对应的网络连接,由此可见,工厂方法类中往往包括了其他业务逻辑,通过工厂方法,可以让具体的业务逻辑和底层的服务分离出来。

总结

工厂方法是设计原则的体现,体现出了单一职责、开闭原则、依赖倒置等原则,因此被广泛应用。一些常用的使用场景如下:

  • 实现对象的复用。例如,在工厂方法中实现对对象的复用,避免重复创建新的对象,可用于一些资源密集型的场景(数据库连接、文件系统和网络资源等)
  • 实现对子类的扩展。当你应用存在多个子类,或者你希望能够对子类进行扩展时,可采用工厂方法。

本文章首发在 LearnKu.com 网站上。

上一篇 下一篇
讨论数量: 0
发起讨论 只看当前版本


暂无话题~