iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 27
1

文同步分享於個人blog

  • 定義


定義一個 Mediator 物件用來封裝一組物件的互動方式。Mediator 藉由避免物件間相互直接的引用,從而降低它們之間的耦合程度,並且可以讓我們獨立地改變這些物件間的互動方式。

在生活上有許多事情會有複雜的關係,例如一間軟體公司產品,需要開發團隊、設計團隊、行銷團隊、客服團隊等等才能有一個較完善的產品團隊。而開發跟設計可能需要協調產品頁面,行銷也要跟開發討論產品功能如何符合市場,客服也會跟設計反應哪些流程需要改善。如此一來團隊之間的關係就會非常的複雜,如下圖:

網狀結構
網狀結構

每個團隊自己去跟不同團隊有關係,我們稱這結構叫網狀結構。但很明顯這樣的一個結夠並不是很理想,所以產品需要一個產品經理,也就是中介者。由產品經理來處理所有團隊的,結構就會變成下圖:

星狀結構
星狀結構

這種結構稱作星狀結構,現在每個團隊都經由產品經理去跟其他團隊溝通,團隊間的相依性就降低了許多。

  • Mediator Pattern 成員


成員 功用
Mediator 抽象中介者。主要為中介者接口,提供同事物件方法。
ConcreteMediator 實體中介者。實作抽象中介者,定義一個集合來管理同事物件,處理個同事間的關係,所以她依賴於同事。
Colleague 抽象同事類。定義同事類別的接口,保存中介者物件,提供同事物件抽象方法。
ConcreteColleague 實體同事類。實作抽象同事,當需要與其他同事物件工作時,需要由中介者物件負責後續的接洽。

M

  • Mediator Pattern 實作


剛剛有提到,一個產品需要工程、設計、客服、行銷團隊,而團隊之間的溝通則交由產品經理去做。所以我們首先要先定義抽象中介者給產品經理實作。


import java.util.*;

enum Type { // 建立一個列舉方便等等程式使用
    ENGINEERING, DESIGN, SERVICE, MARKETING
}

//抽象中介者
abstract class Mediator {
    public abstract void register(Type type, Colleague colleague);
    public abstract void relay(Type type, String msg); //轉發
}

再來產品經理需要實作抽象中介者。

//實體中介者
class ProductManager extends Mediator {
    private Map<Type, Colleague> colleagues = new HashMap<Type, Colleague>();
    public void register(Type type, Colleague colleague) {
        colleague.setMedium(this);
        colleagues.put(type, colleague);
    }
    public void relay(Type type, String msg) {
        Colleague toCl = colleagues.get(type);
        
        toCl.receive(msg);
    }
}

再來定義個團隊的抽象類別,有設定中介者的方法以及發送接收的抽象方法。

//抽象同事類
abstract class Colleague {
    protected Mediator mediator;
    public void setMedium(Mediator mediator) {
        this.mediator = mediator;
    }   
    public abstract void receive(String msg);   
    public abstract void send(Type type, String msg);
}

接著再依照不同的團隊,實作不同的邏輯在繼承的方法內。而在send最後會由中介者去轉發這個訊息到對應的同事類別內。

//實體同事類
class EngineeringTeam extends Colleague {
    public void receive(String msg) {
        System.out.println("工程團隊收到請求: " + msg + "。");
    }   
    public void send(Type type, String msg) {
        System.out.println("工程團隊發出請求: " + msg + "。");
        mediator.relay(type, msg); //請中介者轉發
    }
}
//實體同事類
class DesignTeam extends Colleague {
    public void receive(String msg) {
        System.out.println("設計團隊收到請求: " + msg + "。");
    }   
    public void send(Type type, String msg) {
        System.out.println("設計團隊發出請求: " + msg + "。");
        mediator.relay(type, msg); //請中介者轉發
    }
}
//實體同事類
class MarketingTeam extends Colleague {
    public void receive(String msg) {
        System.out.println("行銷團隊收到請求: " + msg + "。");
    }   
    public void send(Type type, String msg) {
        System.out.println("行銷團隊發出請求: " + msg + "。");
        mediator.relay(type, msg); //請中介者轉發
    }
}
//實體同事類
class CustomerService extends Colleague {
    public void receive(String msg) {
        System.out.println("客服團隊收到請求: " + msg + "。");
    }   
    public void send(Type type, String msg) {
        System.out.println("客服團隊發出請求: " + msg + "。");
        mediator.relay(type, msg); //請中介者轉發
    }
}

最後在Client端,先將各種團隊的物件建立起來,並都傳到中介者pm內。然後若某一個團隊有問題,只要將問題種類及問題發送出去,對應的團隊就會收到訊息。

public class MediatorPattern {
    public static void main(String[] args){
        ProductManager pm = new ProductManager();
        Colleague c1, c2, c3, c4;
        c1 = new DesignTeam();
        c2 = new MarketingTeam();
        c3 = new CustomerService();
        c4 = new EngineeringTeam();
        pm.register(Type.DESIGN, c1);
        pm.register(Type.MARKETING, c2);
        pm.register(Type.SERVICE, c3);
        pm.register(Type.ENGINEERING, c4);
        c1.send(Type.SERVICE, "設計有問題");
        System.out.println("-------------");
        c1.send(Type.ENGINEERING, "軟體設計有問題");
        System.out.println("-------------");
    }
}

