Laravel 13 工厂模式深度解析

工厂模式是创建型设计模式中最常用的模式之一,它提供了一种创建对象的最佳方式。本文将深入探讨 Laravel 13 中工厂模式的高级用法。

工厂模式基础

什么是工厂模式

工厂模式将对象的创建逻辑封装起来,使客户端代码不直接使用 new 关键字来实例化对象,而是通过工厂来创建。

1
2
3
4
5
6
7
8
<?php

namespace App\Factories;

interface FactoryInterface
{
public function create(array $data = []): mixed;
}

简单工厂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<?php

namespace App\Factories;

use App\Services\Payment\PaymentGatewayInterface;
use App\Services\Payment\StripeGateway;
use App\Services\Payment\PayPalGateway;
use App\Services\Payment\BankTransferGateway;
use InvalidArgumentException;

class PaymentGatewayFactory
{
protected array $gateways = [
'stripe' => StripeGateway::class,
'paypal' => PayPalGateway::class,
'bank' => BankTransferGateway::class,
];

public function create(string $type, array $config = []): PaymentGatewayInterface
{
if (!isset($this->gateways[$type])) {
throw new InvalidArgumentException("Payment gateway [{$type}] not supported.");
}

$gatewayClass = $this->gateways[$type];

return app($gatewayClass, ['config' => $config]);
}

public function getSupportedGateways(): array
{
return array_keys($this->gateways);
}

public function registerGateway(string $type, string $class): void
{
$this->gateways[$type] = $class;
}
}

Laravel 模型工厂

基础模型工厂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<?php

namespace Database\Factories;

use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;

class UserFactory extends Factory
{
protected $model = User::class;

protected static ?string $password;

public function definition(): array
{
return [
'name' => fake()->name(),
'email' => fake()->unique()->safeEmail(),
'email_verified_at' => now(),
'password' => static::$password ??= Hash::make('password'),
'remember_token' => Str::random(10),
];
}

public function unverified(): static
{
return $this->state(fn(array $attributes) => [
'email_verified_at' => null,
]);
}

public function admin(): static
{
return $this->state(fn(array $attributes) => [
'role' => 'admin',
]);
}

public function active(): static
{
return $this->state(fn(array $attributes) => [
'is_active' => true,
]);
}

public function inactive(): static
{
return $this->state(fn(array $attributes) => [
'is_active' => false,
]);
}
}

复杂模型工厂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
<?php

namespace Database\Factories;

use App\Models\Order;
use App\Models\User;
use App\Models\Product;
use Illuminate\Database\Eloquent\Factories\Factory;

class OrderFactory extends Factory
{
protected $model = Order::class;

public function definition(): array
{
return [
'order_number' => 'ORD-' . strtoupper(fake()->bothify('??####')),
'user_id' => User::factory(),
'status' => 'pending',
'subtotal' => fake()->randomFloat(2, 10, 1000),
'tax' => fake()->randomFloat(2, 0, 100),
'shipping' => fake()->randomFloat(2, 0, 50),
'total' => fake()->randomFloat(2, 10, 1200),
'notes' => fake()->optional()->sentence(),
];
}

public function pending(): static
{
return $this->state(fn(array $attributes) => [
'status' => 'pending',
]);
}

public function processing(): static
{
return $this->state(fn(array $attributes) => [
'status' => 'processing',
]);
}

public function completed(): static
{
return $this->state(fn(array $attributes) => [
'status' => 'completed',
'completed_at' => now(),
]);
}

public function cancelled(): static
{
return $this->state(fn(array $attributes) => [
'status' => 'cancelled',
'cancelled_at' => now(),
]);
}

public function forUser(User $user): static
{
return $this->state(fn(array $attributes) => [
'user_id' => $user->id,
]);
}

public function withItems(int $count = 3): static
{
return $this->afterCreating(function (Order $order) use ($count) {
$products = Product::factory()->count($count)->create();

foreach ($products as $product) {
$order->items()->create([
'product_id' => $product->id,
'quantity' => fake()->numberBetween(1, 5),
'price' => $product->price,
'total' => $product->price * fake()->numberBetween(1, 5),
]);
}
});
}
}

抽象工厂模式

抽象工厂接口

1
2
3
4
5
6
7
8
9
10
11
12
<?php

namespace App\Factories\Contracts;

interface ReportFactoryInterface
{
public function createReportBuilder(): ReportBuilderInterface;

public function createReportExporter(): ReportExporterInterface;

public function createReportFormatter(): ReportFormatterInterface;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php

namespace App\Factories\Contracts;

interface ReportBuilderInterface
{
public function setDataSource($source): self;

public function addColumn(string $name, string $field): self;

public function addFilter(string $field, $value): self;

public function build(): Report;
}
1
2
3
4
5
6
7
8
9
10
<?php

namespace App\Factories\Contracts;

interface ReportExporterInterface
{
public function export(Report $report, string $format): string;

public function getSupportedFormats(): array;
}

具体工厂实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php

namespace App\Factories\Reports;

use App\Factories\Contracts\ReportFactoryInterface;

class PdfReportFactory implements ReportFactoryInterface
{
public function createReportBuilder(): ReportBuilderInterface
{
return new PdfReportBuilder();
}

public function createReportExporter(): ReportExporterInterface
{
return new PdfReportExporter();
}

public function createReportFormatter(): ReportFormatterInterface
{
return new PdfReportFormatter();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php

namespace App\Factories\Reports;

use App\Factories\Contracts\ReportFactoryInterface;

class ExcelReportFactory implements ReportFactoryInterface
{
public function createReportBuilder(): ReportBuilderInterface
{
return new ExcelReportBuilder();
}

public function createReportExporter(): ReportExporterInterface
{
return new ExcelReportExporter();
}

public function createReportFormatter(): ReportFormatterInterface
{
return new ExcelReportFormatter();
}
}

工厂提供者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<?php

namespace App\Factories\Reports;

use App\Factories\Contracts\ReportFactoryInterface;
use InvalidArgumentException;

class ReportFactoryProvider
{
protected array $factories = [];

public function __construct()
{
$this->registerDefaultFactories();
}

protected function registerDefaultFactories(): void
{
$this->factories = [
'pdf' => PdfReportFactory::class,
'excel' => ExcelReportFactory::class,
'csv' => CsvReportFactory::class,
'html' => HtmlReportFactory::class,
];
}

public function getFactory(string $type): ReportFactoryInterface
{
if (!isset($this->factories[$type])) {
throw new InvalidArgumentException("Report factory [{$type}] not found.");
}

return app($this->factories[$type]);
}

public function registerFactory(string $type, string $factoryClass): void
{
$this->factories[$type] = $factoryClass;
}

public function getAvailableTypes(): array
{
return array_keys($this->factories);
}
}

服务工厂

通知服务工厂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
<?php

namespace App\Factories;

use App\Contracts\NotificationServiceInterface;
use App\Services\Notifications\EmailNotificationService;
use App\Services\Notifications\SmsNotificationService;
use App\Services\Notifications\PushNotificationService;
use App\Services\Notifications\SlackNotificationService;
use InvalidArgumentException;

class NotificationFactory
{
protected array $channels = [];

public function __construct()
{
$this->registerDefaultChannels();
}

protected function registerDefaultChannels(): void
{
$this->channels = [
'email' => EmailNotificationService::class,
'sms' => SmsNotificationService::class,
'push' => PushNotificationService::class,
'slack' => SlackNotificationService::class,
];
}

public function create(string $channel, array $config = []): NotificationServiceInterface
{
if (!isset($this->channels[$channel])) {
throw new InvalidArgumentException("Notification channel [{$channel}] not supported.");
}

return app($this->channels[$channel], ['config' => $config]);
}

public function createMany(array $channels): array
{
$services = [];

foreach ($channels as $channel => $config) {
if (is_int($channel)) {
$channel = $config;
$config = [];
}

$services[$channel] = $this->create($channel, $config);
}

return $services;
}

public function registerChannel(string $name, string $class): void
{
$this->channels[$name] = $class;
}

public function getAvailableChannels(): array
{
return array_keys($this->channels);
}
}

缓存工厂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?php

namespace App\Factories;

use App\Contracts\CacheDriverInterface;
use App\Services\Cache\RedisCacheDriver;
use App\Services\Cache\MemcachedCacheDriver;
use App\Services\Cache\FileCacheDriver;
use App\Services\Cache\DatabaseCacheDriver;
use InvalidArgumentException;

class CacheFactory
{
public static function create(string $driver, array $config = []): CacheDriverInterface
{
return match ($driver) {
'redis' => new RedisCacheDriver(
$config['host'] ?? '127.0.0.1',
$config['port'] ?? 6379,
$config['database'] ?? 0
),
'memcached' => new MemcachedCacheDriver(
$config['servers'] ?? [['host' => '127.0.0.1', 'port' => 11211]]
),
'file' => new FileCacheDriver(
$config['path'] ?? storage_path('framework/cache')
),
'database' => new DatabaseCacheDriver(
$config['table'] ?? 'cache',
$config['connection'] ?? null
),
default => throw new InvalidArgumentException("Cache driver [{$driver}] not supported."),
};
}
}

静态工厂方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<?php

namespace App\Models;

use App\Enums\UserRole;
use App\Enums\UserStatus;

class User
{
public static function createAdmin(array $data): self
{
return self::create(array_merge($data, [
'role' => UserRole::ADMIN,
'status' => UserStatus::ACTIVE,
]));
}

public static function createCustomer(array $data): self
{
return self::create(array_merge($data, [
'role' => UserRole::CUSTOMER,
'status' => UserStatus::PENDING,
]));
}

public static function createModerator(array $data): self
{
return self::create(array_merge($data, [
'role' => UserRole::MODERATOR,
'status' => UserStatus::ACTIVE,
]));
}

public static function createFromSocialite($socialiteUser): self
{
return self::create([
'name' => $socialiteUser->getName(),
'email' => $socialiteUser->getEmail(),
'avatar' => $socialiteUser->getAvatar(),
'provider_id' => $socialiteUser->getId(),
'email_verified_at' => now(),
]);
}
}

参数化工厂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
<?php

namespace App\Factories;

use App\Contracts\PaymentProcessorInterface;
use App\Services\Payments\Processors\{
CreditCardProcessor,
PayPalProcessor,
BankTransferProcessor,
CryptoProcessor
};

class PaymentProcessorFactory
{
protected array $processors = [];

public function __construct()
{
$this->processors = [
'credit_card' => [
'class' => CreditCardProcessor::class,
'config' => fn() => [
'api_key' => config('services.stripe.key'),
'secret' => config('services.stripe.secret'),
],
],
'paypal' => [
'class' => PayPalProcessor::class,
'config' => fn() => [
'client_id' => config('services.paypal.client_id'),
'secret' => config('services.paypal.secret'),
'mode' => config('services.paypal.mode'),
],
],
'bank_transfer' => [
'class' => BankTransferProcessor::class,
'config' => fn() => [
'bank_code' => config('services.bank.code'),
'account_number' => config('services.bank.account'),
],
],
'crypto' => [
'class' => CryptoProcessor::class,
'config' => fn() => [
'api_key' => config('services.crypto.key'),
'network' => config('services.crypto.network'),
],
],
];
}

public function create(string $method, array $overrides = []): PaymentProcessorInterface
{
if (!isset($this->processors[$method])) {
throw new \InvalidArgumentException("Payment method [{$method}] not supported.");
}

$processor = $this->processors[$method];
$config = array_merge($processor['config'](), $overrides);

return app($processor['class'], ['config' => $config]);
}

public function getAvailableMethods(): array
{
return array_keys($this->processors);
}

public function getMethodConfig(string $method): array
{
if (!isset($this->processors[$method])) {
return [];
}

return $this->processors[$method]['config']();
}
}

工厂与依赖注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use App\Factories\PaymentGatewayFactory;
use App\Factories\NotificationFactory;
use App\Factories\ReportFactoryProvider;

class FactoryServiceProvider extends ServiceProvider
{
public function register(): void
{
$this->app->singleton(PaymentGatewayFactory::class, function ($app) {
$factory = new PaymentGatewayFactory();

$factory->registerGateway('stripe', \App\Services\Payments\StripeGateway::class);
$factory->registerGateway('paypal', \App\Services\Payments\PayPalGateway::class);

return $factory;
});

$this->app->singleton(NotificationFactory::class, function ($app) {
return new NotificationFactory();
});

$this->app->singleton(ReportFactoryProvider::class, function ($app) {
return new ReportFactoryProvider();
});
}
}

在控制器中使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<?php

namespace App\Http\Controllers;

use App\Factories\PaymentGatewayFactory;
use App\Factories\NotificationFactory;
use Illuminate\Http\Request;

class PaymentController extends Controller
{
public function __construct(
protected PaymentGatewayFactory $paymentFactory,
protected NotificationFactory $notificationFactory
) {}

public function process(Request $request)
{
$gateway = $this->paymentFactory->create($request->input('gateway'));

$result = $gateway->charge(
$request->input('amount'),
$request->input('options', [])
);

if ($result->success) {
$notification = $this->notificationFactory->create('email');
$notification->send(
$request->user()->email,
'Payment Successful',
'Your payment has been processed.'
);
}

return response()->json($result);
}
}

测试工厂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
<?php

namespace Tests\Unit\Factories;

use Tests\TestCase;
use App\Factories\PaymentGatewayFactory;
use App\Services\Payment\StripeGateway;
use App\Services\Payment\PayPalGateway;
use InvalidArgumentException;

class PaymentGatewayFactoryTest extends TestCase
{
protected PaymentGatewayFactory $factory;

protected function setUp(): void
{
parent::setUp();
$this->factory = new PaymentGatewayFactory();
}

public function test_can_create_stripe_gateway(): void
{
$gateway = $this->factory->create('stripe');

$this->assertInstanceOf(StripeGateway::class, $gateway);
}

public function test_can_create_paypal_gateway(): void
{
$gateway = $this->factory->create('paypal');

$this->assertInstanceOf(PayPalGateway::class, $gateway);
}

public function test_throws_exception_for_invalid_gateway(): void
{
$this->expectException(InvalidArgumentException::class);

$this->factory->create('invalid');
}

public function test_can_register_custom_gateway(): void
{
$this->factory->registerGateway('custom', CustomGateway::class);

$gateway = $this->factory->create('custom');

$this->assertInstanceOf(CustomGateway::class, $gateway);
}

public function test_can_get_supported_gateways(): void
{
$gateways = $this->factory->getSupportedGateways();

$this->assertIsArray($gateways);
$this->assertContains('stripe', $gateways);
$this->assertContains('paypal', $gateways);
}
}

最佳实践

1. 使用接口定义产品

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php

namespace App\Contracts;

interface LoggerInterface
{
public function log(string $level, string $message, array $context = []): void;

public function emergency(string $message, array $context = []): void;

public function error(string $message, array $context = []): void;

public function info(string $message, array $context = []): void;
}

2. 工厂返回接口类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php

namespace App\Factories;

use App\Contracts\LoggerInterface;

class LoggerFactory
{
public function create(string $channel): LoggerInterface
{
return match ($channel) {
'file' => new FileLogger(),
'database' => new DatabaseLogger(),
'syslog' => new SyslogLogger(),
default => throw new \InvalidArgumentException("Unknown logger: {$channel}"),
};
}
}

3. 支持配置驱动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php

namespace App\Factories;

class CacheFactory
{
public static function createFromConfig(): CacheInterface
{
$driver = config('cache.default');
$config = config("cache.stores.{$driver}");

return self::create($driver, $config);
}
}

总结

Laravel 13 的工厂模式提供了一种灵活的方式来创建对象。通过合理使用工厂模式,可以将对象创建逻辑与业务逻辑分离,提高代码的可维护性和可测试性。