TungNT (Blue)

tungnt.blue@gmail.com

User Tools

Site Tools


development:software-architecture:design-patterns:observer

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
development:software-architecture:design-patterns:observer [2024/08/13 07:20] – [Observer] tungntdevelopment:software-architecture:design-patterns:observer [2024/08/19 09:51] (current) – [Ví dụ 3:] tungnt
Line 10: Line 10:
  
 {{ :development:software-architecture:design-patterns:observer-comic-1-en-2x.png |}} {{ :development:software-architecture:design-patterns:observer-comic-1-en-2x.png |}}
-====== Tham khảo ======+ 
 +**Sử dụng 2 interface có sẵn của PHP (SplSubject, SplObserver):** 
 +  * http://php.net/manual/en/class.splsubject.php 
 +  * http://php.net/manual/en/class.splobserver.php 
 +  * https://www.php.net/manual/en/class.splobjectstorage.php 
 + 
 +===== Ví dụ 1: ===== 
 + 
 +<file php> 
 +<?php 
 + 
 +/** 
 + * Subject,that who makes news 
 + */ 
 +class Newspaper implements \SplSubject{ 
 +    private $name; 
 +    private $observers = array(); 
 +    private $content; 
 +     
 +    public function __construct($name) { 
 +        $this->name = $name; 
 +    } 
 + 
 +    //add observer 
 +    public function attach(\SplObserver $observer) { 
 +        $this->observers[] = $observer; 
 +    } 
 +     
 +    //remove observer 
 +    public function detach(\SplObserver $observer) { 
 +         
 +        $key = array_search($observer,$this->observers, true); 
 +        if($key){ 
 +            unset($this->observers[$key]); 
 +        } 
 +    } 
 +     
 +    //set breakouts news 
 +    public function breakOutNews($content) { 
 +        $this->content = $content; 
 +        $this->notify(); 
 +    } 
 +     
 +    public function getContent() { 
 +        return $this->content." ({$this->name})"; 
 +    } 
 +     
 +    //notify observers(or some of them) 
 +    public function notify() { 
 +        foreach ($this->observers as $value) { 
 +            $value->update($this); 
 +        } 
 +    } 
 +
 + 
 +/** 
 + * Observer,that who recieves news 
 + */ 
 +class Reader implements SplObserver{ 
 +    private $name; 
 +     
 +    public function __construct($name) { 
 +        $this->name = $name; 
 +    } 
 +     
 +    public function update(\SplSubject $subject) { 
 +        echo $this->name.' is reading breakout news <b>'.$subject->getContent().'</b><br>'; 
 +    } 
 +
 + 
 +$newspaper = new Newspaper('Newyork Times'); 
 + 
 +$allen = new Reader('Allen'); 
 +$jim = new Reader('Jim'); 
 +$linda = new Reader('Linda'); 
 + 
 +//add reader 
 +$newspaper->attach($allen); 
 +$newspaper->attach($jim); 
 +$newspaper->attach($linda); 
 + 
 +//remove reader 
 +$newspaper->detach($linda); 
 + 
 +//set break outs 
 +$newspaper->breakOutNews('USA break down!'); 
 + 
 +//=====output====== 
 +//Allen is reading breakout news USA break down! (Newyork Times) 
 +//Jim is reading breakout news USA break down! (Newyork Times) 
 +</file> 
 + 
 +===== Ví dụ 2: ===== 
 + 
 +<file php> 
 +<?php  
 + 
 +// Example implementation of Observer design pattern: 
 + 
 +class MyObserver1 implements SplObserver { 
 +    public function update(SplSubject $subject) { 
 +        echo __CLASS__ . ' - ' . $subject->getName(); 
 +    } 
 +
 + 
 +class MyObserver2 implements SplObserver { 
 +    public function update(SplSubject $subject) { 
 +        echo __CLASS__ . ' - ' . $subject->getName(); 
 +    } 
 +
 + 
 +class MySubject implements SplSubject { 
 +    private $_observers; 
 +    private $_name; 
 + 
 +    public function __construct($name) { 
 +        $this->_observers = new SplObjectStorage(); 
 +        $this->_name = $name; 
 +    } 
 + 
 +    public function attach(SplObserver $observer) { 
 +        $this->_observers->attach($observer); 
 +    } 
 + 
 +    public function detach(SplObserver $observer) { 
 +        $this->_observers->detach($observer); 
 +    } 
 + 
 +    public function notify() { 
 +        foreach ($this->_observers as $observer) { 
 +            $observer->update($this); 
 +        } 
 +    } 
 + 
 +    public function getName() { 
 +        return $this->_name; 
 +    } 
 +
 + 
 +$observer1 = new MyObserver1(); 
 +$observer2 = new MyObserver2(); 
 + 
 +$subject = new MySubject("test"); 
 + 
 +$subject->attach($observer1); 
 +$subject->attach($observer2); 
 +$subject->notify(); 
 + 
 +/*  
 +will output: 
 + 
 +MyObserver1 - test 
 +MyObserver2 - test 
 +*/ 
 + 
 +$subject->detach($observer2); 
 +$subject->notify(); 
 + 
 +/*  
 +will output: 
 + 
 +MyObserver1 - test 
 +*/ 
 + 
 +?> 
 +</file> 
 + 
 +===== Ví dụ 3: ===== 
 + 
 +{{ :development:software-architecture:design-patterns:observerstructure-2x.png |}} 
 + 
 +<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://php.net/manual/en/class.splsubject.php 
 + * 
 +     interface SplSubject 
 +     { 
 +         // Attach an observer to the subject. 
 +         public function attach(SplObserver $observer); 
 + * 
 +         // Detach an observer from the subject. 
 +         public function detach(SplObserver $observer); 
 + * 
 +         // Notify all observers about an event. 
 +         public function notify(); 
 +     } 
 + * 
 + * There's also a built-in interface for Observers: 
 + * 
 + * @link http://php.net/manual/en/class.splobserver.php 
 + * 
 +     interface SplObserver 
 +     { 
 +         public function update(SplSubject $subject); 
 +     } 
 + */ 
 + 
 +/** 
 + * 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's state, essential to 
 +     * all subscribers, is stored in this variable. 
 +     */ 
 +    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->observers = new \SplObjectStorage(); 
 +    } 
 + 
 +    /** 
 +     * The subscription management methods. 
 +     */ 
 +    public function attach(\SplObserver $observer): void 
 +    { 
 +        echo "Subject: Attached an observer.\n"; 
 +        $this->observers->attach($observer); 
 +    } 
 + 
 +    public function detach(\SplObserver $observer): void 
 +    { 
 +        $this->observers->detach($observer); 
 +        echo "Subject: Detached an observer.\n"; 
 +    } 
 + 
 +    /** 
 +     * Trigger an update in each subscriber. 
 +     */ 
 +    public function notify(): void 
 +    { 
 +        echo "Subject: Notifying observers...\n"; 
 +        foreach ($this->observers as $observer) { 
 +            $observer->update($this); 
 +        } 
 +    } 
 + 
 +    /** 
 +     * 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(): void 
 +    { 
 +        echo "\nSubject: I'm doing something important.\n"; 
 +        $this->state = rand(0, 10); 
 + 
 +        echo "Subject: My state has just changed to: {$this->state}\n"; 
 +        $this->notify(); 
 +    } 
 +
 + 
 +/** 
 + * 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->state < 3) { 
 +            echo "ConcreteObserverA: Reacted to the event.\n"; 
 +        } 
 +    } 
 +
 + 
 +class ConcreteObserverB implements \SplObserver 
 +
 +    public function update(\SplSubject $subject): void 
 +    { 
 +        if ($subject->state == 0 || $subject->state >= 2) { 
 +            echo "ConcreteObserverB: Reacted to the event.\n"; 
 +        } 
 +    } 
 +
 + 
 +/** 
 + * The client code. 
 + */ 
 + 
 +$subject = new Subject(); 
 + 
 +$o1 = new ConcreteObserverA(); 
 +$subject->attach($o1); 
 + 
 +$o2 = new ConcreteObserverB(); 
 +$subject->attach($o2); 
 + 
 +$subject->someBusinessLogic(); 
 +$subject->someBusinessLogic(); 
 + 
 +$subject->detach($o2); 
 + 
 +$subject->someBusinessLogic(); 
 +</file> 
 + 
 +===== 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->observers["*"] = []; 
 +    } 
 + 
 +    private function initEventGroup(string $event = "*"): void 
 +    { 
 +        if (!isset($this->observers[$event])) { 
 +            $this->observers[$event] = []; 
 +        } 
 +    } 
 + 
 +    private function getEventObservers(string $event = "*"): array 
 +    { 
 +        $this->initEventGroup($event); 
 +        $group = $this->observers[$event]; 
 +        $all = $this->observers["*"]; 
 + 
 +        return array_merge($group, $all); 
 +    } 
 + 
 +    public function attach(\SplObserver $observer, string $event = "*"): void 
 +    { 
 +        $this->initEventGroup($event); 
 + 
 +        $this->observers[$event][] = $observer; 
 +    } 
 + 
 +    public function detach(\SplObserver $observer, string $event = "*"): void 
 +    { 
 +        foreach ($this->getEventObservers($event) as $key => $s) { 
 +            if ($s === $observer) { 
 +                unset($this->observers[$event][$key]); 
 +            } 
 +        } 
 +    } 
 + 
 +    public function notify(string $event = "*", $data = null): void 
 +    { 
 +        echo "UserRepository: Broadcasting the '$event' event.\n"; 
 +        foreach ($this->getEventObservers($event) as $observer) { 
 +            $observer->update($this, $event, $data); 
 +        } 
 +    } 
 + 
 +    // Here are the methods representing the business logic of the class. 
 + 
 +    public function initialize($filename): void 
 +    { 
 +        echo "UserRepository: Loading user records from a file.\n"; 
 +        // ... 
 +        $this->notify("users:init", $filename); 
 +    } 
 + 
 +    public function createUser(array $data): User 
 +    { 
 +        echo "UserRepository: Creating a user.\n"; 
 + 
 +        $user = new User(); 
 +        $user->update($data); 
 + 
 +        $id = bin2hex(openssl_random_pseudo_bytes(16)); 
 +        $user->update(["id" => $id]); 
 +        $this->users[$id] = $user; 
 + 
 +        $this->notify("users:created", $user); 
 + 
 +        return $user; 
 +    } 
 + 
 +    public function updateUser(User $user, array $data): User 
 +    { 
 +        echo "UserRepository: Updating a user.\n"; 
 + 
 +        $id = $user->attributes["id"]; 
 +        if (!isset($this->users[$id])) { 
 +            return null; 
 +        } 
 + 
 +        $user = $this->users[$id]; 
 +        $user->update($data); 
 + 
 +        $this->notify("users:updated", $user); 
 + 
 +        return $user; 
 +    } 
 + 
 +    public function deleteUser(User $user): void 
 +    { 
 +        echo "UserRepository: Deleting a user.\n"; 
 + 
 +        $id = $user->attributes["id"]; 
 +        if (!isset($this->users[$id])) { 
 +            return; 
 +        } 
 + 
 +        unset($this->users[$id]); 
 + 
 +        $this->notify("users:deleted", $user); 
 +    } 
 +
 + 
 +/** 
 + * Let's keep the User class trivial since it's not the focus of our example. 
 + */ 
 +class User 
 +
 +    public $attributes = []; 
 + 
 +    public function update($data): void 
 +    { 
 +        $this->attributes = array_merge($this->attributes, $data); 
 +    } 
 +
 + 
 +/** 
 + * This Concrete Component logs any events it's subscribed to. 
 + */ 
 +class Logger implements \SplObserver 
 +
 +    private $filename; 
 + 
 +    public function __construct($filename) 
 +    { 
 +        $this->filename = $filename; 
 +        if (file_exists($this->filename)) { 
 +            unlink($this->filename); 
 +        } 
 +    } 
 + 
 +    public function update(\SplSubject $repository, string $event = null, $data = null): void 
 +    { 
 +        $entry = date("Y-m-d H:i:s") . ": '$event' with data '" . json_encode($data) . "'\n"; 
 +        file_put_contents($this->filename, $entry, FILE_APPEND); 
 + 
 +        echo "Logger: I've written '$event' entry to the log.\n"; 
 +    } 
 +
 + 
 +/** 
 + * 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->adminEmail = $adminEmail; 
 +    } 
 + 
 +    public function update(\SplSubject $repository, string $event = null, $data = null): void 
 +    { 
 +        // mail($this->adminEmail, 
 +        //     "Onboarding required", 
 +        //     "We have a new user. Here's his info: " .json_encode($data)); 
 + 
 +        echo "OnboardingNotification: The notification has been emailed!\n"; 
 +    } 
 +
 + 
 +/** 
 + * The client code. 
 + */ 
 + 
 +$repository = new UserRepository(); 
 +$repository->attach(new Logger(__DIR__ . "/log.txt"), "*"); 
 +$repository->attach(new OnboardingNotification("1@example.com"), "users:created"); 
 + 
 +$repository->initialize(__DIR__ . "/users.csv"); 
 + 
 +// ... 
 + 
 +$user = $repository->createUser([ 
 +    "name" => "John Smith", 
 +    "email" => "john99@example.com", 
 +]); 
 + 
 +// ... 
 + 
 +$repository->deleteUser($user); 
 +</file> 
 + 
 +===== Tham khảo =====
  
   * https://refactoring.guru/design-patterns/observer   * https://refactoring.guru/design-patterns/observer
-  * {{ :development:software-architecture:design-patterns:the_observer_design_pattern_in_laravel.pdf |The Observer Design Pattern in Laravel}}+  * https://moezmissaoui.medium.com/the-observer-design-pattern-in-laravel-a-comprehensive-guide-cdac37601e7a 
 +  * https://medium.com/@mansha99/better-laravel-monoliths-observer-pattern-ff9119db5b65 
 +  * https://www.youtube.com/watch?v=7J5pRc2vzWk
  
development/software-architecture/design-patterns/observer.1723533639.txt.gz · Last modified: 2024/08/13 07:20 by tungnt

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki