工厂模式的故事:程序员、书和狗

PHP , 实战928 字

在正式开始之前,我们先来编写一个自动加载功能,这样我们就不用老是 include 文件了。

<?php

define('ROOT_PATH', str_replace('\\', '/', realpath(dirname(__FILE__) . '/')) . "/");
function autoload($className)
{
    $classPath = ROOT_PATH . 'src' . DIRECTORY_SEPARATOR . $className . '.class.php';
    include $classPath;
}

spl_autoload_register('autoload');
  • 首先我们声明一个 ROOT_PATH,用来帮助我们定位当前目录
  • autoload 函数,用来自动文件的函数,其中定义了加载规则,我们统一将代码放到 src 目录下,每个类文件的后缀为 .class.php
  • 通过 spl_autoload_register 来做自动加载注册,当 php 运行后如果发现当前没有要用得类就会触发 autoload 函数进行文件加载。

我们的项目目录最终结构如下:

├── autoload.php
├── index.php
└── src
    └── xxx.class.php

好了我们回到正题。很多人觉着设计模式在实际代码中离自己很远,希望看完这个系列的文章后,能够利用设计模式改善自己的代码。

最开始我们来学最简单的工厂模式,网上有很多关于工厂模式的文章,在这里,我们结合 PHP7 来进一步的演化和演进,我们写设计模式不能按照网上的通用代码,一定要结合语言和项目才能写好,很多同学在网上看到了 Java 的工厂模式,就和 Java 写的一模一样,那为什么不直接使用 Java 而使用 PHP 呢?

我们来看一个非典型的电商系统,刚起步的时候往往只有 1-2 个商品,例如只有图书,此时对的协作模式很简单,开发人员可能只有一个,编写的代码可能就是下面这个样子。

$object = new Books();

在 Book 中包含:

  1. 图书信息的维护
  2. 图书点击量
  3. 图书架构
  4. 图书的订单

如果这种规模确实这么做就可以了,随着项目越来越大,我们的库中会增加的一些其他商品除了图书还增加了酒喝狗,开发人员也越来越多。

# index.php
# 程序员 A 负责 Book 的维护迭代
$obj = new Books();
# 程序员 B 负责 Dog 的维护迭代
$obj = new Dogs();
# 程序员 C 负责 Wine 的维护迭代
$obj = new Wines();

我们大哥比方,假设每个类都有一个获取商品列表的方法:getList();

Books 的代码实现:

<?php

class Books
{
    public function getList()
    {
        return [
            ['product_id' => 101, "product_name" => "java 从入门到精通"],
            ['product_id' => 102, "product_name" => "PHP 从入门到精通"],
        ];
    }
}

Dogs 的代码实现

<?php

class Dogs
{
    public function getList()
    {
        return [
            ['product_id' => 201, "product_name" => "泰迪"],
            ['product_id' => 202, "product_name" => "金毛"],
        ];
    }
}

Wines的代码实现

<?php

class Wines
{
    public function getList()
    {
        return [
            ['product_id' => 301, "product_name" => "红酒"],
            ['product_id' => 302, "product_name" => "白酒"],
        ];
    }
}

正常业务代码中,getList 中的应该从数据库中取,这里为了演示方便直接返回数组。

如果我们做这个开发,这三个商品都应该在库中标记为商品,不过类型不同,很可能呈现的展现形式和业务逻辑是不一样的,所以需要区分成不同的商品。

这么写有没有什么问题?

如果我们把书和狗看成两个独立的子频道,开发起来是没有任何问题的。

但是假设现在有一天图书的货源出问题了,老板临时决定范式访问书的数,统一返回狗的数据。

这个时候程序员可能这么做:

<?php

include 'autoload.php';
//$object = new Books();
$obj = new Dogs();
$list = $obj->getList();

注释掉原有的代码,用新的实体类替换老的实体类,如果项目做大了,这个时候要进行这样的改动会很大。这个时候就需要涉及到工厂模式了。

工厂模式就是将我们类的实例化和取出全部有一个通过工厂方法的参数不同来取出对象,实例化对象的时候就变成了这样。

<?php

include 'autoload.php';

$object = ProductFactory::getProduct('book');
$object->getList();

实现代码如下:

<?php

class ProductFactory
{
    public static function getProduct($type)
    {

        switch ($type) {
            case "book" :
                $obj = new Books();
                break;
            case "dog" :
                $obj = new Dogs();
                break;
            case "wine" :
                $obj = new Wines();
                break;
            default:
                $obj = false;
        }

        return $obj;
    }
}

这样工厂模式就完成了,回到刚才老板说的需求,要替代将 book 的数据提到成 dog,我们只需要修改工厂模式就可以了。

<?php

class ProductFactory
{
    public static function getProduct($type)
    {
        if ($type == "book") {
            $type = "dog";
        }

        switch ($type) {
            case "book" :
                $obj = new Books();
                break;
            case "dog" :
                $obj = new Dogs();
                break;
            case "wine" :
                $obj = new Wines();
                break;
            default:
                $obj = false;
        }

        return $obj;
    }
}

这样一来,我们在获取图书列表的的时候就会被替代成狗的数据。

maksim
Maksim(一笑,吡罗),PHPer,Goper
OωO
开启隐私评论,您的评论仅作者和评论双方可见