Design Pattern | Mediator Pattern (미디에이터 패턴)

Mediator 패턴이란?

  • Mediator라는 영어 단어는 중개자라는 의미이다.
  • 맴버가 10명 있었다고 할 때에 서로 지시하고 있으면, 작업은 큰 혼란스러워진다. 그럴 때는 입장이 다른 중개자가 있으면 멤버는 중개자에만 보고하고, 멤버에의 지시는 중개자만으로부터 받도록 하면 된다.
  • Mediator 패턴은 중개자을 통해서 행동을 일으키게 하는 방식이다.
  • GoF의 디자인 패턴에서는 행위에 대한 디자인 패턴으로 분류된다.

Mediator 패턴 예제 프로그램

로그인 다이얼로그를 표시하여 텍스트나 버튼의 유효/무효 상태를 제어하는 ​​프로그램이다.

Class Diagram
Mediator Pattern Class Diagram

1. Mediator 인터페이스

상담역이 되는 인터페이스이다.

Mediator.java

public interface Mediator {
    void createColleagues();
    void colleagueChanged();
}

2. Colleague 인터페이스

멤버가 되는 인터페이스이다.

Colleague.java

public interface Colleague {
    public abstract void setMediator(Mediator mediator);
    public abstract void setColleagueEnabled(boolean enabled);
}

3. ColleagueButton 클래스

버튼을 나타내는 클래스이다. Colleague 인터페이스를 구현한다.

ColleagueButton.java

package com.devkuma.designpattern.behavioral.mediator;

import java.awt.Button;

public class ColleagueButton extends Button implements Colleague {

    private Mediator mediator;

    public ColleagueButton(String caption) {
        super(caption);
    }

    public void setMediator(Mediator mediator) {
        this.mediator = mediator;
    }

    public void setColleagueEnabled(boolean enabled) {
        // Mediator에서 활성화/비활성화를 지시함
        setEnabled(enabled);
    }
}

4. ColleagueCheckbox 클래스

체크 박스 (여기에서는 라디오 버튼)를 나타내는 클래스이다. Colleague 인터페이스를 구현한다.

ColleagueCheckbox.java

package com.devkuma.designpattern.behavioral.mediator;

import java.awt.Checkbox;
import java.awt.CheckboxGroup;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;

public class ColleagueCheckbox extends Checkbox implements ItemListener, Colleague {

    private Mediator mediator;

    public ColleagueCheckbox(String caption, CheckboxGroup group, boolean state) {
        super(caption, group, state);
    }

    public void setMediator(Mediator mediator) {
        this.mediator = mediator;
    }

    public void setColleagueEnabled(boolean enabled) {
        // Mediator에서 활성화/비활성화를 지시함
        setEnabled(enabled);
    }

    public void itemStateChanged(ItemEvent e) {
        // 상태가 변경되면 Mediator에 알림
        mediator.colleagueChanged();
    }
}

5. ColleagueTextField 클래스

텍스트 박스를 나타내는 클래스이다. Colleague 인터페이스를 구현한다.

ColleagueTextField.java

package com.devkuma.designpattern.behavioral.mediator;

import java.awt.Color;
import java.awt.TextField;
import java.awt.event.TextEvent;
import java.awt.event.TextListener;

public class ColleagueTextField extends TextField implements TextListener, Colleague {

    private Mediator mediator;

    public ColleagueTextField(String text, int columns) {
        super(text, columns);
    }

    public void setMediator(Mediator mediator) {
        this.mediator = mediator;
    }

    public void setColleagueEnabled(boolean enabled) {
        // Mediator에서 활성화/비활성화를 지시함
        setEnabled(enabled);
        setBackground(enabled ? Color.white : Color.lightGray);
    }

    public void textValueChanged(TextEvent e) {
        // 문자열이 변경되면 Mediator에 알림
        mediator.colleagueChanged();
    }
}

6. LoginFrame 클래스

로그인 다이얼로그를 나타내는 클래스이다. Mediator 인터페이스를 구현한다.

LoginFrame.java

package com.devkuma.designpattern.behavioral.mediator;

import java.awt.CheckboxGroup;
import java.awt.Color;
import java.awt.Frame;
import java.awt.GridLayout;
import java.awt.Label;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class LoginFrame extends Frame implements ActionListener, Mediator {

    private ColleagueCheckbox checkGuest;
    private ColleagueCheckbox checkLogin;
    private ColleagueTextField textUser;
    private ColleagueTextField textPass;
    private ColleagueButton buttonOk;
    private ColleagueButton buttonCancel;

    public LoginFrame(String title) {
        super(title);
        setBackground(Color.lightGray);
        setLayout(new GridLayout(4, 2));

        createColleagues();
        add(checkGuest);
        add(checkLogin);
        add(new Label("Username:"));
        add(textUser);
        add(new Label("Password:"));
        add(textPass);
        add(buttonOk);
        add(buttonCancel);
        colleagueChanged();

        pack();
        setVisible(true);
    }

    public void createColleagues() {

        CheckboxGroup g = new CheckboxGroup();
        checkGuest = new ColleagueCheckbox("Guest", g, true);
        checkLogin = new ColleagueCheckbox("Login", g, false);
        textUser = new ColleagueTextField("", 10);
        textPass = new ColleagueTextField("", 10);
        textPass.setEchoChar('*');
        buttonOk = new ColleagueButton("OK");
        buttonCancel = new ColleagueButton("Cancel");

        checkGuest.setMediator(this);
        checkLogin.setMediator(this);
        textUser.setMediator(this);
        textPass.setMediator(this);
        buttonOk.setMediator(this);
        buttonCancel.setMediator(this);

        checkGuest.addItemListener(checkGuest);
        checkLogin.addItemListener(checkLogin);
        textUser.addTextListener(textUser);
        textPass.addTextListener(textPass);
        buttonOk.addActionListener(this);
        buttonCancel.addActionListener(this);
    }

    // Colleage로부터의 통지로 각 Colleage의 유효/무효를 판정한다.
    public void colleagueChanged() {
        if (checkGuest.getState()) {
            // Guest mode
            textUser.setColleagueEnabled(false);
            textPass.setColleagueEnabled(false);
            buttonOk.setColleagueEnabled(true);
        } else {
            // Login mode
            textUser.setColleagueEnabled(true);
            userpassChanged();
        }
    }

    private void userpassChanged() {
        if (textUser.getText().length() > 0) {
            textPass.setColleagueEnabled(true);
            if (textPass.getText().length() > 0) {
                buttonOk.setColleagueEnabled(true);
            } else {
                buttonOk.setColleagueEnabled(false);
            }
        } else {
            textPass.setColleagueEnabled(false);
            buttonOk.setColleagueEnabled(false);
        }
    }

    public void actionPerformed(ActionEvent e) {
        System.out.println(e.toString());
        System.exit(0);
    }
}

7. Main 클래스

메인 처리를 실행하는 클래스이다.

Main.java

package com.devkuma.designpattern.behavioral.mediator;

public class Main {
    static public void main(String args[]) {
        new LoginFrame("Mediator Sample");
    }
}

8. 실행 결과

Mediator Pattern Result

장점

표시의 유효/무효에 관한 로직은 복잡하게 되지만, LoginFrame 클래스에 중개되고 있다.
표시에 관한 사양을 변경하거나, 버그를 발견했을 경우는 LoginFrame 클래스만 수정하거나 디버그하면 된다.
로직이 ColleagueButton, ColleagueCheckbox, ColleagueTextField에 분산되면 사용하거나 디버깅하거나 수정하는 것이 어렵다.