初識設計模式

2022-11-09 12:01:36

簡介

中介設計模式(Mediator Design Pattern)定義了一個單獨的(中介)物件,來封裝一組物件之間的互動。

如果物件之間存在大量的相互關聯和呼叫,若有一個物件發生變化,則需要跟蹤和該物件的其他所有物件,並進行適當處理。

而中介模式將這些物件之間的互動委派給中介物件互動,來避免物件之間直接互動,使其耦合鬆散。

典型實現

首先,定義一個抽象中介者介面,該介面用於與各物件之間進行通訊。其程式碼範例如下:

public abstract class Mediator {
    // 維持所有同事物件的參照
    protected ArrayList<Colleague> colleagues;

    // 註冊方法,用於增加同事物件
    public void register(Colleague colleague) {
        colleagues.add(colleague);
    }

    // 宣告抽象的業務方法
    public abstract void operation();
}

對於具體的中介者物件,主要是實現自己的業務方法,封裝同事之間的呼叫。其程式碼範例如下:

public class ConcreteMediator extends Mediator {
    @Override
    public void operation() {
        // 通過中介者呼叫同事類的方法
        this.colleagues.get(0).method1();
    }
}

然後,需要定義一個抽象的同事類,其維持了一個抽象中介者的參照,用於呼叫中介者的方法。其程式碼範例如下:

public abstract class Colleague {
    // 維持一個抽象中介者的參照
    protected Mediator mediator;

    public Colleague(Mediator mediator) {
        this.mediator = mediator;
    }

    // 宣告自身方法,處理自己的行為
    public abstract void method1();

    // 定義依賴方法,與中介者通訊
    public void method2() {
        mediator.operation();
    }
}

具體的同事類也比較簡單,只需要繼承自抽象同事類,然後定義好自己的行為即可。

總結

優點

中介模式的主要優點如下:

  • 中介模式將一對多的關係簡化成了一對一的關係,降低了類的複雜度,簡化了物件之間的互動
  • 將各同事物件解耦,增加新的中介者和新的同事類都比較方便,更好地符合「開閉原則」
  • 中介者將原本分佈於多個物件間的行為集中在一起,改變這些行為只需生成新的中介者子類即可,這使得各個同事類可被重用,無須對同事類進行擴充套件

缺點

中介模式的主要缺點如下:

  • 具體的中介者類中包含了大量同事之間的互動邏輯,可能會導致具體中介者類非常複雜

適用場景

中介模式的適用場景如下:

  • 系統中物件之間存在複雜的參照關係,系統結構混亂且難以理解
  • 一個物件由於參照了許多其他物件並且直接和這些物件通訊,導致難以複用該物件
  • 想通過一箇中間類來封裝多個類中的行為,而又不想生成太多子類

原始碼

在 JDK 中 java.util.Timer 就使用到了中介模式。如下是其原始碼部分:

public class Timer {
    private final TaskQueue queue = new TaskQueue();

    private void sched(TimerTask task, long time, long period) {
        if (time < 0)
            throw new IllegalArgumentException("Illegal execution time.");

        if (Math.abs(period) > (Long.MAX_VALUE >> 1))
            period >>= 1;

        synchronized(queue) {
            if (!thread.newTasksMayBeScheduled)
                throw new IllegalStateException("Timer already cancelled.");

            synchronized(task.lock) {
                if (task.state != TimerTask.VIRGIN)
                    throw new IllegalStateException(
                        "Task already scheduled or cancelled");
                task.nextExecutionTime = time;
                task.period = period;
                task.state = TimerTask.SCHEDULED;
            }

            queue.add(task);
            if (queue.getMin() == task)
                queue.notify();
        }
    }
}