工厂模式到底是什么?

简单说,工厂模式是一种 “对象创建” 的标准化解决方案。它的核心是:把对象的创建过程从业务逻辑中抽离出来,交给专门的 “工厂类” 负责。在代码里,就是你不用直接写 new User() 或 new Order(),而是调用 Factory::create('user') 或 Factory::createOrder(),由工厂来完成实例化。

解决的问题

一、解决 “对象创建太复杂” 的问题

  • 初始化数据库连接,需要处理主机、端口、账号密码、驱动选择、连接池等一堆参数;
  • 创建一个订单对象,可能需要先查用户信息、验证库存、初始化日志器等前置操作。

如果这些逻辑散落在代码各处(到处都是 new Database(...)),会导致:

  • 代码臃肿,业务逻辑和创建逻辑混在一起;
  • 一旦创建逻辑变了(比如换数据库驱动),所有 new 的地方都要改,极易出错。

二、解决 “依赖硬编码” 导致的耦合问题

假设你的代码里到处都是 $logger = new FileLogger(),一旦某天需要换成 DatabaseLogger,你得全局搜索替换所有 new FileLogger()—— 这就是典型的 “硬编码依赖”,耦合度极高。

三、解决 “动态创建对象” 的需求

实际开发中,经常需要根据运行时的条件(如配置、用户输入)决定创建哪种对象。比如:

  • 根据用户选择的支付方式,创建 Alipay 或 WechatPay 对象;
  • 根据环境配置(dev/test/prod),创建不同的日志处理器。

如果用 if-else 硬写,会导致代码冗长且难以扩展。

四、解决 “对象创建需要统一管控” 的问题

有些场景下,我们需要对对象创建做统一管理:

  • 限制某个类只能创建 3 个实例(对象池);
  • 记录每个对象的创建时间、次数(用于监控);
  • 对创建的对象进行统一初始化(如设置默认属性)。

这些逻辑如果散落在代码里,会非常混乱。

简单工厂(Simple Factory)

通过静态方法传入不同的参数创建不同的对象,实现对象创建和使用的分离

<?php
class mysql_conn{
    private $dbh = null;
    private $host = 'localhost';
    private $port = '3306';
    private $user = 'root';
    private $password = '****';
    private $dbname = 'test';

    public function __construct(){
        $this->dbh = new PDO('mysql:host=' . $this->host . ';port=' . $this->port . ';dbname=' . $this->dbname, $this->user, $this->password);
    }

    public function get_table_data($table_name){
        foreach($this->dbh->query("SELECT * from `$table_name`") as $row){
            echo "<pre>";
            print_r($row);
            echo "</pre>";
        }
    }
}

class psql_conn{
    private $dbh = null;
    private $host = 'localhost';
    private $port = '5432';
    private $user = 'postgres';
    private $password = '****';
    private $dbname = 'test';

    public function __construct(){
        $this->dbh = new PDO('pgsql:host=' . $this->host . ';port=' . $this->port . ';dbname=' . $this->dbname, $this->user, $this->password);
    }

    public function get_table_data($table_name){
        foreach($this->dbh->query("SELECT * from `$table_name`") as $row){
            echo "<pre>";
            print_r($row);
            echo "</pre>";
        }
    }

    public function custom_query($sql){
        $stmt = $this->dbh->prepare($sql);
        $stmt->execute();
        foreach ($stmt as $row) {
            echo "<pre>";
            print_r($row);
            echo "</pre>";
        }
    }
}

class Factory{
    public static function pdo_conn($db = 'mysql'){
        if ($db == 'mysql') {
            return new mysql_conn();
        }
        else if ($db == 'psql'){
            return new psql_conn();
        }
    }
}

// mysql
// $mysql_dbh = Factory::pdo_conn('mysql');
// $mysql_dbh->get_table_data('user');
// $mysql_dbh->get_table_data('order');

// postgresql
// $psql_dbh = Factory::pdo_conn('psql');
// $psql_dbh->custom_query('select * from student');

通过上面的代码可以看出,简单工厂的缺点就是后面如果想增加新的连接,需要频繁地去修改静态方法

工厂方法(Factory Method)

通过定义一个抽象的核心工厂类,并定义创建产品对象的接口。
创建具体产品示例的工作延迟到其工厂子类去完成。

当系统需要新增一个产品是,无需修改现有系统代码,只需要添加一个具体产品类和其对应的工厂子类,使系统的扩展性变得很好,符合面向对象编程的开闭原则;

<?php
interface database_option{
    public function custom_query($sql);
    public function get_table_data($table_name);
}

class mysql_conn implements database_option{
    private $dbh = null;
    private $host = 'localhost';
    private $port = '3306';
    private $user = 'root';
    private $password = '****';
    private $dbname = 'test';

    public function __construct(){
        $this->dbh = new PDO('mysql:host=' . $this->host . ';port=' . $this->port . ';dbname=' . $this->dbname, $this->user, $this->password);
    }

    public function get_table_data($table_name){
        foreach($this->dbh->query("SELECT * from `$table_name`") as $row){
            echo "<pre>";
            print_r($row);
            echo "</pre>";
        }
    }

    public function custom_query($sql){
        $stmt = $this->dbh->prepare($sql);
        $stmt->execute();
        foreach ($stmt as $row) {
            echo "<pre>";
            print_r($row);
            echo "</pre>";
        }
    }
}

class psql_conn implements database_option{
    private $dbh = null;
    private $host = 'localhost';
    private $port = '5432';
    private $user = 'postgres';
    private $password = '****';
    private $dbname = 'test';

    public function __construct(){
        $this->dbh = new PDO('pgsql:host=' . $this->host . ';port=' . $this->port . ';dbname=' . $this->dbname, $this->user, $this->password);
    }

    public function get_table_data($table_name){
        foreach($this->dbh->query("SELECT * from `$table_name`") as $row) {
            echo "<pre>";
            print_r($row);
            echo "</pre>";
        }
    }

    public function custom_query($sql){
        $stmt = $this->dbh->prepare($sql);
        $stmt->execute();
        foreach ($stmt as $row) {
            echo "<pre>";
            print_r($row);
            echo "</pre>";
        }
    }
}

abstract class Factory{
    abstract static function pdo_conn();
}

class mysql_Factory extends Factory{
    public static function pdo_conn(){
        return new mysql_conn();
    }
}

class psql_Factory extends Factory{
    public static function pdo_conn(){
        return new psql_conn();
    }
}

// mysql
// $mysql_dbh = mysql_Factory::pdo_conn();
// $mysql_dbh->get_table_data('order');

// psql
// $psql_dbh = psql_Factory::pdo_conn();
// $psql_dbh->custom_query('select * from student');

工厂方法是简单工厂的进一步抽象,由于使用了面向对象的多态性,工厂方法保持了简单工厂的优点,在工厂方法中,核心的工厂类不再负责所有产品的创建,而是将具体创建工作交给子类。所以可以在不修改工厂方法的情况下引入新的产品,符合开闭原则。

抽象工厂(Abstract Factory)

提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。

在工厂方法模式中,一个具体的工厂负责生产一类具体的产品,即一对一的关系,但是,如果需要一个具体的工厂生产多种产品对象,那么就需要用到抽象工厂模式了。

<?php
interface database_option{
    public function custom_query($sql);
    public function get_table_data($table_name);
}

class mysql_conn implements database_option{
    private $dbh = null;
    private $host = 'localhost';
    private $port = '3306';
    private $user = 'root';
    private $password = '****';
    private $dbname = 'test';

    public function __construct(){
        $this->dbh = new PDO('mysql:host=' . $this->host . ';port=' . $this->port . ';dbname=' . $this->dbname, $this->user, $this->password);
    }

    public function get_table_data($table_name){
        foreach($this->dbh->query("SELECT * from `$table_name`") as $row) {
            echo "<pre>";
            print_r($row);
            echo "</pre>";
        }
    }

    public function custom_query($sql){
        $stmt = $this->dbh->prepare($sql);
        $stmt->execute();
        foreach ($stmt as $row) {
            echo "<pre>";
            print_r($row);
            echo "</pre>";
        }
    }
}

class psql_conn implements database_option{
    private $dbh = null;
    private $host = 'localhost';
    private $port = '5432';
    private $user = 'postgres';
    private $password = '****';
    private $dbname = 'test';

    public function __construct(){
        $this->dbh = new PDO('pgsql:host=' . $this->host . ';port=' . $this->port . ';dbname=' . $this->dbname, $this->user, $this->password);
    }

    public function get_table_data($table_name){
        foreach($this->dbh->query("SELECT * from `$table_name`") as $row) {
            echo "<pre>";
            print_r($row);
            echo "</pre>";
        }
    }

    public function custom_query($sql){
        $stmt = $this->dbh->prepare($sql);
        $stmt->execute();
        foreach ($stmt as $row) {
            echo "<pre>";
            print_r($row);
            echo "</pre>";
        }
    }
}

abstract class Factory{
    abstract public static function create_mysql_conn();
    abstract public static function create_psql_conn();
}

class db_Factory extends Factory{
    public static function create_mysql_conn(){
        return new mysql_conn();
    }

    public static function create_psql_conn(){
        return new psql_conn();
    }
}

// mysql
// $mysql_dbh = db_Factory::create_mysql_conn();
// $mysql_dbh->get_table_data('order');

// psql
// $psql_dbh = db_Factory::create_psql_conn();
// $psql_dbh->custom_query('select * from student');

总结

这样的代码,当需求变化时(比如换数据库、加支付方式),改动会非常小,这就是工厂模式在大型项目中最实际的价值 ——降低维护成本,提高扩展性

记住:判断该不该用工厂模式的简单标准是 —— 当你发现代码里有很多重复的 new 操作,或者创建对象的逻辑经常变动时,就是该引入工厂的时候了。