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