中介者模式

童話述說我的結局發表於2021-08-22

一、定義

中介者模式(Mediator Pattern) 又稱為調解者模式或調停者模式。用一箇中介物件封裝一系列的物件互動,中介者使各物件不需要顯示地相互作用,從而使其耦合鬆散,而且可以獨立地改變它們之間的互動。屬於行為型模式。中介者模式包裝了一系列物件相互作用的方式,使得這些物件不必相互明顯作用。從而使它們可以鬆散耦合。當某些物件之間的作用發生改變時,不會立即影響其他的一些物件之間的作用。保證這些作用可以彼此獨立的變化。其核心思想是,通過中介者解耦系統各層次物件的直接耦合,層次物件的對外依賴通訊統統交由中介者轉發。

中介者模式主要包含四個角色:

  • 抽象中介者(Mediator) :定義統一的介面, 用於各同事角色之間的通訊;
  • 具體中介者(Concrete Mediator) :從具體的同事物件接收訊息, 向具體同事物件發出命令,協調各同事間的協作;
  • 抽象同事類(Colleague) :每一個同事物件均需要依賴中介者角色, 與其他同事間通訊時,交由中介者進行轉發協作;
  • 具體同事類(Concrete Colleague) :負責實現自發行為(Self-Method) , 轉發依賴方法(Dep-Method) 交由中介者進行協調。

 

 

 

二、中介者模式的案例

 在現實生活中,中介者的存在是不可缺少的,如果沒有了中介者,我們就不能與遠方的朋友進行交流了。各個同事物件將會相互進行引用,如果每個物件都與多個物件進行互動時,將會形成如下圖所示的網狀結構。

 

 

 從上圖可以發現,每個物件之間過度耦合,這樣的既不利於資訊的複用也不利於擴充套件。如果引入了中介者模式,那麼物件之間的關係將變成星型結構,採用中介者模式之後會形成如下圖所示的結構:

 

 

從上圖可以發現,使用中介者模式之後,任何一個類的變化,只會影響中介者和類本身,不像之前的設計,任何一個類的變化都會引起其關聯所有類的變化。這樣的設計大大減少了系統的耦合度。

中介者模式是用來降低多個物件和類之間的通訊複雜性。這種模式通過提供一箇中介類,將系統各層次物件間的多對多關係變成一對多關係,中介者物件可以將複雜的網狀結構變成以調停者為中心的星形結構,達到降低系統的複雜性,提高可擴充套件性的作用。若系統各層次物件之間存在大量的關聯關係,即層次物件呈複雜的網狀結構,如果直接讓它們緊耦合通訊,會造成系統結構變得異常複雜,且其中某個層次物件發生改變,則與其緊耦合的相應層次物件也需進行修改,系統很難進行維護。而通過為該系統增加一箇中介者層次物件,讓其他各層次需對外通訊的行為統統交由中介者進行轉發,系統呈現以中介者為中心進行通訊的星形結構,系統的複雜性大大降低。簡單的說就是多個類相互耦合,形成了網狀結構,則考慮使用中介者模式進行優化。中介者模式適用於以下場景:

  • 系統中物件之間存在複雜的引用關係,產生的相互依賴關係結構混亂且難以理解;
  • 互動的公共行為,如果需要改變行為則可以增加新的中介者類。

1、群聊場景

假設我們要構建一個聊天室系統,使用者可以向聊天室傳送訊息,聊天室會向所有的使用者顯示訊息,實際上就是使用者發資訊與聊天室顯示的通訊過程,不過使用者無法直接將資訊發給聊天室,而是需要將資訊先發到伺服器上,然後伺服器再將該訊息發給聊天室進行顯示。具體程式碼如下。建立User類:

public class User {
    private String name;
    private ChatRoom chatRoom;

    public User(String name, ChatRoom chatRoom) {
        this.name = name;
        this.chatRoom = chatRoom;
    }

    public String getName() {
        return name;
    }

    public void sendMessage(String msg){
        this.chatRoom.showMsg(this,msg);
    }
}

然後是ChatRoom:

public class ChatRoom {
    public void showMsg(User user,String msg){
        System.out.println("[" + user.getName() + "] : " + msg);
    }
}

測試:

public class Test {
    public static void main(String[] args) {
        ChatRoom chatRoom = new ChatRoom();

        User zs = new User("張三",chatRoom);
        User ls = new User("李四",chatRoom);

        zs.sendMessage("您好");
        ls.sendMessage("有啥事");
    }
}

上面這麼寫估計也很簡單,下面按中介者定義來寫一個

// 抽象中介者
public abstract class Mediator {
    protected ConcreteColleagueA colleagueA;
    protected ConcreteColleagueB colleagueB;

    public void setColleageA(ConcreteColleagueA colleague) {
        this.colleagueA = colleague;
    }

    public void setColleageB(ConcreteColleagueB colleague) {
        this.colleagueB = colleague;
    }

