Hotline
0388011834
Trang chủ/ Góc lập trình/Design Patterns

Behavioral Patterns - Mediator



Khái niệm

Mediator Design Pattern là một mẫu thiết kế thuộc nhóm hành vi (Behavioral Pattern) dùng để giảm thiểu sự phụ thuộc hỗn loạn giữa các đối tượng. Nó hạn chế việc giao tiếp trực tiếp giữa các đối tượng và buộc chúng giao tiếp thông qua một đối tượng trung gian.

    1.  

Các thành phần chính

  • Mediator (Interface/Abstract Class): Định nghĩa giao diện để giao tiếp với các thành phần.
  • Concrete Mediator: Triển khai phương thức của lớp giao diện và quản lý sự phối hợp giữa các đối tượng thành phần.
  • Components: Các đối tượng thành phần tham gia tương tác, chúng sẽ gọi tới Mediator khi cần giao tiếp.

Ưu điểm

  • Tăng tính tái sử dụng: Có thể dễ dàng tái sử dụng các thành phần riêng lẻ.
  • Dễ bảo trì: Logic tương tác tập trung tại một nơi.
  • Nguyên tắc Trách nhiệm Đơn nhất (Single Responsibility Principle): Có thể tách việc giao tiếp giữa các thành phần khác nhau vào nơi duy nhất, giúp dễ hiểu và dễ bảo trì.
  • Nguyên tắc Mở/Đóng (Open/Closed Principle): Có thể thêm các Mediator mới mà không cần thay đổi các thành phần hiện tại.

Nhược điểm

  • Mediator trở thành “God object”: Lớp Mediator có thể trở thành quá lớn và khó bảo trì nếu phải quản lý việc giao tiếp giữa quá nhiều thành phần với nhau.

Ví dụ thực tế

  • Bài toán

Tiếp tục với bài toán về quản lý nhà hàng, chúng ta sẽ có một kịch bản mà có thể áp dụng được mẫu thiết kế Mediator này.

Sau khi khách hàng gọi món và chờ đợi. Lúc này giữa người phục vụ và đầu bếp sẽ làm việc liên tục với nhau, chẳng hạn: Người phục vụ thông báo cho đầu bếp để nấu món ăn, đầu bếp nhận được thông báo thì sẽ thực hiện việc nấu nướng. Khi đầu bếp nấu món ăn xong thì lại cần thông báo cho người phục vụ biết để mang món ăn tới khách hàng.

Như vậy việc giao tiếp qua lại giữa người phục vụ và đầu bếp sẽ thực hiện liên tục đến khi món ăn hoàn thành và mang tới khách hàng.

  • Phân tích bài toán

Chúng ta sẽ có người phục vụ và đầu bếp là hai đối tượng chính cần giao tiếp với nhau. Người phục vụ đưa thực đơn tới đầu bếp để đầu bếp thực hiện nấu nướng, sau đó đầu bếp thực hiện nấu nướng và thông báo lại người phục vụ biết.

Đôi khi việc chờ đợi nấu nướng diễn ra quá lâu, thì lúc này người phục vụ lại thông báo lại người đầu bếp để biết tình hình.

Như vậy việc gửi thông báo và nhận thông báo của người phục vụ và đầu bếp diễn ra liên tục cho tới khi món ăn hoàn thành.

  • Cách triển khai

Ta sẽ tạo thành phần đầu tiên Mediator. Thành phần này chỉ có một phương thức để cho các thành phần Component sử dụng.

    public interface IDatBanMediator
    {
        void ThongBao(object sender, string ev);
    }

