Laravel 13 中介者模式深度解析 中介者模式是一种行为型设计模式,它通过将对象间的交互集中到一个中介者对象中,从而降低对象间的耦合度。本文将深入探讨 Laravel 13 中中介者模式的高级用法。
中介者模式基础 什么是中介者模式 中介者模式定义了一个对象,它封装了一组对象之间的交互方式,使对象之间不需要显式相互引用。
1 2 3 4 5 6 7 8 <?php namespace App \Contracts ;interface MediatorInterface { public function notify (object $sender , string $event , mixed $data = null ): void ; }
1 2 3 4 5 6 7 8 <?php namespace App \Contracts ;interface ColleagueInterface { public function setMediator (MediatorInterface $mediator ): void ; }
聊天室中介者 聊天中介者接口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?php namespace App \Contracts \Chat ;interface ChatMediatorInterface { public function addUser (User $user ): void ; public function removeUser (User $user ): void ; public function sendMessage (string $message , User $sender , ?string $recipient = null ): void ; public function broadcast (string $message , User $sender ): void ; }
聊天室实现 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 <?php namespace App \Mediators \Chat ;use App \Contracts \Chat \ChatMediatorInterface ;use App \Models \User ;use Illuminate \Support \Collection ;class ChatRoom implements ChatMediatorInterface { protected Collection $users ; protected string $name ; public function __construct (string $name ) { $this ->name = $name ; $this ->users = collect (); } public function addUser (User $user ): void { if (!$this ->users->contains ('id' , $user ->id)) { $this ->users->push ($user ); $this ->broadcast ("{$user->name} has joined the chat" , $user ); } } public function removeUser (User $user ): void { $this ->users = $this ->users->reject (fn($u ) => $u ->id === $user ->id); $this ->broadcast ("{$user->name} has left the chat" , $user ); } public function sendMessage (string $message , User $sender , ?string $recipient = null ): void { if ($recipient ) { $targetUser = $this ->users->firstWhere ('name' , $recipient ); if ($targetUser ) { $targetUser ->receivePrivateMessage ($message , $sender ); } return ; } $this ->broadcast ($message , $sender ); } public function broadcast (string $message , User $sender ): void { foreach ($this ->users as $user ) { if ($user ->id !== $sender ->id) { $user ->receiveMessage ($message , $sender ); } } } public function getUsers ( ): Collection { return $this ->users; } public function getName ( ): string { return $this ->name; } }
用户类 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 App \Models ;use App \Contracts \Chat \ChatMediatorInterface ;class User { protected ?ChatMediatorInterface $mediator = null ; public int $id ; public string $name ; public function __construct (int $id , string $name ) { $this ->id = $id ; $this ->name = $name ; } public function setMediator (ChatMediatorInterface $mediator ): void { $this ->mediator = $mediator ; } public function send (string $message , ?string $recipient = null ): void { if ($this ->mediator) { $this ->mediator->sendMessage ($message , $this , $recipient ); } } public function receiveMessage (string $message , User $sender ): void { echo "[{$this->name} ] Received from {$sender->name} : {$message} \n" ; } public function receivePrivateMessage (string $message , User $sender ): void { echo "[{$this->name} ] Private from {$sender->name} : {$message} \n" ; } public function join (ChatMediatorInterface $chatRoom ): void { $this ->mediator = $chatRoom ; $chatRoom ->addUser ($this ); } public function leave ( ): void { if ($this ->mediator) { $this ->mediator->removeUser ($this ); $this ->mediator = null ; } } }
表单组件中介者 表单中介者接口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <?php namespace App \Contracts \Form ;interface FormMediatorInterface { public function notify (object $component , string $event , mixed $data = null ): void ; public function registerComponent (string $name , FormComponentInterface $component ): void ; public function getComponent (string $name ): ?FormComponentInterface ; public function validate ( ): bool ; public function getData ( ): array ; }
表单组件接口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <?php namespace App \Contracts \Form ;interface FormComponentInterface { public function setMediator (FormMediatorInterface $mediator ): void ; public function getName ( ): string ; public function getValue ( ): mixed ; public function setValue (mixed $value ): void ; public function validate ( ): array ; public function render ( ): string ; }
表单中介者实现 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 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 <?php namespace App \Mediators \Form ;use App \Contracts \Form \FormMediatorInterface ;use App \Contracts \Form \FormComponentInterface ;use Illuminate \Support \Collection ;class FormMediator implements FormMediatorInterface { protected Collection $components ; protected array $errors = []; public function __construct ( ) { $this ->components = collect (); } public function registerComponent (string $name , FormComponentInterface $component ): void { $component ->setMediator ($this ); $this ->components->put ($name , $component ); } public function getComponent (string $name ): ?FormComponentInterface { return $this ->components->get ($name ); } public function notify (object $component , string $event , mixed $data = null ): void { $componentName = $component ->getName (); switch ($event ) { case 'change' : $this ->handleComponentChange ($componentName , $data ); break ; case 'focus' : $this ->handleComponentFocus ($componentName ); break ; case 'blur' : $this ->handleComponentBlur ($componentName ); break ; case 'error' : $this ->errors[$componentName ] = $data ; break ; } } public function validate ( ): bool { $this ->errors = []; $isValid = true ; foreach ($this ->components as $component ) { $errors = $component ->validate (); if (!empty ($errors )) { $this ->errors[$component ->getName ()] = $errors ; $isValid = false ; } } return $isValid ; } public function getData ( ): array { $data = []; foreach ($this ->components as $name => $component ) { $data [$name ] = $component ->getValue (); } return $data ; } public function getErrors ( ): array { return $this ->errors; } protected function handleComponentChange (string $name , mixed $value ): void { unset ($this ->errors[$name ]); if ($name === 'country' ) { $this ->updateStates ($value ); } if ($name === 'zipCode' ) { $this ->validateZipCode ($value ); } } protected function handleComponentFocus (string $name ): void { } protected function handleComponentBlur (string $name ): void { $component = $this ->getComponent ($name ); if ($component ) { $errors = $component ->validate (); if (!empty ($errors )) { $this ->errors[$name ] = $errors ; } } } protected function updateStates (string $country ): void { $stateComponent = $this ->getComponent ('state' ); if ($stateComponent ) { $states = $this ->getStatesForCountry ($country ); $stateComponent ->setOptions ($states ); } } protected function validateZipCode (string $zipCode ): void { } protected function getStatesForCountry (string $country ): array { return config ("states.{$country} " , []); } }
UI 组件中介者 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 <?php namespace App \Mediators \UI ;use App \Contracts \MediatorInterface ;class UIMediator implements MediatorInterface { protected $button ; protected $textbox ; protected $checkbox ; protected $listbox ; public function setButton ($button ): void { $this ->button = $button ; } public function setTextbox ($textbox ): void { $this ->textbox = $textbox ; } public function setCheckbox ($checkbox ): void { $this ->checkbox = $checkbox ; } public function setListbox ($listbox ): void { $this ->listbox = $listbox ; } public function notify (object $sender , string $event , mixed $data = null ): void { if ($sender === $this ->checkbox) { $this ->handleCheckboxEvent ($event , $data ); } if ($sender === $this ->textbox) { $this ->handleTextboxEvent ($event , $data ); } if ($sender === $this ->listbox) { $this ->handleListboxEvent ($event , $data ); } } protected function handleCheckboxEvent (string $event , mixed $data ): void { if ($event === 'toggle' ) { $this ->textbox->setEnabled ($data ); $this ->button->setEnabled ($data ); } } protected function handleTextboxEvent (string $event , mixed $data ): void { if ($event === 'change' ) { $this ->button->setEnabled (!empty ($data )); } } protected function handleListboxEvent (string $event , mixed $data ): void { if ($event === 'select' ) { $this ->textbox->setValue ($data ); } } }
事件总线中介者 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 <?php namespace App \Mediators \Events ;use Illuminate \Support \Collection ;class EventBus { protected Collection $listeners ; protected Collection $wildcardListeners ; public function __construct ( ) { $this ->listeners = collect (); $this ->wildcardListeners = collect (); } public function subscribe (string $event , callable $handler ): void { if ($event === '*' ) { $this ->wildcardListeners->push ($handler ); } else { if (!$this ->listeners->has ($event )) { $this ->listeners->put ($event , collect ()); } $this ->listeners->get ($event )->push ($handler ); } } public function unsubscribe (string $event , callable $handler ): void { if ($event === '*' ) { $this ->wildcardListeners = $this ->wildcardListeners->reject (fn($h ) => $h === $handler ); } elseif ($this ->listeners->has ($event )) { $this ->listeners->put ( $event , $this ->listeners->get ($event )->reject (fn($h ) => $h === $handler ) ); } } public function publish (string $event , mixed $data = null ): void { if ($this ->listeners->has ($event )) { foreach ($this ->listeners->get ($event ) as $handler ) { $handler ($event , $data ); } } foreach ($this ->wildcardListeners as $handler ) { $handler ($event , $data ); } } public function hasListeners (string $event ): bool { return $this ->listeners->has ($event ) || $this ->wildcardListeners->isNotEmpty (); } }
订单处理中介者 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 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 <?php namespace App \Mediators \Order ;use App \Contracts \MediatorInterface ;use App \Services \InventoryService ;use App \Services \PaymentService ;use App \Services \ShippingService ;use App \Services \NotificationService ;class OrderMediator implements MediatorInterface { protected InventoryService $inventory ; protected PaymentService $payment ; protected ShippingService $shipping ; protected NotificationService $notification ; public function __construct ( InventoryService $inventory , PaymentService $payment , ShippingService $shipping , NotificationService $notification ) { $this ->inventory = $inventory ; $this ->payment = $payment ; $this ->shipping = $shipping ; $this ->notification = $notification ; } public function notify (object $sender , string $event , mixed $data = null ): void { switch ($event ) { case 'order.created' : $this ->handleOrderCreated ($data ); break ; case 'payment.completed' : $this ->handlePaymentCompleted ($data ); break ; case 'payment.failed' : $this ->handlePaymentFailed ($data ); break ; case 'inventory.reserved' : $this ->handleInventoryReserved ($data ); break ; case 'inventory.insufficient' : $this ->handleInventoryInsufficient ($data ); break ; case 'shipping.created' : $this ->handleShippingCreated ($data ); break ; case 'order.cancelled' : $this ->handleOrderCancelled ($data ); break ; } } protected function handleOrderCreated ($order ): void { $this ->inventory->reserve ($order , $this ); $this ->notification->send ( $order ->user, 'Order Created' , "Your order #{$order->id} has been created." ); } protected function handlePaymentCompleted ($data ): void { $order = $data ['order' ]; $this ->shipping->createShipment ($order , $this ); $this ->notification->send ( $order ->user, 'Payment Successful' , "Payment for order #{$order->id} has been processed." ); } protected function handlePaymentFailed ($data ): void { $order = $data ['order' ]; $this ->inventory->release ($order ); $this ->notification->send ( $order ->user, 'Payment Failed' , "Payment for order #{$order->id} has failed." ); } protected function handleInventoryReserved ($data ): void { $order = $data ['order' ]; $this ->payment->process ($order , $this ); } protected function handleInventoryInsufficient ($data ): void { $order = $data ['order' ]; $order ->update (['status' => 'cancelled' ]); $this ->notification->send ( $order ->user, 'Order Cancelled' , "Order #{$order->id} has been cancelled due to insufficient inventory." ); } protected function handleShippingCreated ($data ): void { $order = $data ['order' ]; $trackingNumber = $data ['tracking_number' ]; $order ->update ([ 'status' => 'shipped' , 'tracking_number' => $trackingNumber , ]); $this ->notification->send ( $order ->user, 'Order Shipped' , "Your order #{$order->id} has been shipped. Tracking: {$trackingNumber} " ); } protected function handleOrderCancelled ($data ): void { $order = $data ['order' ]; $this ->inventory->release ($order ); if ($order ->payment_status === 'completed' ) { $this ->payment->refund ($order ); } $this ->notification->send ( $order ->user, 'Order Cancelled' , "Your order #{$order->id} has been cancelled." ); } }
测试中介者模式 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 Tests \Unit \Mediators ;use Tests \TestCase ;use App \Mediators \Chat \ChatRoom ;use App \Models \User ;class MediatorTest extends TestCase { public function test_chat_room_mediator ( ): void { $chatRoom = new ChatRoom ('General' ); $user1 = new User (1 , 'Alice' ); $user2 = new User (2 , 'Bob' ); $user3 = new User (3 , 'Charlie' ); $user1 ->join ($chatRoom ); $user2 ->join ($chatRoom ); $user3 ->join ($chatRoom ); $this ->assertCount (3 , $chatRoom ->getUsers ()); $user1 ->send ('Hello everyone!' ); $user1 ->leave (); $this ->assertCount (2 , $chatRoom ->getUsers ()); } public function test_private_message ( ): void { $chatRoom = new ChatRoom ('Private' ); $user1 = new User (1 , 'Alice' ); $user2 = new User (2 , 'Bob' ); $user1 ->join ($chatRoom ); $user2 ->join ($chatRoom ); $user1 ->send ('Hello Bob!' , 'Bob' ); } }
最佳实践 1. 中介者应该专注 1 2 3 4 5 6 7 8 9 10 11 12 13 <?php class FocusedMediator implements MediatorInterface { public function notify (object $sender , string $event , mixed $data = null ): void { $this ->handleEvent ($event , $data ); } protected function handleEvent (string $event , mixed $data ): void { } }
2. 使用事件映射 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <?php class EventMappedMediator implements MediatorInterface { protected array $eventHandlers = []; public function __construct ( ) { $this ->eventHandlers = [ 'user.created' => [$this , 'handleUserCreated' ], 'user.deleted' => [$this , 'handleUserDeleted' ], ]; } public function notify (object $sender , string $event , mixed $data = null ): void { if (isset ($this ->eventHandlers[$event ])) { call_user_func ($this ->eventHandlers[$event ], $data ); } } }
3. 避免中介者过于庞大 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 <?php class ModularMediator implements MediatorInterface { protected array $modules = []; public function registerModule (string $name , MediatorInterface $module ): void { $this ->modules[$name ] = $module ; } public function notify (object $sender , string $event , mixed $data = null ): void { [$module , $subEvent ] = $this ->parseEvent ($event ); if (isset ($this ->modules[$module ])) { $this ->modules[$module ]->notify ($sender , $subEvent , $data ); } } protected function parseEvent (string $event ): array { $parts = explode ('.' , $event , 2 ); return [$parts [0 ], $parts [1 ] ?? '' ]; } }
总结 Laravel 13 的中介者模式提供了一种有效的方式来降低对象间的耦合。通过合理使用中介者模式,可以创建更加模块化、可维护的系统架构,同时保持组件间的通信清晰有序。