    // 中介者業務邏輯
    public abstract void transferA();

    public abstract void transferB();
}
// 具體中介者
public class ConcreteMediator extends Mediator {
    @Override
    public void transferA() {
        // 協調行為:A 轉發到 B
        this.colleagueB.selfMethodB();
    }

    @Override
    public void transferB() {
        // 協調行為:B 轉發到 A
        this.colleagueA.selfMethodA();
    }
}
// 抽象同事類
public abstract class Colleague {
    protected Mediator mediator;

    public Colleague(Mediator mediator) {
        this.mediator = mediator;
    }
}
// 具體同事類
public class ConcreteColleagueA extends Colleague {
    public ConcreteColleagueA(Mediator mediator) {
        super(mediator);
        this.mediator.setColleageA(this);
    }

    // 自有方法
    public void selfMethodA() {
        // 處理自己的邏輯
        System.out.println(String.format("自有方法", this.getClass().getSimpleName()));
    }

    // 依賴方法
    public void depMethodA() {
        // 處理自己的邏輯
        System.out.println(String.format("依賴方法", this.getClass().getSimpleName()));
        // 無法處理的業務邏輯委託給中介者處理
        this.mediator.transferA();
    }
}
// 具體同事類
public class ConcreteColleagueB extends Colleague {
    public ConcreteColleagueB(Mediator mediator) {
        super(mediator);
        this.mediator.setColleageB(this);
    }

    // 自有方法
    public void selfMethodB() {
        // 處理自己的邏輯
        System.out.println(String.format("自有方法", this.getClass().getSimpleName()));
    }

    // 依賴方法
    public void depMethodB() {
        // 處理自己的邏輯
        System.out.println(String.format("依賴方法", this.getClass().getSimpleName()));
        // 無法處理的業務邏輯委託給中介者處理
        this.mediator.transferB();
    }
}
public class Test {
    public static void main(String[] args) {
        Mediator mediator = new ConcreteMediator();
        ConcreteColleagueA colleagueA = new ConcreteColleagueA(mediator);
        ConcreteColleagueB colleagueB = new ConcreteColleagueB(mediator);
        colleagueA.depMethodA();
        System.out.println("-------------------------");
        colleagueB.depMethodB();
    }
}

三、中介者模式在原始碼中的體現

在JDK的Timer類中有很多的schedule()過載方法,如下圖:

 

 隨便點開一個方法,會發現所有的方法最終都是呼叫了私有的sched()方法,下面看下Timer原始碼

public class Timer {
     ......

    public void schedule(TimerTask var1, long var2) {
        if (var2 < 0L) {
            throw new IllegalArgumentException("Negative delay.");
        } else {
            this.sched(var1, System.currentTimeMillis() + var2, 0L);
        }
    }
    ......
    private void sched(TimerTask var1, long var2, long var4) {
        if (var2 < 0L) {
            throw new IllegalArgumentException("Illegal execution time.");
        } else {
            if (Math.abs(var4) > 4611686018427387903L) {
                var4 >>= 1;
            }

            synchronized(this.queue) {
                if (!this.thread.newTasksMayBeScheduled) {
                    throw new IllegalStateException("Timer already cancelled.");
                } else {
                    synchronized(var1.lock) {
                        if (var1.state != 0) {
                            throw new IllegalStateException("Task already scheduled or cancelled");
                        }

                        var1.nextExecutionTime = var2;
                        var1.period = var4;
                        var1.state = 1;
                    }

                    this.queue.add(var1);
                    if (this.queue.getMin() == var1) {
                        this.queue.notify();
                    }

                }
            }
        }
    }

    public void cancel() {
        synchronized(this.queue) {
            this.thread.newTasksMayBeScheduled = false;
            this.queue.clear();
            this.queue.notify();
        }
    }

    public int purge() {
        int var1 = 0;
        synchronized(this.queue) {
            for(int var3 = this.queue.size(); var3 > 0; --var3) {
                if (this.queue.get(var3).state == 3) {
                    this.queue.quickRemove(var3);
                    ++var1;
                }
            }

            if (var1 != 0) {
                this.queue.heapify();
            }

            return var1;
        }
    }
}

從中可以看到,不管是什麼樣的任務都被加到一個queue佇列中順序執行,可以把這個佇列中的所有物件當成“同事”,同事之間的通訊都是通過Timer來協調完成的,Timer就承擔了中介者的角色。

四、總結

優點:

  • 減少類間依賴,將多對多依賴轉化成了一對多,降低了類間耦合;
  • 類間各司其職,符合迪米特法則。

缺點:

  中介者模式中將原本多個物件直接的相互依賴變成了中介者和多個同事類的依賴關係。當同事類越多時,中介者就會越臃腫,變得複雜且難以維護。

補充:服務治理用的也是這個思路,將多條鏈路的訪問進行拉平,呼叫同一個服務中心呼叫 

GIT原始碼:https://github.com/ljx958720/design_patterns.git

相關文章