1. 개요

이 기사에서는 GoF 행동 패턴 중 하나인 중재자 패턴을 살펴보겠습니다 . 그 목적을 설명하고 언제 사용해야 하는지 설명하겠습니다.

늘 그렇듯이 간단한 코드 예제도 제공합니다.

2. 중재자 패턴

객체 지향 프로그래밍에서 우리는 항상 구성 요소가 느슨하게 결합되고 재사용 가능한 방식으로 시스템을 설계 하려고 노력해야 합니다 . 이 접근 방식을 사용하면 코드를 유지 관리하고 테스트하기가 더 쉬워집니다.

그러나 실생활에서는 종종 복잡한 종속 개체 집합을 처리해야 합니다. 이때 중재자 패턴이 유용할 수 있습니다.

중재자 패턴의 의도는 서로 직접 통신하는 밀접하게 결합된 개체 간의 복잡성과 의존성을 줄이는 것 입니다. 이는 종속 개체 간의 상호 작용을 처리하는 중재자 개체를 생성하여 달성됩니다. 결과적으로 모든 커뮤니케이션은 중재자를 통해 이루어집니다.

이는 함께 작동하는 구성 요소 집합이 더 이상 직접 상호 작용할 필요가 없기 때문에 느슨한 결합을 촉진합니다. 대신 단일 중재자 개체만 참조합니다. 이렇게 하면 시스템의 다른 부분에서 이러한 개체를 재사용하는 것이 더 쉽습니다.

3. Mediator Pattern의 UML 다이어그램

이제 패턴을 시각적으로 살펴보겠습니다.

중재인

위의 UML 다이어그램에서 다음 참가자를 식별할 수 있습니다.

  • Mediator 는 동료 개체가 통신하는 데 사용 하는 인터페이스를 정의합니다.
  • 동료 는 Mediator 에 대한 단일 참조를 보유하는 추상 클래스를 정의합니다.
  • ConcreteMediator 는 동료 개체 간의 상호 작용 논리를 캡슐화합니다.
  • ConcreteColleague1ConcreteColleague2 는 중재자 를 통해서만 통신합니다.

보시다시피 Colleague 객체는 서로를 직접 참조하지 않습니다. 대신 모든 통신은 중재자 에 의해 수행됩니다 .

결과적으로 ConcreteColleague1ConcreteColleague2 는 더 쉽게 재사용할 수 있습니다.

또한 Colleague 객체가 함께 작동 하는 방식을 변경해야 하는 경우 ConcreteMediator 로직 만 수정하면 됩니다. 또는 Mediator 의 새로운 구현을 만들 수 있습니다 .

4. 자바 구현

이제 이론에 대한 명확한 아이디어를 얻었으므로 실제로 개념을 더 잘 이해하기 위한 예를 살펴보겠습니다.

4.1. 예시 시나리오

팬, 전원 공급 장치 및 버튼으로 구성된 간단한 냉각 시스템을 구축한다고 상상해 보십시오. 버튼을 누르면 팬이 켜지거나 꺼집니다. 팬을 켜기 전에 전원을 켜야 합니다. 마찬가지로 팬이 꺼진 직후에 전원을 꺼야 합니다.

이제 예제 구현을 살펴보겠습니다.

public class Button {
    private Fan fan;

    // constructor, getters and setters

    public void press(){
        if(fan.isOn()){
            fan.turnOff();
        } else {
            fan.turnOn();
        }
    }
}
public class Fan {
    private Button button;
    private PowerSupplier powerSupplier;
    private boolean isOn = false;

    // constructor, getters and setters

    public void turnOn() {
        powerSupplier.turnOn();
        isOn = true;
    }

    public void turnOff() {
        isOn = false;
        powerSupplier.turnOff();
    }
}
public class PowerSupplier {
    public void turnOn() {
        // implementation
    }

    public void turnOff() {
        // implementation
    }
}

다음으로 기능을 테스트해 보겠습니다.

@Test
public void givenTurnedOffFan_whenPressingButtonTwice_fanShouldTurnOnAndOff() {
    assertFalse(fan.isOn());

    button.press();
    assertTrue(fan.isOn());

    button.press();
    assertFalse(fan.isOn());
}

