의도
- 중재자 -> 객체 간의 직접적인 의존을 제한하고 중재자를 통하도록 하는 행동 디자인 패턴
문제
- 프로필을 만들고 편집하기 위한 대화 상자가 있다고 가정해보자. 이미 구조가 상당히 복잡한 상태이다.
- 일부 요소들은 다른 요소들과 상호작용할 수 있다. 가령 "집에 개가 있다"라는 조건을 만족하면 개의 이름을 입력하기 위한 새로운 필드창이 나타날 수도 있다.
- 그런데 이를 만약 코드 내에 직접 작성한면, 대화 상자 컴포넌트는 다른 곳에서 재활용하기 매우 어려워질 것이다.
해결책
- 서로 독립적으로 작동하는 컴포넌트 간의 직접 통신을 중단하고 호출을 적절한 컴포넌트로 리다이렉트하기
- 중재자라 불리는 컴포넌트를 만들어 이 컴포넌트를 통해서만 소통하게 함으로써 코드의 복잡성을 줄일 수 있음
- 더 나아가면 여러 유형의 컴포넌트에서 공통적으로 사용되는 인터페이스를 추출해 하나의 클래스에 집어넣어 의존성을 줄일 수 있음
- 중재자 패턴은 객체 내부의 복잡한 관계를 정리하고 이를 캡슐화 할 수 있도록 해줌
구조
interface Mediator {
notify(sender: object, event: string): void;
}
class ConcreteMediator implements Mediator {
private component1: Component1;
private component2: Component2;
constructor(c1: Component1, c2: Component2) {
this.component1 = c1;
this.component1.setMediator(this);
this.component2 = c2;
this.component2.setMediator(this);
}
public notify(sender: object, event: string): void {
if (event === 'A') {
console.log('Mediator reacts on A and triggers following operations:');
this.component2.doC();
}
if (event === 'D') {
console.log('Mediator reacts on D and triggers following operations:');
this.component1.doB();
this.component2.doC();
}
}
}
class BaseComponent {
protected mediator: Mediator;
constructor(mediator?: Mediator) {
this.mediator = mediator!;
}
public setMediator(mediator: Mediator): void {
this.mediator = mediator;
}
}
class Component1 extends BaseComponent {
public doA(): void {
console.log('Component 1 does A.');
this.mediator.notify(this, 'A');
}
public doB(): void {
console.log('Component 1 does B.');
this.mediator.notify(this, 'B');
}
}
class Component2 extends BaseComponent {
public doC(): void {
console.log('Component 2 does C.');
this.mediator.notify(this, 'C');
}
public doD(): void {
console.log('Component 2 does D.');
this.mediator.notify(this, 'D');
}
}
const c1 = new Component1();
const c2 = new Component2();
const mediator = new ConcreteMediator(c1, c2);
console.log('Client triggers operation A.');
c1.doA();
console.log('');
console.log('Client triggers operation D.');
c2.doD();
적용
- 일부 클래스들이 다른 클래스들과 단단하게 결합하여 변경하기 어려울 때 사용
특정 컴포넌트에 대한 모든 변경을 나머지 컴포넌트로부터 고립해 클래스 간의 모든 관계를 분리할 수 있음
이를 통해 컴포넌트 재사용을 용이하게 할수도 있음
- 몇가지 비즈니스 로직이 여러 컴포넌트로 복제되고 있을 때 사용
중재자를 하나 만들어 여러 컴포넌트들이 중재자 코드를 사용할 수 있도록 하게 변경
구현 방법
- 클래스가 비즈니스 로직과 단단히 결합되어있는지 확인
- 중재자 인터페이스를 선언하고 중재자와 컴포넌트 간의 통신 프로토콜을 단일 메서드로 선언
- 구상 중재자 클래스를 생성하고 연결, 이 때 구상 클래스 내부에 컴포넌트에 대한 참조를 넣어주는 것이 좋음
필요하다면 컴포넌트 객체의 생성, 삭제를 담당할 수 있으며, 이 경우 중재자는 팩토리, 퍼사드와 유사해지게 됨
- 클라이언트 코드에서 다른 컴포넌트의 메서드 대신 중재자의 알림 세서드를 호출하도록 컴포넌트 코드를 고치기
장단점
- 다양한 컴포넌트 간의 통신을 한 곳으로 추출해 단일 책임 원칙 준수
- 실제 컴포넌트 변경 없이 새로운 중재자를 도입할수 있기 때문에 개방, 폐쇄 원칙을 준수함
- 프로그램의 다양한 컴포넌트 간의 결합도를 줄일 수 있고, 더 쉽게 컴포넌트를 재사용할 수 있게 됨
- 중재자가 과도하게 커질 우려가 있음
출처:
https://refactoring.guru/ko/design-patterns/mediator