工厂模式 (Factory)
手握设计模式宝典 - 工厂模式 Factory
Design-Pattern - Creational - Factory
- Title: 《手握设计模式宝典》 之 工厂模式 Factory
- Tag: Design-Pattern、Creational、Factory、工厂模式、设计模式、简单工厂、工厂方法、抽象工厂
- Author: Tacks
- Create-Date: 2023-08-21
- Update-Date: 2023-08-21
大纲
0、REF
- @refactoringguru - 工厂方法
- @refactoringguru - 抽象工厂
- @PHP设计模式全集 - 简单工厂模式(Simple Factory)
- @PHP设计模式全集 - 工厂方法模式(Factory Method)
- @PHP设计模式全集 - 抽象工厂模式(Abstract Factory)
- 解读 Laravel 中的扩展自动注册机制(Package Auto-discovery)
1、5W1H
1.1 WHAT 什么是工厂模式?
工厂模式 是一种 创建型 设计模式,用于封装创建对象的代码,负责处理创建对象的实例的细节。
解耦: 单一职责,把创建对象和使用对象分离
工厂模式:将 new 实例化对象(产品)的逻辑封装到工厂中,使客户端只需要与工厂类进行交互,从而使得产品类与客户端进行解耦。
一些情况下,有些创建对象的时候,需要做一些初始化工作。那么客户端调用的时候,在 new 对象之前就需要写几行代码,一个调用处还好,要是项目中多个地方都要用到这个对象,那么就是会出现很多重复代码,并且当需要变动的时候,修改起来也很不方便。那么不如用一个工厂来掩盖实例化对象的逻辑步骤,客户端只需要告诉工厂你需要什么,工厂就直接返回什么对象。
当然,这个里面细分还有不同的工厂实现;
- 简单工厂 Simple Factory
- 工厂方法 Factory Method
- 抽象工厂 Abstract Factory
          简单<--            -->扩展                
[简单工厂] ------ [工厂方法] ------ [抽象工厂]1.1.1 简单工厂模式-概念
简单工厂 封装一个工厂的创建器,根据客户端传入参数,来判断具体返回什么类的对象实例。
(非 GOF 提出的23种设计模式)
特点
- 一个工厂类;- 职责过重,创建器根据参数来识别创建对应具体产品
 
1.1.2 工厂方法模式-概念
工厂方法 定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。
特点
- 多个工厂类;- 多少个产品对应多少个工厂;
- 一个工厂只能创建对应的一个具体产品实例
 
- 一个产品抽象类- 派生处多个具体产品类
 
1.1.3 抽象工厂模式-概念
抽象工厂 提供一个创建一系列相关或相互依赖对象的接口,而不需要指明它们具体的类。
特点
- 多个工厂类- 产品分组,有多少组,就有多少工厂类;
- 每个工厂类可以创建多个具体产品的实例
 
- 产品等级分组- 相同功能或特点的,由于不同的产地导致有不同系列
- 比如:- 椅子;有宜家风格、有中式风格
- 桌子;有宜家风格、有中式风格
- 沙发:有宜家风格、有中式风格
 
- 那么这里的椅子就有两种等级分组,最后的工厂也是两个
 
- 产品分族- 同一个系列的不同功能或者特点的产品
- 那么这里的中式风格,就是一个分族,表示同一系列的产品
 
1.2 WHY 为什么有工厂模式?
这些工厂都具备的一些优点
- 解耦;对象的创建和使用分离
1.2.1 简单工厂模式-优缺点
- 优点- 简单易于实现;对于产品对象已经确定的,不经常修改工厂类的,比较适用
- 隐藏细节;客户端无需关心对象的具体创建过程
 
- 缺点- if/else的使用
- 违反开闭原则;如果需要新增产品,要修改工厂的逻辑,随着产品越来越多,工厂的生产器也会越复杂
 
1.2.2 工厂方法模式-优缺点
- 优点- 解决了开闭原则;
- 解决 if/else的使用
 
- 缺点- 代码量增多;每一个产品对应都需要有一个工厂类。
 
所以说。 工厂方法 -> 简单化处理 -> 简单工厂
1.2.3 抽象工厂模式-优缺点
- 优点- 依赖倒置原则;要依赖抽象,不要依赖具体的类
 
- 缺点- 违反了开闭原则;
 
1.3 WHEN 什么时间使用工厂?
- 当需要创建多种类型的对象,可以利用工厂集中管理对象的创建过程
- 创建对象的过程比较复杂,需要隐藏对象创建的细节,只提供一个统一的对象获取器
1.4 WHERE 在什么地方使用工厂模式?
- 数据库连接器
- 视图连接器
1.5 WHO 谁负责调用工厂?
客户端调用工厂类,来获取具体的产品对象
[客户业务逻辑] ------> [工厂] ------> [基础类]1.6 HOW 如何去实现工厂?
讲述一个不用工厂,到用简单工厂带来的好处,虽然例子没有很形象,但是看一下代码大概就能理解,工厂解耦的好处,以及封装的思想隐藏实例化对象的细节。
1.6.1 不用工厂-Ⅰ
其实看下面代码也没有什么问题,无非就是需要啥的时候去 new 就行
namespace App\Creational\Factory\NoFactory;
abstract class Fruit {
    abstract public function intro();
}
class Apple extends Fruit {
    public function intro() {
        echo sprintf("[%s] 我是又脆又甜的苹果!", __CLASS__) . PHP_EOL;
    }
}
class Banana extends Fruit {
    public function intro() {
        echo sprintf("[%s] 我是香甜软绵的香蕉!", __CLASS__) . PHP_EOL;
    }
}
$appleObj = new Apple();
$appleObj->intro();
$bananaObj = new Banana();
$bananaObj->intro();1.6.2 不用工厂-Ⅱ
- 场景切换
之前就是简单的 苹果类、香蕉类,直接 new 实例化就行,不需要做什么初始化工作。
现在引入 Market 市场类,市场监管局来了,想要生产什么水果,需要在这注册一下,并且设置市场价格
就会多了一步操作,需要去传入市场价格。
- 之前 $appleObj = new Apple();
- 现在 $appleObj = new Apple($market->getPrice('apple'));
看似也问题不大,但是哪天万一是大促,苹果可以在市场价上打个折,那就需要做一个 $market->getPrice('apple') * 9 操作,重新传入进去,如果客户端很多,也就是零售点贼多,你还需要让他们自己调整价格。
要是打折结束了,又要回调市场价,那又要告诉下面零售店再重新改成  $market->getPrice('apple') 。 太栓Q !
- 想一个思路
要是有一个统一的生产厂商多好,只需要他跟商场监管局打好招呼就行,该是多少钱,就是多少钱,顶多生产商定期活动啥的,也是再市场价格上下浮动。零售点都根据生产商提供的价格来卖,不再关心价格,只是关心买什么。
namespace App\Creational\Factory\NoFactory2;
// h
class Market {
    private $prices;
    private static $instance = null;
    private function __construct(){}
    public static function getInstance()
    {
        if(null === self::$instance) {
            self::$instance = new self();
        }
        return self::$instance;
    }
    public function setPrice(string $goods, float $price)
    {
        !isset($this->prices[$goods]) ? $this->prices[$goods] = $price : null;
    }
    public function getPrice(string $goods)
    {
        return $this->prices[$goods] ?? -1;
    }
}
abstract class Fruit {
    private $price;
    public function __construct(float $price)
    {
        $this->price = $price;
    }
    abstract public function intro();
    public function getPrice() {
        echo sprintf("[%s] 单价:%s/斤", get_class($this), $this->price) . PHP_EOL;
    }
}
class Apple extends Fruit {
    public function intro() {
        echo sprintf("[%s] 我是又脆又甜的苹果!", __CLASS__) . PHP_EOL;
    }
}
class Banana extends Fruit {
    public function intro() {
        echo sprintf("[%s] 我是香甜软绵的香蕉!", __CLASS__) . PHP_EOL;
    }
}
// 市场调控
$market = Market::getInstance();
$market->setPrice('apple', '3.0');
$market->setPrice('banana', '3.5');
// 实例化苹果
$appleObj = new Apple($market->getPrice('apple'));
$appleObj->intro();
$appleObj->getPrice();
// 实例化香蕉
$bananaObj = new Banana($market->getPrice('banana'));
$bananaObj->intro();
$bananaObj->getPrice();1.6.3 简单工厂-Ⅲ
- 引入水果生产商
利用组合的方式,再工厂实例化的时候,就传入市场监管局定的价格,然后提供一个创建器,用来根据用户参数需求,提供对应水果的对象实例。
class FruitFactory {
    private $market;
    public function __construct(Market $market)
    {
        $this->market = $market;
    }
    public function createor(string $originType) {
        $type = strtolower($originType);
        $price= $this->market->getPrice($type);
        if($price == -1) {
            throw new Exception("{$originType} 商品市场未分配价格,暂时无法售卖");
        }
        if ($type == 'apple') {
            return new Apple($price);
        } else if ($type == 'banana') {
            return new Banana($price);
        }
        throw new Exception("{$originType} 水果店-暂时未引入该水果,暂时无法提供");
    }
}
// 市场调控
$market = Market::getInstance();
$market->setPrice('apple', '3.0');
$market->setPrice('banana', '3.5');
/*
// *************************未用工厂
// 实例化苹果
$appleObj = new Apple($market->getPrice('apple'));
$appleObj->intro();
$appleObj->getPrice();
// 实例化香蕉
$bananaObj = new Banana($market->getPrice('banana'));
$bananaObj->intro();
$bananaObj->getPrice();
*/
// *************************使用工厂
try {
    // 水果店
    $factory = new FruitFactory($market);
    // 想要水果
    $appleObj = $factory->createor('apple');
    $appleObj->intro();
    $appleObj->getPrice();
    // 想要香蕉
    $bananaObj = $factory->createor('banana');
    $bananaObj->intro();
    $bananaObj->getPrice();
    $cherryObj = $factory->createor('cherry');
    $cherryObj->intro();
    $cherryObj->getPrice();
} catch(Exception $e) {
    echo $e->getMessage() . PHP_EOL;
}2、Code
2.1 简单工厂实现-水果工厂卖苹果香蕉
- 抽象产品 FruitAbstract- abstract- class抽象父类
- 作用:简单工厂生产的所有产品的抽象父类
- 约束:描述所有产品的公共的接口,例如每一种水果都有一个自我介绍 intro()
 