모든 것이 잘 작동하는 것 같습니다. 그러나 Button, FanPowerSupplier 클래스가 어떻게 밀접하게 결합되어 있는지 확인 하십시오. ButtonFan 에서 직접 작동하고 Fan ButtonPowerSupplier 와 상호 작용합니다 .

다른 모듈에서 Button 클래스 를 재사용하는 것은 어려울 것 입니다. 또한 시스템에 두 번째 전원 공급 장치를 추가해야 하는 경우 Fan 클래스의 논리 를 수정해야 합니다 .

4.2. 중재자 패턴 추가

이제 중재자 패턴을 구현하여 클래스 간의 의존성을 줄이고 코드를 더 재사용할 수 있도록 만들어 보겠습니다.

먼저 Mediator 클래스를 소개합니다.

public class Mediator {
    private Button button;
    private Fan fan;
    private PowerSupplier powerSupplier;

    // constructor, getters and setters

    public void press() {
        if (fan.isOn()) {
            fan.turnOff();
        } else {
            fan.turnOn();
        }
    }

    public void start() {
        powerSupplier.turnOn();
    }

    public void stop() {
        powerSupplier.turnOff();
    }
}

다음으로 나머지 클래스를 수정하겠습니다.

public class Button {
    private Mediator mediator;

    // constructor, getters and setters

    public void press() {
        mediator.press();
    }
}
public class Fan {
    private Mediator mediator;
    private boolean isOn = false;

    // constructor, getters and setters

    public void turnOn() {
        mediator.start();
        isOn = true;
    }

    public void turnOff() {
        isOn = false;
        mediator.stop();
    }
}

다시 기능을 테스트해 보겠습니다.

@Test
public void givenTurnedOffFan_whenPressingButtonTwice_fanShouldTurnOnAndOff() {
    assertFalse(fan.isOn());
 
    button.press();
    assertTrue(fan.isOn());
 
    button.press();
    assertFalse(fan.isOn());
}

냉각 시스템이 예상대로 작동합니다.

중재자 패턴을 구현했으므로 이제 Button , Fan 또는 PowerSupplier 클래스 중 어느 것도 직접 통신하지 않습니다. 중재자 에 대한 단일 참조만 있습니다 .

나중에 두 번째 전원 공급 장치를 추가해야 하는 경우 Mediator의 논리를 업데이트하기만 하면 됩니다. 버튼 클래스는 그대로 유지됩니다.

이 예는 종속 개체를 얼마나 쉽게 분리하고 시스템을 유지 관리하기 쉽게 만드는지 보여줍니다.

5. 중재자 패턴을 사용하는 경우

매개체 패턴은 밀접하게 결합되어 유지 관리하기 어려운 개체 집합을 처리해야 하는 경우 좋은 선택입니다. 이렇게 하면 개체 간의 의존성을 줄이고 전반적인 복잡성을 줄일 수 있습니다.

또한 중재자 개체를 사용하여 통신 논리를 단일 구성 요소로 추출하므로 단일 책임 원칙 을 따릅니다 . 또한 시스템의 나머지 부분을 변경할 필요 없이 새로운 중재자를 도입할 수 있습니다. 따라서 우리는 개방-폐쇄 원칙을 따릅니다.

그러나 때때로 시스템의 잘못된 설계로 인해 너무 많은 밀접하게 결합된 개체가 있을 수 있습니다. 이 경우 중재자 패턴을 적용해서는 안 됩니다 . 대신 한 걸음 뒤로 물러서서 클래스를 모델링한 방식을 재고해야 합니다.

다른 모든 패턴과 마찬가지로 중재자 패턴을 맹목적으로 구현하기 전에 특정 사용 사례를 고려해야 합니다 .

6. 결론

이번 포스팅에서는 중재자 패턴에 대해 알아보았습니다. 우리는 이 패턴이 어떤 문제를 해결하고 언제 실제로 사용을 고려해야 하는지 설명했습니다. 디자인 패턴의 간단한 예제도 구현했습니다.

항상 그렇듯이 전체 코드 샘플은 GitHub에서 사용할 수 있습니다 .

Generic footer banner