output

設計團隊發出請求: 設計有問題。
客服團隊收到請求: 設計有問題。
-------------
設計團隊發出請求: 軟體設計有問題。
工程團隊收到請求: 軟體設計有問題。
-------------
  • Mediator Pattern 簡化


而在開發中我們也可以簡化中介者模式:
1. 不定義中介者接口,將實體中介者物件創建成單例。
2. 同事物件不持有中介者,而是在需要的時候直接取得中介者物件並使用。

import java.util.*;
enum Type {
    ENGINEERING, DESIGN, SERVICE, MARKETING
}
// 簡單單例中介者
class SimpleProductManager {
    private static SimpleProductManager spm = new SimpleProductManager();   
    private Map<Type, SimpleColleague> colleagues = new HashMap<Type, SimpleColleague>();
    private SimpleProductManager(){}   
    public static SimpleProductManager getMedium(){    // 讓同事物件取得中介者物件
        return(spm);   
    }
    public void register(Type type, SimpleColleague colleague) {
        colleagues.put(type, colleague);
    }
    public void relay(Type type, String msg) {
        SimpleColleague toCl = colleagues.get(type);
        toCl.receive(msg);
    }
}

interface SimpleColleague {
    void receive(String msg);   
    void send(Type type, String msg);
}
// 實體同事類
class SimpleEngineeringTeam implements SimpleColleague {
    SimpleEngineeringTeam(){
        SimpleProductManager spm = SimpleProductManager.getMedium(); // 需要使用時直接取得
        spm.register(Type.ENGINEERING, this); // 並使用
    }
    public void receive(String msg){    
        System.out.println("工程團隊收到請求: " + msg + "。");
    }   
    public void send(Type type, String msg) {
        SimpleProductManager spm = SimpleProductManager.getMedium();
        System.out.println("工程團隊發出請求: " + msg + "。");
        spm.relay(type, msg); // 中介者轉發
    }
}
//實體同事類
class SimpleDesignTeam implements SimpleColleague {
    SimpleDesignTeam(){
        SimpleProductManager spm = SimpleProductManager.getMedium();
        spm.register(Type.DESIGN, this);
    }
    public void receive(String msg) {    
        System.out.println("設計團隊收到請求: " + msg + "。");   
    }   
    public void send(Type type, String msg) {
        SimpleProductManager spm = SimpleProductManager.getMedium();
        System.out.println("設計團隊發出請求: " + msg + "。");
        spm.relay(type, msg); // 中介者轉發
    }
}
public class SimpleMediatorPattern {
    public static void main(String[] args) {
        SimpleColleague c1,c2;
        c1 = new SimpleEngineeringTeam();
        c2 = new SimpleDesignTeam();
        c1.send(Type.DESIGN, "設計有問題");
        System.out.println("-------------");
        c1.send(Type.DESIGN, "UX設計有問題");
    }
}

output

工程團隊發出請求: 設計有問題。
設計團隊收到請求: 設計有問題。
-------------
工程團隊發出請求: UX設計有問題。
設計團隊收到請求: UX設計有問題。

以上就是中介者模式以及中介者模式的簡化,藉由使用中介者模式,可以降低物件之間的耦合,也可增加物件的獨立性。

  • 小結


Mediator Pattern 目標

定義一個中介者物件來處理其餘物件的互動,藉此降低物件之間的耦合性。

Mediator Pattern 成員
Mediator:抽象中介者。主要為中介者接口,提供同事物件方法。
ConcreteMediator:實體中介者。實作抽象中介者,定義一個集合來管理同事物件,處理個同事間的關係,所以她依賴於同事。
Colleague:抽象同事類。定義同事類別的接口,保存中介者物件,提供同事物件抽象方法。
ConcreteColleague:實體同事類。實作抽象同事,當需要與其他同事物件工作時,需要由中介者物件負責後續的接洽。
Mediator Pattern 優缺點
優點
1. 降低物件之間的耦合性,讓物件容易重複使用。
2. 物件之間一對多的關聯性變成一對一,提高系統靈活性,也是整體容易維護及擴充。
缺點
1. 同事類別過多時,中介者責任很大,會使系統提升一定程度的複雜性,
Iterator Pattern的使用時機
1. 需要集合物件提供多種遍歷方法。
2. 需要遍歷不同的集合結構而提供統一的接口時。
  • 範例程式碼


範例1:實作中介者模式
範例2:實作簡單單例中介者

  • References


中介者模式(详解版)
設計模式:中介者模式 (Mediator Pattern)
中介者模式 (Mediator Pattern)

圖片來源:
網狀結構
星狀結構


上一篇
[Day26] 狀態模式 | State Pattern
下一篇
[Day28] 觀察者模式 | Observer Pattern
系列文
從生活中認識Design Pattern30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
weybrian
iT邦新手 5 級 ‧ 2021-07-21 11:41:38

這篇是Mediator,不是Memento

很喜歡你寫的design pattern,對我幫助很多

我要留言

立即登入留言