- 具体产品 AppleFruitConcreteBananaFruitConcrete- class具体子类
- 作用:简单工厂生产的具体实例对象
- 实现:抽象方法 intro()
 
- 简单工厂 FruitFactory- class简单工厂
- 作用:简单工厂核心,负责创建所有具体实例对象的逻辑,通常表现为 if/else或switch/case
- 创建器方法:用来供外部调用,创建所需产品的实例对象,例如 createor($type) :FruitAbstract
 
2.1.1 抽象层-水果父类
// 抽象产品
abstract class FruitAbstract
{
    abstract public function intro();
}2.1.2 实现层-苹果/香蕉
// 苹果-具体产品
class AppleFruitConcrete extends FruitAbstract
{
    public function intro()
    {
        echo sprintf("[%s] 我是又脆又甜的苹果!", __CLASS__) . PHP_EOL;
    }
}
// 香蕉-具体产品
class BananaFruitConcrete extends FruitAbstract
{
    public function intro()
    {
        echo sprintf("[%s] 我是香甜软绵的香蕉!", __CLASS__) . PHP_EOL;
    }
}
2.1.3 工厂层-水果工厂
//  水果-工厂类
class FruitFactory
{
    /**
     * 创建器
     *
     * @param string $originType
     * @return FruitAbstract
     * @throws Exception
     */
    public function createor(string $originType): FruitAbstract
    {
        $type = strtolower($originType);
        if ($type == 'apple') {
            return new AppleFruitConcrete();
        } else if ($type == 'banana') {
            return new BananaFruitConcrete();
        }
        throw new \Exception("{$originType} 暂不支持");
    }
}2.1.4 逻辑层-卖水果了,想吃啥快来
try {
    $factory = new FruitFactory();
    $appleObj = $factory->createor('apple');
    $appleObj->intro();
    $bananaObj = $factory->createor('banana');
    $bananaObj->intro();
    $cherryObj = $factory->createor('cherry');
    $cherryObj->intro();
} catch (\Exception $e) {
    echo $e->getMessage() . PHP_EOL;
}
- 调用输出
/*
[App\Creational\Factory\SimpleFactory\AppleFruitConcrete] 我是又脆又甜的苹果!
[App\Creational\Factory\SimpleFactory\BananaFruitConcrete] 我是香甜软绵的香蕉!
cherry 暂不支持
*/2.2 工厂方法实现-水果工厂卖苹果香蕉
2.2.1 抽象层-水果父类
abstract class Fruit {
    abstract public function intro();
}2.2.2 实现层-苹果/香蕉
class Apple extends Fruit {
    public function intro() {
        echo sprintf("[%s] 我是又脆又甜的苹果!", __CLASS__) . PHP_EOL;
    }
}
class Banana extends Fruit {
    public function intro() {
        echo sprintf("[%s] 我是香甜软绵的香蕉!", __CLASS__) . PHP_EOL;
    }
}2.2.3 工厂层-工厂父类约束方法/苹果工厂/香蕉工厂
abstract class FactoryBase {
    function createor(){}
}
class AppleFruitFactory extends FactoryBase {
    public function createor() {
        return new Apple();
    }
}
class BananaFruitFactory extends FactoryBase {
    public function createor() {
        return new Banana();
    }
}2.2.4 逻辑层-吃啥找对应工厂
$appleObj = (new AppleFruitFactory())->createor();
$appleObj->intro();
$bananaObj = (new BananaFruitFactory())->createor();
$bananaObj->intro();2.3 抽象工厂实现-本地和进口的水果工厂
2.3.1 同族的不同品种
- PineappleFruitInterface菠萝接口- PineappleA菠萝A
- PineappleB菠萝B
 
// 菠萝接口
interface PineappleFruitInterface
{
    public function pineapple();
}
// 菠萝A产品
class PineappleA implements PineappleFruitInterface
{
    public function pineapple()
    {
        echo sprintf("[%s] 我吃菠萝!", __CLASS__) . PHP_EOL;
    }
}
// 菠萝B产品
class PineappleB implements PineappleFruitInterface
{
    public function pineapple()
    {
        echo sprintf("[%s] 我吃凤梨!", __CLASS__) . PHP_EOL;
    }
}- CherryFruitInterface樱桃接口- CherryA樱桃A
- CherryB樱桃B
 
// 樱桃接口
interface CherryFruitInterface
{
    public function cherry();
}
// 樱桃A产品
class CherryA implements CherryFruitInterface
{
    public function cherry()
    {
        echo sprintf("[%s] 我吃樱桃!", __CLASS__) . PHP_EOL;
    }
}
// 樱桃B产品
class CherryB implements CherryFruitInterface
{
    public function cherry()
    {
        echo sprintf("[%s] 我吃车厘子!", __CLASS__) . PHP_EOL;
    }
}2.3.2 工厂接口层
- AbstractFactory抽象工厂
interface AbstractFactory
{
    public function createFruitPineapple();
    public function createFruitCherry();
}2.3.3 工厂分组实现
- LocalConcreteFactory本地工厂
- ImportConcreteFactory进口工厂
// 本地工厂
class LocalConcreteFactory implements AbstractFactory
{
    public function createFruitPineapple()
    {
        return new PineappleA();
    }
    public function createFruitCherry()
    {
        return new CherryA();
    }
}
// 进口工厂
class ImportConcreteFactory implements AbstractFactory
{
    public function createFruitPineapple()
    {
        return new PineappleB();
    }
    public function createFruitCherry()
    {
        return new CherryB();
    }
}2.3.4 逻辑层-不同工厂产出的品种不同
// 两家工厂
$local = new LocalConcreteFactory();
$import = new ImportConcreteFactory();
// 本地工厂-生产
$localPineapple = $local->createFruitPineapple();
$localCherry = $local->createFruitCherry();
// 进口工厂-生产
$importPineapple = $import->createFruitPineapple();
$importCherry = $import->createFruitCherry();
// 小明喜欢吃
$localPineapple->pineapple();
$localCherry->cherry();
// 小红喜欢吃
$importPineapple->pineapple();
$importCherry->cherry();3、Application
3.1 从 Laravel \Illuminate\Database\Connectors\ConnectionFactory 数据库连接工厂类中体现出简单工厂模式
ConnectionFactory数据库连接工厂类的createConnection()方法就能直接体现出 简单工厂模式。
Laravel 的数据库连接工厂使用工厂模式来创建和管理数据库连接实例。它根据配置文件和驱动类型创建适当的数据库连接对象,隐藏了连接细节,使得数据库连接的创建和切换变得简单和可扩展。
3.1.1 数据库配置文件入手
// .env 配置
DB_CONNECTION=mysql// config\database.php 文件系统配置
'default' => env('DB_CONNECTION', 'mysql'),
'connections' => [
    'sqlite' => [
        'driver' => 'sqlite',
        'url' => env('DATABASE_URL'),
        'database' => env('DB_DATABASE', database_path('database.sqlite')),
        'prefix' => '',
        'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true),
    ],
    'mysql' => [
        'driver' => 'mysql',
        'url' => env('DATABASE_URL'),
        'host' => env('DB_HOST', '127.0.0.1'),
        'port' => env('DB_PORT', '3306'),
        'database' => env('DB_DATABASE', 'forge'),
        'username' => env('DB_USERNAME', 'forge'),
        'password' => env('DB_PASSWORD', ''),
        'unix_socket' => env('DB_SOCKET', ''),
        'charset' => 'utf8mb4',
        'collation' => 'utf8mb4_unicode_ci',
        'prefix' => '',
        'prefix_indexes' => true,
        'strict' => true,
        'engine' => null,
        'options' => extension_loaded('pdo_mysql') ? array_filter([
            PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
        ]) : [],
    ],
    'pgsql' => [
        'driver' => 'pgsql',
        'url' => env('DATABASE_URL'),
        'host' => env('DB_HOST', '127.0.0.1'),
        'port' => env('DB_PORT', '5432'),
        'database' => env('DB_DATABASE', 'forge'),
        'username' => env('DB_USERNAME', 'forge'),
        'password' => env('DB_PASSWORD', ''),
        'charset' => 'utf8',
        'prefix' => '',
        'prefix_indexes' => true,
        'schema' => 'public',
        'sslmode' => 'prefer',
    ],
    ...
],3.1.2 DB 的 Facade 门面操作,快速执行原始 SQL
- DBFacades 门面操作- select可以快速执行 SQL 查询
那肯定会想,咦~ ,没看见 数据库连接的地方,直接都能使用 SQL 查询,好神奇 😲
// 举个例子
$res = \Illuminate\Support\Facades\DB::select('select version()');
var_dump($res);
/*
array(1) {
  [0]=>
  object(stdClass)#280 (1) {
    ["version()"]=>
    string(6) "8.0.28"
  }
}
*/namespace Illuminate\Support\Facades;
/*
 * @see \Illuminate\Database\DatabaseManager
 * @see \Illuminate\Database\Connection
 */
