development:software-architecture:design-patterns:observer
Differences
This shows you the differences between two versions of the page.
Next revision | Previous revision | ||
development:software-architecture:design-patterns:observer [2024/08/06 06:59] – created - external edit 127.0.0.1 | development:software-architecture:design-patterns:observer [2024/08/19 09:51] (current) – [Ví dụ 3:] tungnt | ||
---|---|---|---|
Line 1: | Line 1: | ||
====== Observer ====== | ====== Observer ====== | ||
- | ====== Tham khảo | + | Hãy tưởng tượng rằng bạn có hai loại đối tượng: Khách hàng và Cửa hàng. Khách hàng rất quan tâm đến một thương hiệu sản phẩm cụ thể (ví dụ, đó là một mẫu iPhone mới) sẽ sớm có mặt tại cửa hàng. |
+ | |||
+ | Khách hàng có thể đến cửa hàng mỗi ngày và kiểm tra tình trạng còn hàng của sản phẩm. Nhưng trong khi sản phẩm vẫn đang trên đường, hầu hết các chuyến đi này sẽ vô nghĩa. | ||
+ | |||
+ | Mặt khác, cửa hàng có thể gửi hàng tấn email (có thể được coi là thư rác) cho tất cả khách hàng mỗi khi có sản phẩm mới. Điều này sẽ giúp một số khách hàng không phải đến cửa hàng liên tục. Đồng thời, nó sẽ làm phiền những khách hàng khác không quan tâm đến sản phẩm mới. | ||
+ | |||
+ | Có vẻ như chúng ta có xung đột. Hoặc là khách hàng mất thời gian kiểm tra tình trạng sản phẩm hoặc cửa hàng mất nguồn lực khi thông báo nhầm khách hàng. | ||
+ | |||
+ | {{ : | ||
+ | |||
+ | **Sử dụng 2 interface có sẵn của PHP (SplSubject, | ||
+ | * http:// | ||
+ | * http:// | ||
+ | * https:// | ||
+ | |||
+ | ===== Ví dụ 1: ===== | ||
+ | |||
+ | <file php> | ||
+ | <?php | ||
+ | |||
+ | /** | ||
+ | * Subject, | ||
+ | */ | ||
+ | class Newspaper implements \SplSubject{ | ||
+ | private $name; | ||
+ | private $observers = array(); | ||
+ | private $content; | ||
+ | |||
+ | public function __construct($name) { | ||
+ | $this-> | ||
+ | } | ||
+ | |||
+ | //add observer | ||
+ | public function attach(\SplObserver $observer) { | ||
+ | $this-> | ||
+ | } | ||
+ | |||
+ | //remove observer | ||
+ | public function detach(\SplObserver $observer) { | ||
+ | |||
+ | $key = array_search($observer, | ||
+ | if($key){ | ||
+ | unset($this-> | ||
+ | } | ||
+ | } | ||
+ | |||
+ | //set breakouts news | ||
+ | public function breakOutNews($content) { | ||
+ | $this-> | ||
+ | $this-> | ||
+ | } | ||
+ | |||
+ | public function getContent() { | ||
+ | return $this-> | ||
+ | } | ||
+ | |||
+ | //notify observers(or some of them) | ||
+ | public function notify() { | ||
+ | foreach ($this-> | ||
+ | $value-> | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * Observer, | ||
+ | */ | ||
+ | class Reader implements SplObserver{ | ||
+ | private $name; | ||
+ | |||
+ | public function __construct($name) { | ||
+ | $this-> | ||
+ | } | ||
+ | |||
+ | public function update(\SplSubject $subject) { | ||
+ | echo $this-> | ||
+ | } | ||
+ | } | ||
+ | |||
+ | $newspaper = new Newspaper(' | ||
+ | |||
+ | $allen = new Reader(' | ||
+ | $jim = new Reader(' | ||
+ | $linda = new Reader(' | ||
+ | |||
+ | //add reader | ||
+ | $newspaper-> | ||
+ | $newspaper-> | ||
+ | $newspaper-> | ||
+ | |||
+ | //remove reader | ||
+ | $newspaper-> | ||
+ | |||
+ | //set break outs | ||
+ | $newspaper-> | ||
+ | |||
+ | // | ||
+ | //Allen is reading breakout news USA break down! (Newyork Times) | ||
+ | //Jim is reading breakout news USA break down! (Newyork Times) | ||
+ | </ | ||
+ | |||
+ | ===== Ví dụ 2: ===== | ||
+ | |||
+ | <file php> | ||
+ | <?php | ||
+ | |||
+ | // Example implementation of Observer design pattern: | ||
+ | |||
+ | class MyObserver1 implements SplObserver { | ||
+ | public function update(SplSubject $subject) { | ||
+ | echo __CLASS__ . ' - ' . $subject-> | ||
+ | } | ||
+ | } | ||
+ | |||
+ | class MyObserver2 implements SplObserver { | ||
+ | public function update(SplSubject $subject) { | ||
+ | echo __CLASS__ . ' - ' . $subject-> | ||
+ | } | ||
+ | } | ||
+ | |||
+ | class MySubject implements SplSubject { | ||
+ | private $_observers; | ||
+ | private $_name; | ||
+ | |||
+ | public function __construct($name) { | ||
+ | $this-> | ||
+ | $this-> | ||
+ | } | ||
+ | |||
+ | public function attach(SplObserver $observer) { | ||
+ | $this-> | ||
+ | } | ||
+ | |||
+ | public function detach(SplObserver $observer) { | ||
+ | $this-> | ||
+ | } | ||
+ | |||
+ | public function notify() { | ||
+ | foreach ($this-> | ||
+ | $observer-> | ||
+ | } | ||
+ | } | ||
+ | |||
+ | public function getName() { | ||
+ | return $this-> | ||
+ | } | ||
+ | } | ||
+ | |||
+ | $observer1 = new MyObserver1(); | ||
+ | $observer2 = new MyObserver2(); | ||
+ | |||
+ | $subject = new MySubject(" | ||
+ | |||
+ | $subject-> | ||
+ | $subject-> | ||
+ | $subject-> | ||
+ | |||
+ | /* | ||
+ | will output: | ||
+ | |||
+ | MyObserver1 - test | ||
+ | MyObserver2 - test | ||
+ | */ | ||
+ | |||
+ | $subject-> | ||
+ | $subject-> | ||
+ | |||
+ | /* | ||
+ | will output: | ||
+ | |||
+ | MyObserver1 - test | ||
+ | */ | ||
+ | |||
+ | ?> | ||
+ | </ | ||
+ | |||
+ | ===== Ví dụ 3: ===== | ||
+ | |||
+ | {{ : | ||
+ | |||
+ | <file php> | ||
+ | <?php | ||
+ | |||
+ | namespace RefactoringGuru\Observer\Conceptual; | ||
+ | |||
+ | /** | ||
+ | * PHP has a couple of built-in interfaces related to the Observer pattern. | ||
+ | * | ||
+ | * Here's what the Subject interface looks like: | ||
+ | * | ||
+ | * @link http:// | ||
+ | * | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | * | ||
+ | | ||
+ | | ||
+ | * | ||
+ | | ||
+ | | ||
+ | | ||
+ | * | ||
+ | * There' | ||
+ | * | ||
+ | * @link http:// | ||
+ | * | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | */ | ||
+ | |||
+ | /** | ||
+ | * The Subject owns some important state and notifies observers when the state | ||
+ | * changes. | ||
+ | */ | ||
+ | class Subject implements \SplSubject | ||
+ | { | ||
+ | /** | ||
+ | * @var int For the sake of simplicity, the Subject' | ||
+ | * all subscribers, | ||
+ | */ | ||
+ | public $state; | ||
+ | |||
+ | /** | ||
+ | * @var \SplObjectStorage List of subscribers. In real life, the list of | ||
+ | * subscribers can be stored more comprehensively (categorized by event | ||
+ | * type, etc.). | ||
+ | */ | ||
+ | private $observers; | ||
+ | |||
+ | public function __construct() | ||
+ | { | ||
+ | $this-> | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * The subscription management methods. | ||
+ | */ | ||
+ | public function attach(\SplObserver $observer): void | ||
+ | { | ||
+ | echo " | ||
+ | $this-> | ||
+ | } | ||
+ | |||
+ | public function detach(\SplObserver $observer): void | ||
+ | { | ||
+ | $this-> | ||
+ | echo " | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * Trigger an update in each subscriber. | ||
+ | */ | ||
+ | public function notify(): void | ||
+ | { | ||
+ | echo " | ||
+ | foreach ($this-> | ||
+ | $observer-> | ||
+ | } | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * Usually, the subscription logic is only a fraction of what a Subject can | ||
+ | * really do. Subjects commonly hold some important business logic, that | ||
+ | * triggers a notification method whenever something important is about to | ||
+ | * happen (or after it). | ||
+ | */ | ||
+ | public function someBusinessLogic(): | ||
+ | { | ||
+ | echo " | ||
+ | $this-> | ||
+ | |||
+ | echo " | ||
+ | $this-> | ||
+ | } | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * Concrete Observers react to the updates issued by the Subject they had been | ||
+ | * attached to. | ||
+ | */ | ||
+ | class ConcreteObserverA implements \SplObserver | ||
+ | { | ||
+ | public function update(\SplSubject $subject): void | ||
+ | { | ||
+ | if ($subject-> | ||
+ | echo " | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | class ConcreteObserverB implements \SplObserver | ||
+ | { | ||
+ | public function update(\SplSubject $subject): void | ||
+ | { | ||
+ | if ($subject-> | ||
+ | echo " | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * The client code. | ||
+ | */ | ||
+ | |||
+ | $subject = new Subject(); | ||
+ | |||
+ | $o1 = new ConcreteObserverA(); | ||
+ | $subject-> | ||
+ | |||
+ | $o2 = new ConcreteObserverB(); | ||
+ | $subject-> | ||
+ | |||
+ | $subject-> | ||
+ | $subject-> | ||
+ | |||
+ | $subject-> | ||
+ | |||
+ | $subject-> | ||
+ | </ | ||
+ | |||
+ | ===== Ví dụ 4: ===== | ||
+ | |||
+ | <file php> | ||
+ | <?php | ||
+ | |||
+ | namespace RefactoringGuru\Observer\RealWorld; | ||
+ | |||
+ | /** | ||
+ | * The UserRepository represents a Subject. Various objects are interested in | ||
+ | * tracking its internal state, whether it's adding a new user or removing one. | ||
+ | */ | ||
+ | class UserRepository implements \SplSubject | ||
+ | { | ||
+ | /** | ||
+ | * @var array The list of users. | ||
+ | */ | ||
+ | private $users = []; | ||
+ | |||
+ | // Here goes the actual Observer management infrastructure. Note that it's | ||
+ | // not everything that our class is responsible for. Its primary business | ||
+ | // logic is listed below these methods. | ||
+ | |||
+ | /** | ||
+ | * @var array | ||
+ | */ | ||
+ | private $observers = []; | ||
+ | |||
+ | public function __construct() | ||
+ | { | ||
+ | // A special event group for observers that want to listen to all | ||
+ | // events. | ||
+ | $this-> | ||
+ | } | ||
+ | |||
+ | private function initEventGroup(string $event = " | ||
+ | { | ||
+ | if (!isset($this-> | ||
+ | $this-> | ||
+ | } | ||
+ | } | ||
+ | |||
+ | private function getEventObservers(string $event = " | ||
+ | { | ||
+ | $this-> | ||
+ | $group = $this-> | ||
+ | $all = $this-> | ||
+ | |||
+ | return array_merge($group, | ||
+ | } | ||
+ | |||
+ | public function attach(\SplObserver $observer, string $event = " | ||
+ | { | ||
+ | $this-> | ||
+ | |||
+ | $this-> | ||
+ | } | ||
+ | |||
+ | public function detach(\SplObserver $observer, string $event = " | ||
+ | { | ||
+ | foreach ($this-> | ||
+ | if ($s === $observer) { | ||
+ | unset($this-> | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | public function notify(string $event = " | ||
+ | { | ||
+ | echo " | ||
+ | foreach ($this-> | ||
+ | $observer-> | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // Here are the methods representing the business logic of the class. | ||
+ | |||
+ | public function initialize($filename): | ||
+ | { | ||
+ | echo " | ||
+ | // ... | ||
+ | $this-> | ||
+ | } | ||
+ | |||
+ | public function createUser(array $data): User | ||
+ | { | ||
+ | echo " | ||
+ | |||
+ | $user = new User(); | ||
+ | $user-> | ||
+ | |||
+ | $id = bin2hex(openssl_random_pseudo_bytes(16)); | ||
+ | $user-> | ||
+ | $this-> | ||
+ | |||
+ | $this-> | ||
+ | |||
+ | return $user; | ||
+ | } | ||
+ | |||
+ | public function updateUser(User $user, array $data): User | ||
+ | { | ||
+ | echo " | ||
+ | |||
+ | $id = $user-> | ||
+ | if (!isset($this-> | ||
+ | return null; | ||
+ | } | ||
+ | |||
+ | $user = $this-> | ||
+ | $user-> | ||
+ | |||
+ | $this-> | ||
+ | |||
+ | return $user; | ||
+ | } | ||
+ | |||
+ | public function deleteUser(User $user): void | ||
+ | { | ||
+ | echo " | ||
+ | |||
+ | $id = $user-> | ||
+ | if (!isset($this-> | ||
+ | return; | ||
+ | } | ||
+ | |||
+ | unset($this-> | ||
+ | |||
+ | $this-> | ||
+ | } | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * Let's keep the User class trivial since it's not the focus of our example. | ||
+ | */ | ||
+ | class User | ||
+ | { | ||
+ | public $attributes = []; | ||
+ | |||
+ | public function update($data): | ||
+ | { | ||
+ | $this-> | ||
+ | } | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * This Concrete Component logs any events it's subscribed to. | ||
+ | */ | ||
+ | class Logger implements \SplObserver | ||
+ | { | ||
+ | private $filename; | ||
+ | |||
+ | public function __construct($filename) | ||
+ | { | ||
+ | $this-> | ||
+ | if (file_exists($this-> | ||
+ | unlink($this-> | ||
+ | } | ||
+ | } | ||
+ | |||
+ | public function update(\SplSubject $repository, | ||
+ | { | ||
+ | $entry = date(" | ||
+ | file_put_contents($this-> | ||
+ | |||
+ | echo " | ||
+ | } | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * This Concrete Component sends initial instructions to new users. The client | ||
+ | * is responsible for attaching this component to a proper user creation event. | ||
+ | */ | ||
+ | class OnboardingNotification implements \SplObserver | ||
+ | { | ||
+ | private $adminEmail; | ||
+ | |||
+ | public function __construct($adminEmail) | ||
+ | { | ||
+ | $this-> | ||
+ | } | ||
+ | |||
+ | public function update(\SplSubject $repository, | ||
+ | { | ||
+ | // mail($this-> | ||
+ | // " | ||
+ | // " | ||
+ | |||
+ | echo " | ||
+ | } | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * The client code. | ||
+ | */ | ||
+ | |||
+ | $repository = new UserRepository(); | ||
+ | $repository-> | ||
+ | $repository-> | ||
+ | |||
+ | $repository-> | ||
+ | |||
+ | // ... | ||
+ | |||
+ | $user = $repository-> | ||
+ | " | ||
+ | " | ||
+ | ]); | ||
+ | |||
+ | // ... | ||
+ | |||
+ | $repository-> | ||
+ | </ | ||
+ | |||
+ | ===== Tham khảo ===== | ||
* https:// | * https:// | ||
+ | * https:// | ||
+ | * https:// | ||
+ | * https:// | ||
+ |
development/software-architecture/design-patterns/observer.1722927590.txt.gz · Last modified: 2024/08/06 06:59 by 127.0.0.1