Thành phần tiếp theo sẽ cụ thể hóa cho Mediator.

    class DatBanMediator : IDatBanMediator
    {
        private GoiDoAnComponent _component1;

 
        private ChuanBiDoAnComponent _component2;

 
        public DatBanMediator(GoiDoAnComponent component1, ChuanBiDoAnComponent component2)
        {
            this._component1 = component1;
            this._component1.SetMediator(this);
            this._component2 = component2;
            this._component2.SetMediator(this);
        }

 
        public void ThongBao(object sender, string ev)
        {
            if (ev == "GoiCom")
            {
                Console.WriteLine("--- DatBanMediator da tiep nhan viec Goi Com va se thuc hien thong bao toi Dau Bep de chuan bi mon.");
                this._component2.ChuanBiCom();
            }
            if (ev == "GoiPho")
            {
                Console.WriteLine("--- DatBanMediator da tiep nhan viec Goi Pho va se thuc hien thong bao toi Dau Bep de chuan bi mon.");
                this._component2.ChuanBiPho();
            }
            if (ev == "NauComXong")
            {
                Console.WriteLine("--- DatBanMediator da tiep nhan viec Nau Com xong tu Dau Bep va se thuc hien thong bao toi Nguoi phuc vu.");
                this._component1.LayCom();
            }
            if (ev == "NauPhoXong")
            {
                Console.WriteLine("--- DatBanMediator da tiep nhan viec Nau Pho xong tu Dau Bep va se thuc hien thong bao toi Nguoi phuc vu.");
                this._component1.LayPho();
            }
        }
    }

Trong thành phần này sẽ phụ trách việc phối hợp giữa các thành phần Component thông qua việc triển khai phương thức ThongBao từ lớp giao diện Mediator.

Cuối cùng sẽ là việc triển khai cho các thành phần Component. Ta sẽ tạo một lớp cơ sở để làm lớp cha cho các lớp Component con.

    class BaseComponent
    {
        protected IDatBanMediator _mediator;

 
        public BaseComponent(IDatBanMediator mediator = null)
        {
            this._mediator = mediator;
        }

 
        public void SetMediator(IDatBanMediator mediator)
        {
            this._mediator = mediator;
        }
    }
    class GoiDoAnComponent : BaseComponent
    {
        public void GoiCom()
        {
            Console.WriteLine("Nguoi phuc vu: Khach hang da goi mon Com. Thuc hien thong bao toi DatBanMediator.");

 
            this._mediator.ThongBao(this, "GoiCom");
        }

 
        public void GoiPho()
        {
            Console.WriteLine("Nguoi phuc vu: Khach hang da goi mon Pho. Thuc hien thong bao toi DatBanMediator.");

 
            this._mediator.ThongBao(this, "GoiPho");
        }

 
        public void LayCom()
        {
            Console.WriteLine("Nguoi phuc vu: Mang Com tu Dau Bep cho Khach hang.");
        }

 
        public void LayPho()
        {
            Console.WriteLine("Nguoi phuc vu: Mang Pho tu Dau Bep cho Khach hang.");
        }
    }

 
    class ChuanBiDoAnComponent : BaseComponent
    {
        public void ChuanBiCom()
        {
            Console.WriteLine("Dau Bep: Da chuan bi xong mon Com. Thuc hien thong bao toi DatBanMediator.");

 
            this._mediator.ThongBao(this, "NauComXong");
        }

 
        public void ChuanBiPho()
        {
            Console.WriteLine("Dau Bep: Da chuan bi xong mon Pho. Thuc hien thong bao toi DatBanMediator.");

 
            this._mediator.ThongBao(this, "NauPhoXong");
        }
    }

Dưới đây sẽ là hàm Main mô phỏng các kịch bản gọi đồ ăn từ khách hàng:

    class ClientMediator
    {
        public void Main()
        {
            // The client code.
            GoiDoAnComponent component1 = new GoiDoAnComponent();
            ChuanBiDoAnComponent component2 = new ChuanBiDoAnComponent();
            new DatBanMediator(component1, component2);

 
            Console.WriteLine("========== Kich ban 1: Khach hang dat mon Com.");
            component1.GoiCom();

 
            Console.WriteLine();

 
            Console.WriteLine("========== Kich ban 2: Khach hang dat mon Pho.");
            component1.GoiPho();
        }
    }

Kết quả sau khi chạy chương trình:

    1.  

Kết luận

Như vậy, qua bài viết này, tôi hi vọng các bạn sẽ nắm được khái niệm cũng như biết được việc khi nào thì nên áp dụng mẫu thiết kế Mediator vào trong ứng dụng của mình.

Ví dụ tôi đưa đôi khi không chính xác và thuyết phục hoàn toàn, rất mong sự góp ý từ phía các bạn để tôi có thể củng cố thêm kiến thức cho bản thân mình.