class DB extends Facade
{
    // 重写 Facade 父类的 getFacadeAccessor() ,返回 db 用于提供容器key,解析对应的实例
    protected static function getFacadeAccessor()
    {
        return 'db';
    }
}
- 门面操作的背后- 首先所有具体 Facade都会继承一个AbstractFacade门面的抽象类
- 由于调用者执行 \Illuminate\Support\Facades\DB::select(),但是又没有select()的静态方法
- 然后会来到 Facade::__callStatic($method, $args)魔术方法 (在静态上下文中调用一个不可访问方法时,__callStatic()会被调用)
- 魔术方法会调用 Facade::getFacadeRoot()来解析门面的具体实例$instance从而调用$instance->$method()
- 具体 $instance实例是从 Laravel 的服务容器中解析出来的
 
- 首先所有具体 
namespace Illuminate\Support\Facades;
abstract class Facade
{
    // 
    public static function __callStatic($method, $args)
    {
        $instance = static::getFacadeRoot();
        if (! $instance) {
            throw new RuntimeException('A facade root has not been set.');
        }
        return $instance->$method(...$args);
    }
    // 从门面后访问真正的对象
    public static function getFacadeRoot()
    {
        return static::resolveFacadeInstance(static::getFacadeAccessor());
    }
    // 获取注册的名称 (如果子类没有实现这个方法,则会抛出异常)
    protected static function getFacadeAccessor()
    {
        throw new RuntimeException('Facade does not implement getFacadeAccessor method.');
    }
    // 从容器中解析门面模式
    protected static function resolveFacadeInstance($name)
    {
        if (is_object($name)) {
            return $name;
        }
        if (isset(static::$resolvedInstance[$name])) {
            return static::$resolvedInstance[$name];
        }
        if (static::$app) {
            return static::$resolvedInstance[$name] = static::$app[$name];
        }
    }
}3.1.3 DB 的 Provider 服务提供者是如何提供的
主要了解,服务提供者是如何注册并启动的。其实就是
Illuminate\Database\DatabaseServiceProvider执行register()在服务提供者注册时执行,用于注册服务和配置,程序启动后执行boot()引导应用程序事件。
- app.php配置文件,可以看到配置的服务提供者- DatabaseServiceProvider,但是这些配置是什么时候被加载的呢
// config\app.php
'providers' => [
    Illuminate\Database\DatabaseServiceProvider::class,
],
'aliases' => [
    'DB' => Illuminate\Support\Facades\DB::class,
],- Illuminate\Database\DatabaseServiceProvider服务提供者的- register()和- boot()方法
namespace Illuminate\Database;
class DatabaseServiceProvider extends ServiceProvider
{
    // 启动引导
    public function boot()
    {
        // 模型的数据库连接解析器
        Model::setConnectionResolver($this->app['db']);
        // 事件调度器
        Model::setEventDispatcher($this->app['events']);
    }
    // 注册
    public function register()
    {
        Model::clearBootedModels();
        // 注册数据库连接服务
        $this->registerConnectionServices();
        // 数据工厂
        $this->registerEloquentFactory();
        // 队列实体解析器
        $this->registerQueueableEntityResolver();
    }
    ...
}- public/index.php入口文件
针对 web http 这种方式,还是从入口文件来看资源的加载。
// 创建 APP 
$app = require_once __DIR__.'/../bootstrap/app.php';
$kernel = $app->make(Kernel::class);
// 执行web处理
$response = $kernel->handle(
    $request = Request::capture()
)->send();- Illuminate\Foundation\Http\KernelHTTP 内核类
Illuminate\Foundation\Http\Kernel 的 handle() 方法 中会调用 sendRequestThroughRouter() 也就是通过路由器来处理请求,在进入 Pipeline 管道处理之前,有一个 $this->bootstrap(); 的调用,里面主要是执行 $app 应用的 bootstrapWith()。 确保应用程序的各个组件都被正确初始化和配置。
// vendor\laravel\framework\src\Illuminate\Foundation\Http\Kernel.php
namespace Illuminate\Foundation\Http;
class Kernel {
    protected $bootstrappers = [
        \Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class,  // 环境变量
        \Illuminate\Foundation\Bootstrap\LoadConfiguration::class       ,  // 应用配置
        \Illuminate\Foundation\Bootstrap\HandleExceptions::class        ,  // 异常处理
        \Illuminate\Foundation\Bootstrap\RegisterFacades::class         ,  // 门面注册
        \Illuminate\Foundation\Bootstrap\RegisterProviders::class       ,  // 服务提供
        \Illuminate\Foundation\Bootstrap\BootProviders::class           ,  // 引导初始化
    ];
    // 为 HTTP 请求引导应用程序
    public function bootstrap()
    {
        // 如果应用程序还未加载过
        if (! $this->app->hasBeenBootstrapped()) {
            $this->app->bootstrapWith($this->bootstrappers());
        }
    }
}- Illuminate\Foundation\ApplicationApp 应用的- bootstrapWith()方法
// vendor\laravel\framework\src\Illuminate\Foundation\Http\Kernel.php
// 运行一组给定的启动类
public function bootstrapWith(array $bootstrappers)
{
    $this->hasBeenBootstrapped = true; // 已经进行了引导操作
    foreach ($bootstrappers as $bootstrapper) {
        // 开始:通过事件系统通知其他监听器引导过程的开始和完成
        $this['events']->dispatch('bootstrapping: '.$bootstrapper, [$this]);
        // 服务容器的 make() 方法,实例化当前迭代到的引导类,然后调用 bootstrap() 方法
        $this->make($bootstrapper)->bootstrap($this);
        // 结束:
        $this['events']->dispatch('bootstrapped: '.$bootstrapper, [$this]);
    }
}- Illuminate\Foundation\Bootstrap\RegisterProviders注册服务提供类的- bootstrap()方法
// vendor\laravel\framework\src\Illuminate\Foundation\Bootstrap\RegisterProviders.php
namespace Illuminate\Foundation\Bootstrap;
class RegisterProviders
{
    // 引导应用服务提供
    public function bootstrap(Application $app)
    {
        $app->registerConfiguredProviders();
    }
}- Illuminate\Foundation\ApplicationApp 应用的- registerConfiguredProviders()方法,用于读取配置文件,加载应用程序的服务提供者
// config/app.php 配置文件
'providers' => [
    /*
    * Laravel Framework Service Providers...
    */
    Illuminate\Auth\AuthServiceProvider::class,
    Illuminate\Broadcasting\BroadcastServiceProvider::class,
    ...
     /*
    * Application Service Providers...
    */
    App\Providers\AppServiceProvider::class,
    App\Providers\AuthServiceProvider::class,
    ...
],// vendor\laravel\framework\src\Illuminate\Foundation\Application.php
// 读取配置文件 `config\app.php`的 `providers` 服务提供商 | 注册所有已配置的提供商
public function registerConfiguredProviders()
{
    // 获取应用程序配置中定义的服务提供者数组包含,然后转成集合
    $providers = Collection::make($this->make('config')->get('app.providers'))
                    ->partition(function ($provider) {
                        return strpos($provider, 'Illuminate\\') === 0;
                    });
    // 0 => 对应集合是 Laravel 框架基础服务提供商
    // 2 => 对应集合是 Laravel App 提供者
    // 1 => 对应集合是 框架发现的需要自动加载的 Package ,通过 PackageManifest ,读取 composer 进行获取的
    $providers->splice(1, 0, [$this->make(PackageManifest::class)->providers()]);
    // 然后会生成一个映射缓存
    // 关于从 composer 自动加载的会对应生成 bootstrap\cache\packages.php
    // 具体可以看 vendor\laravel\framework\src\Illuminate\Foundation\ProviderRepository.php
    // 遍历服务提供者数组, 
    // 实例化每个服务提供者并调用其 register() 方法,
    // 服务提供者的注册操作会将其所提供的服务注册到应用程序 APP 的服务容器中,可以全局使用
    (new ProviderRepository($this, new Filesystem, $this->getCachedServicesPath()))
                ->load($providers->collapse()->toArray());
}- Illuminate\Foundation\ApplicationApp 应用的- register()方法,用于将服务实例化并注册到 APP 容器中
// vendor\laravel\framework\src\Illuminate\Foundation\Application.php
/**
 * Register a service provider with the application.
 *
 * @param  \Illuminate\Support\ServiceProvider|string  $provider
 * @param  bool  $force
 * @return \Illuminate\Support\ServiceProvider
 */
