メインコンテンツへ移動

เนื้อหาภาษาไทย / タイ語のコンテンツ

システムガイド 内容確認済み

การใช้งาน Event Dispatcher

บทความนี้แนะนำ Event Dispatcher ซึ่งเป็น pattern การออกแบบที่ช่วยให้ส่วนต่างๆ ของระบบสื่อสารกันได้โดยไม่ต้องรู้จักกันโดยตรง ทำให้ระบบมีความยืดหยุ่น, decoupled,...

การใช้งาน Event Dispatcher

ในบทที่แล้ว เราได้พูดถึง Dependency Injection Container (DIC) ซึ่งช่วยให้เราจัดการ dependencies ของคลาสต่างๆ ได้อย่างเป็นระบบ ในบทนี้ เราจะมาดูอีกหนึ่งเครื่องมือที่มีประโยชน์ในการออกแบบระบบให้มีความยืดหยุ่นและ decoupled นั่นคือ Event Dispatcher

Event Dispatcher คืออะไร?

Event Dispatcher เป็น pattern การออกแบบที่ช่วยให้ส่วนต่างๆ ของระบบสามารถสื่อสารกันได้โดยไม่ต้องรู้จักกันโดยตรง เปรียบเสมือนการประกาศข่าวสาร (event) และให้ผู้ที่สนใจ (listener) คอยรับฟังและตอบสนองต่อข่าวนั้นๆ

ทำไมต้องใช้ Event Dispatcher?

  • Decoupling: ลดการพึ่งพากันระหว่างคลาส ทำให้โค้ดมีความยืดหยุ่นและแก้ไขได้ง่าย
  • Extensibility: เพิ่มความสามารถใหม่ๆ ให้กับระบบได้โดยไม่ต้องแก้ไขโค้ดเดิม
  • Testability: ทดสอบส่วนต่างๆ ของระบบได้อย่างอิสระ

Event Listeners

Event Listener คือคลาสหรือฟังก์ชันที่ทำหน้าที่รับฟัง event ที่ถูกประกาศออกมา เมื่อ event ที่ listener สนใจถูก dispatch ออกมา Listener จะทำงานตามที่กำหนดไว้

ตัวอย่าง: สมมติว่าเรามีระบบจัดการผู้ใช้งาน เมื่อผู้ใช้งานสมัครสมาชิกสำเร็จ เราต้องการส่งอีเมลต้อนรับและบันทึกข้อมูลการสมัครสมาชิก เราสามารถใช้ Event Listener เพื่อจัดการงานเหล่านี้ได้

// Event Listener สำหรับส่งอีเมลต้อนรับ
class WelcomeEmailListener
{
    public function handle(UserRegisteredEvent $event)
    {
        // ส่งอีเมลต้อนรับไปยัง $event->user
        echo "Sending welcome email to " . $event->user->email . "\n";
    }
}

// Event Listener สำหรับบันทึกข้อมูลการสมัครสมาชิก
class RegistrationLoggerListener
{
    public function handle(UserRegisteredEvent $event)
    {
        // บันทึกข้อมูลการสมัครสมาชิกของ $event->user
        echo "Logging registration data for " . $event->user->username . "\n";
    }
}

ในตัวอย่างนี้ WelcomeEmailListener และ RegistrationLoggerListener คือ Event Listener ที่จะทำงานเมื่อ UserRegisteredEvent ถูก dispatch ออกมา

Dispatching Events

การ Dispatch Event คือการประกาศให้ระบบทราบว่ามี event เกิดขึ้น เมื่อ event ถูก dispatch Event Dispatcher จะค้นหา Listener ที่สนใจ event นั้นๆ และเรียกใช้งาน Listener เหล่านั้น

ตัวอย่าง:

use Symfony\Component\EventDispatcher\EventDispatcher;

// สร้าง Event Dispatcher
$dispatcher = new EventDispatcher();

// สร้าง Listener
$welcomeEmailListener = new WelcomeEmailListener();
$registrationLoggerListener = new RegistrationLoggerListener();

// ลงทะเบียน Listener กับ Event Dispatcher
$dispatcher->addListener(UserRegisteredEvent::class, [$welcomeEmailListener, 'handle']);
$dispatcher->addListener(UserRegisteredEvent::class, [$registrationLoggerListener, 'handle']);


// สร้าง User object (สมมติว่ามี)
$user = new User();
$user->username = 'john.doe';
$user->email = 'john.doe@example.com';

// สร้าง Event
$event = new UserRegisteredEvent($user);

// Dispatch Event
$dispatcher->dispatch($event, UserRegisteredEvent::NAME);

ในตัวอย่างนี้ เราสร้าง Event Dispatcher, สร้าง Listener, ลงทะเบียน Listener กับ Event Dispatcher และสุดท้าย dispatch Event UserRegisteredEvent ออกมา

Creating Custom Events

Event คือ object ที่เก็บข้อมูลเกี่ยวกับเหตุการณ์ที่เกิดขึ้น เราสามารถสร้าง Custom Event เพื่อให้เหมาะสมกับความต้องการของระบบของเราได้

ตัวอย่าง:

use Symfony\Contracts\EventDispatcher\Event;

class UserRegisteredEvent extends Event
{
    public const NAME = 'user.registered';

    public User $user;

    public function __construct(User $user)
    {
        $this->user = $user;
    }
}

ในตัวอย่างนี้ เราสร้าง Custom Event ชื่อ UserRegisteredEvent ซึ่งเก็บข้อมูลเกี่ยวกับผู้ใช้งานที่สมัครสมาชิก

Trade-offs และแนวปฏิบัติที่ดี:

  • Performance: การ dispatch event มากเกินไปอาจส่งผลต่อประสิทธิภาพของระบบ ควรใช้ Event Dispatcher เมื่อจำเป็นเท่านั้น
  • Event Naming: ตั้งชื่อ event ให้สื่อความหมายและสอดคล้องกันทั้งระบบ
  • Event Data: ใส่ข้อมูลที่จำเป็นลงใน event เท่านั้น ไม่ควรใส่ข้อมูลที่ไม่เกี่ยวข้อง
  • Listener Priority: Event Dispatcher บางตัว (เช่น Symfony EventDispatcher) รองรับการกำหนด priority ให้กับ Listener เพื่อควบคุมลำดับการทำงานของ Listener แต่ละตัว ควรใช้เมื่อจำเป็นจริงๆ เพราะอาจทำให้การ debug ยากขึ้น
  • Transaction Boundaries: ระมัดระวังเรื่อง transaction boundaries เมื่อใช้ Event Dispatcher ในระบบที่เกี่ยวข้องกับฐานข้อมูล หาก Listener ทำงานที่อาจทำให้เกิดข้อผิดพลาด ควรจัดการ transaction ให้ดี เพื่อป้องกันข้อมูลเสียหาย

ตัวอย่างการใช้งาน Event Dispatcher อย่างง่าย (โดยไม่ต้องใช้ Library ภายนอก)

แม้ว่า library อย่าง Symfony EventDispatcher จะมีฟีเจอร์มากมาย แต่เราสามารถสร้าง Event Dispatcher อย่างง่ายได้ด้วยตัวเอง เพื่อให้เข้าใจหลักการทำงาน

class SimpleEventDispatcher
{
    private array $listeners = [];

    public function addListener(string $eventName, callable $listener): void
    {
        $this->listeners[$eventName][] = $listener;
    }

    public function dispatch(string $eventName, object $event): void
    {
        if (isset($this->listeners[$eventName])) {
            foreach ($this->listeners[$eventName] as $listener) {
                $listener($event);
            }
        }
    }
}

// ตัวอย่างการใช้งาน

class OrderCreatedEvent
{
    public string $orderId;

    public function __construct(string $orderId)
    {
        $this->orderId = $orderId;
    }
}

$dispatcher = new SimpleEventDispatcher();

$dispatcher->addListener('order.created', function (OrderCreatedEvent $event) {
    echo "Sending confirmation email for order: " . $event->orderId . "\n";
});

$dispatcher->addListener('order.created', function (OrderCreatedEvent $event) {
    echo "Logging order creation for order: " . $event->orderId . "\n";
});

$event = new OrderCreatedEvent('12345');
$dispatcher->dispatch('order.created', $event);

โค้ดนี้แสดง Event Dispatcher แบบง่ายที่สามารถเพิ่ม Listener และ Dispatch Event ได้ โดยไม่ต้องพึ่งพา library ภายนอก

สรุป: Event Dispatcher เป็นเครื่องมือที่มีประโยชน์ในการออกแบบระบบให้มีความยืดหยุ่นและ decoupled ช่วยให้เราเพิ่มความสามารถใหม่ๆ ให้กับระบบได้โดยไม่ต้องแก้ไขโค้ดเดิม และช่วยให้เราทดสอบส่วนต่างๆ ของระบบได้อย่างอิสระ อย่างไรก็ตาม ควรใช้ Event Dispatcher เมื่อจำเป็นเท่านั้น และควรระมัดระวังเรื่อง performance, event naming, event data, listener priority และ transaction boundaries

ในบทต่อไป เราจะมาดูกันว่าเราจะใช้ Cache เพื่อเพิ่มประสิทธิภาพให้กับระบบ PHP ของเราได้อย่างไร

Office.in.th Custom Service

この構成をもとにシステムを開発・カスタマイズしたいですか?

Office.in.th は、エンタープライズ Web アプリケーション、API 連携、官民の業務管理システムの設計・開発を承り、長期的なサポートも提供します。

ソフトウェア開発サービスを見る