public function register($provider, $force = false)
{
    // 是否已经注册了同名的服务提供者
    if (($registered = $this->getProvider($provider)) && ! $force) {
        return $registered;
    }
    // 如果传入的 $provider 参数是一个字符串,代码会将其解析为服务提供者类的实例
    if (is_string($provider)) {
        $provider = $this->resolveProvider($provider);
    }
    // 调用服务提供者的 register() 方法。在服务提供者类中,register() 方法通常用于注册服务容器绑定和单例
    $provider->register();
    // 定义了 $bindings 属性,代码会遍历该属性,并使用 bind() 方法将其绑定到应用程序的服务容器
    if (property_exists($provider, 'bindings')) {
        foreach ($provider->bindings as $key => $value) {
            $this->bind($key, $value);
        }
    }
    // 定义了 $singletons 属性,代码会遍历该属性,并使用 singleton() 方法将其注册为应用程序的服务容器中的单例
    if (property_exists($provider, 'singletons')) {
        foreach ($provider->singletons as $key => $value) {
            $this->singleton($key, $value);
        }
    }
    // 将服务提供者标记为已注册状态,追加到数组中 ,$this->serviceProviders[]
    $this->markAsRegistered($provider);
    // 应用程序已启动时调用 boot() 方法执行服务提供者的启动逻辑
    if ($this->isBooted()) {
        $this->bootProvider($provider);
    }
    return $provider;
}3.1.4 DB 的连接器工厂类 ConnectionFactory
- 注册数据库连接服务 DatabaseServiceProvider的registerConnectionServices()方法
// vendor\laravel\framework\src\Illuminate\Database\DatabaseServiceProvider.php
// 注册与数据库连接相关的服务和绑定
protected function registerConnectionServices()
{
    // 注册连接工厂,创建实际的数据库连接实例
    $this->app->singleton('db.factory', function ($app) {
        return new ConnectionFactory($app);
    });
    // 数据库管理器用于解决各种连接
    $this->app->singleton('db', function ($app) {
        // !!!!【核心】
        return new DatabaseManager($app, $app['db.factory']);
    });
    // 绑定连接解析器
    $this->app->bind('db.connection', function ($app) {
        return $app['db']->connection();
    });
    // 注册事务管理器
    $this->app->singleton('db.transactions', function ($app) {
        return new DatabaseTransactionsManager;
    });
}- 数据库管理器 DatabaseManager的构造函数中reconnection()来创建连接
// vendor\laravel\framework\src\Illuminate\Database\DatabaseManager.php
namespace Illuminate\Database;
class DatabaseManager implements ConnectionResolverInterface
{
    public function __construct($app, ConnectionFactory $factory)
    {
        $this->app = $app;
        $this->factory = $factory;
        $this->reconnector = function ($connection) {
            $this->reconnect($connection->getNameWithReadWriteType());
        };
    }
    // 获取连接
    public function connection($name = null)
    {
        [$database, $type] = $this->parseConnectionName($name);
        $name = $name ?: $database;
        // !!!! [重点] !!! 
        if (! isset($this->connections[$name])) {
            $this->connections[$name] = $this->configure(
                $this->makeConnection($database), $type
            );
        }
        return $this->connections[$name];
    }
    // 创建连接实例
    protected function makeConnection($name)
    {
        // 获取指定连接名称的配置信息
        $config = $this->configuration($name);
        // 是否有针对该连接名称的扩展注册
        if (isset($this->extensions[$name])) {
            return call_user_func($this->extensions[$name], $config, $name);
        }
        // 是否有针对数据库驱动的扩展注册
        if (isset($this->extensions[$driver = $config['driver']])) {
            return call_user_func($this->extensions[$driver], $config, $name);
        }
        // !!!【核心】!!!
        // 连接工厂($this->factory)创建连接。调用连接工厂的 make() 方法
        return $this->factory->make($config, $name);
    }
    ...
}- 数据库连接器工厂 ConnectionFactory的make()根据驱动实例化不同的连接器实例
// vendor\laravel\framework\src\Illuminate\Database\Connectors\ConnectionFactory.php
class ConnectionFactory
{
    // 根据PDO建立链接
    public function make(array $config, $name = null)
    {
        $config = $this->parseConfig($config, $name);
        if (isset($config['read'])) {
            // 根据读写分离配置来选择创建读写分离连接
            return $this->createReadWriteConnection($config);
        }
        // 创建连接单例
        return $this->createSingleConnection($config);
    }
    // 创建 数据库连接实例
    protected function createSingleConnection(array $config)
    {
        $pdo = $this->createPdoResolver($config);
        // 根据驱动类型(driver)和配置创建具体的数据库连接实例
        return $this->createConnection(
            $config['driver'], $pdo, $config['database'], $config['prefix'], $config
        );
    }
    /**
     * 创建 数据库连接实例
     *
     * @param  string  $driver
     * @param  \PDO|\Closure  $connection
     * @param  string  $database
     * @param  string  $prefix
     * @param  array  $config
     * @return \Illuminate\Database\Connection
     *
     * @throws \InvalidArgumentException
     */
    protected function createConnection($driver, $connection, $database, $prefix = '', array $config = [])
    {
        // 是否连接过
        if ($resolver = Connection::getResolver($driver)) {
            return $resolver($connection, $database, $prefix, $config);
        }
        // 根据驱动类型选择适当的连接类
        switch ($driver) {
            case 'mysql':
                return new MySqlConnection($connection, $database, $prefix, $config);
            case 'pgsql':
                return new PostgresConnection($connection, $database, $prefix, $config);
            case 'sqlite':
                return new SQLiteConnection($connection, $database, $prefix, $config);
            case 'sqlsrv':
                return new SqlServerConnection($connection, $database, $prefix, $config);
        }
        throw new InvalidArgumentException("Unsupported driver [{$driver}].");
    }
}- MySqlConnection对应的- select()方法
// vendor\laravel\framework\src\Illuminate\Database\Connection.php
// vendor\laravel\framework\src\Illuminate\Database\MySqlConnection.php
// vendor\laravel\framework\src\Illuminate\Database\PostgresConnection.php
// 数据库连接的 select() 方法的实现
public function select($query, $bindings = [], $useReadPdo = true)
{
    // 调用 run() 方法,用于执行 SQL 查询
    return $this->run($query, $bindings, function ($query, $bindings) use ($useReadPdo) {
        // 模拟查询模式
        if ($this->pretending()) {
            return [];
        }
        // 创建预处理语句对象
        $statement = $this->prepared(
            $this->getPdoForSelect($useReadPdo)->prepare($query)
        );
        // 绑定参数
        $this->bindValues($statement, $this->prepareBindings($bindings));
        // 执行查询
        $statement->execute();
        // 获取结果集
        return $statement->fetchAll();
    });
}3.1.5 回到开头,用 DB::select() 执行 SQL 语句
\Illuminate\Database\Connectors\ConnectionFactory体现出来简单工厂模式的实现思想;具体可以看源码 ConnectionFactory
- 入口文件;
 HTTP 请求中,通过Illuminate\Foundation\Http\Kernel的handle()方法来接收请求并响应
- 加载 Kernel的$bootstrappers基础引导类;
 在 APP 执行hanle()之前会调用自身的$this->bootstrap();,用来加载各个组件,例如 env 环境变量、config 应用配置、exception 异常处理机制、Facade 门面注册、Provider 注册服务提供商,boot 引导初始化,等等,每个组件会make实例化,并且执行对应的bootstrap()方法
- 调用 RegisterProviders注册提供商方法;
 其中,Provider 服务提供商:Illuminate\Foundation\Bootstrap\RegisterProviders的bootstrap()方法,会调用 APP 的registerConfiguredProviders()方法
- 每一个服务提供商会执行 register()注册到 APP 容器中:
 那么 APP 的registerConfiguredProviders()主要是读取config/app.php配置文件中的providers服务提供商数组,来依次的实例化,并且通过$app->register()先执行每一个基础服务自身的register(),然后再注册到 APP 的服务容器中
- 其中,数据库服务提供商 DatabaseServiceProvider,创建数据库管理器DatabaseManager;
 执行自身register,其中有registerConnectionServices()注册数据库连接服务的方法,利用$this->app->singleton()创建DatabaseManager的数据库管理器单例,构造函数中会有connect()连接相关的调用,也就是靠ConnectionFactory连接工厂完成的
- 数据库连接工厂 ConnectionFactory的make()方法创建连接实例;
 根据传入的驱动,和配置文件,来识别创建不同的连接器,例如Mysql、Pgsql等
- 门面操作 \Illuminate\Support\Facades\DB来调用select()方法;
 主要利用__callStatic()魔术方法,从而解析出来db对应的实例,从而调用select()方法执行 SQL 查询语句
4、 Summary
工厂模式,用于解耦客户端逻辑层和对象实例化过程 ,隐藏了对象实例化的细节,简化了使用者获取对象的方式。
- 工厂模式- 创建型的设计模式
 
- 为什么不直接 new 对象,非要搞个工厂?- 项目中,可能很多地方都用到的一些工具类,每次都 new一下再用,如果可能还需要传入一些参数,比较繁琐,可以利用工厂模式来封装对象的实例化,还可以降低代码重复。
 
- 项目中,可能很多地方都用到的一些工具类,每次都 
- 工厂方法模式和抽象工厂有啥区别- 关注点:工厂方法在于创建单个对象,抽象工厂在于创建一组或者相互依赖的对象,比如前面例子中提到的 苹果工厂(工厂方法)、和本地工厂(抽象工厂)
- 用途:工厂方法更多是解耦作用,把创建逻辑封装到工厂方法中;抽象工厂则是创建一组相关的产品,以达到这些对象可以协同工作
- 实现:工厂方法,通常是有一个抽象类,然后具体工厂继承抽象类,从而创建具体的产品;抽象工厂则是一个抽象接口,具体工厂实现后,创建一组产品对象;
 
- 创建的对象有什么区别- 简单工厂 :用来生产同一等级结构中的任意产品;追加新的产品,需要增加新的类,并且修改工厂方法
- 工厂方法 :用来生产同一等级结构中的固定产品;追加新的产品,无需修改工厂方法,符合开闭原则
- 抽象工场 :用来生成不同产品族的全部产品;
 
 
           手握设计模式宝典
手握设计模式宝典 
         
             
             关于 LearnKu
                关于 LearnKu
               
                     
                     
                     粤公网安备 44030502004330号
 粤公网安备 44030502004330号 
 
推荐文章: