Yan Hong 박사의 저서 "JAVA and Patterns"는 중재자 패턴에 대한 설명으로 시작됩니다.
중재자 패턴은 객체의 행동 패턴입니다. 중재자 패턴은 개체 집합이 상호 작용하는 방식을 마무리하므로 개체가 서로를 명시적으로 참조할 필요가 없습니다. 이를 통해 느슨하게 결합할 수 있습니다. 이러한 개체 중 일부 간의 상호 작용이 변경되더라도 다른 개체 간의 상호 작용에는 즉시 영향을 미치지 않습니다. 이렇게 하면 이러한 상호 작용이 서로 독립적으로 달라질 수 있습니다.
중재자가 필요한 이유
아래 그림에 표시된 것처럼 이 도식 다이어그램에는 많은 수의 개체가 있습니다. 이러한 개체는 다른 개체에 영향을 미칠 수도 있고 다른 개체의 영향을 받을 수도 있으므로 동료 개체라고도 합니다. 이러한 동료들은 서로 상호 작용을 통해 시스템의 동작을 형성합니다. 그림에서 볼 수 있듯이 거의 모든 객체는 다른 객체와 상호 작용해야 하며 이러한 상호 작용은 한 객체와 다른 객체 간의 직접적인 결합으로 나타납니다. 이는 과결합 시스템입니다.
중재자 객체(Mediator)를 도입함으로써 시스템의 네트워크 구조는 아래 그림과 같이 중재자를 중심으로 한 스타 구조로 변형될 수 있다. 이 별 구조에서는 동료 개체가 더 이상 직접 연결을 통해 다른 개체와 상호 작용하지 않고 중재 개체를 통해 다른 개체와 상호 작용합니다. 중재자 객체의 존재는 객체 구조의 안정성을 보장한다. 즉, 시스템 구조는 새로운 객체의 도입으로 인해 많은 수정 작업을 일으키지 않을 것이다.
좋은 객체 지향 디자인은 협업을 늘리고 객체 간의 결합을 줄일 수 있습니다. 잘 고안된 설계는 시스템을 협력하는 동료 개체 그룹으로 나눈 다음 각 동료 개체에 고유한 책임을 부여하고 함께 작업할 수 있도록 이들 간의 협력 관계를 적절하게 구성합니다.
메인보드가 없는 경우
우리 모두 알고 있듯이 컴퓨터의 다양한 액세서리 간의 상호 작용은 주로 마더보드를 통해 완료됩니다. 컴퓨터에 마더보드가 없는 경우 다양한 액세서리가 서로 상호 작용하여 서로 데이터를 전송해야 합니다. 그리고 각 액세서리의 인터페이스가 다르기 때문에 서로 상호 작용할 때 데이터 인터페이스를 일치하도록 변환해야 합니다.
다행스럽게도 마더보드를 사용하면 다양한 액세서리의 상호 작용이 마더보드를 통해 완전히 완료됩니다. 각 액세서리는 마더보드와만 상호 작용하면 되며 마더보드는 모든 액세서리를 처리하는 방법을 알고 있으므로 훨씬 간단합니다.
중재자 패턴의 구조
Mediator 패턴의 도식적 클래스 다이어그램은 다음과 같습니다.
중재자 모드에는 다음 역할이 포함됩니다.
● 추상 중재자(Mediator) 역할: 동료 객체에서 중재자 객체까지의 인터페이스를 정의합니다. 여기서 주 메소드는 하나 이상의 이벤트 메소드입니다.
● 구상 중재자(ConcreteMediator) 역할: 추상 중재자가 선언한 이벤트 메소드를 구현합니다. 특정 중재자는 모든 특정 동료 클래스를 인식하고 각 동료 개체 간의 상호 작용을 구체적으로 조정하는 일을 담당합니다.
● 추상 동료 클래스(Colleague) 역할: 중재자에서 동료 개체까지의 인터페이스를 정의합니다. 동료 개체는 중재자에 대해서만 알고 다른 공동 개체는 알지 못합니다.
● ConcreteColleague 역할: 모든 구체적인 동료 클래스는 추상 동료 클래스에서 상속됩니다. 자신의 사업을 실행하기 위해 다른 동료들과 소통해야 할 때는 이를 담당하는 중재자와 소통해야 합니다. 중재자는 다른 동료들과의 교류를 담당하게 됩니다.
소스 코드
추상 조정자 클래스 복사 코드는 다음과 같습니다.
공개 인터페이스 중재자 {
/**
* 동료 객체는 변경 시 중재자 메서드에 이를 알립니다.
* 중재자가 다른 동료 개체와의 해당 상호 작용을 담당하도록 합니다.
*/
공개 무효 변경됨(동료 c);
}
구체적인 중재자 클래스
다음과 같이 코드 코드를 복사합니다.
공개 클래스 ConcreteMediator는 중재자 {를 구현합니다.
//동료 A를 잡고 유지
개인 콘크리트ColleagueA 동료A;
//동료 B를 잡고 유지
개인 콘크리트ColleagueB 동료B;
공공 무효 setColleagueA(ConcreteColleagueA 동료A) {
this.colleagueA = 동료A;
}
공공 무효 setColleagueB(ConcreteColleagueB 동료B) {
this.colleagueB = 동료B;
}
@보수
공개 무효 변경됨(동료 c) {
/**
* 특정 동료 클래스가 변경되어 일반적으로 다른 동료와의 상호 작용이 필요합니다.
* 협력적 행동을 달성하기 위해 해당 동료 개체를 구체적으로 조정합니다.
*/
}
}
추상 동료 수업
다음과 같이 코드 코드를 복사합니다.
공개 추상 클래스 동료 {
//중재자 객체 보유
민간 중재자 중재자;
/**
*건설자
*/
공공 동료(중재자 중재자){
this.mediator = 중재자;
}
/**
* 현재 동료 클래스에 해당하는 중재자 개체를 가져옵니다.
*/
공개 중재자 getMediator() {
반환 중재자;
}
}
동일한 클래스를 복사하는 구체적인 코드는 다음과 같습니다.
공개 클래스 ConcreteColleagueA는 동료 {를 확장합니다.
public ConcreteColleagueA(중개자 중재자) {
슈퍼(중개자);
}
/**
* 특정 작업을 수행하는 방법을 나타냅니다.
*/
공개 무효 연산(){
//다른 동료와 소통해야 할 경우 중재자 개체에 알립니다.
getMediator().changed(this);
}
}
다음과 같이 코드 코드를 복사합니다.
공개 클래스 ConcreteColleagueB는 동료 {를 확장합니다.
public ConcreteColleagueB(중개자 중재자) {
슈퍼(중개자);
}
/**
* 특정 작업을 수행하는 방법을 나타냅니다.
*/
공개 무효 연산(){
//다른 동료와 소통해야 할 경우 중재자 개체에 알립니다.
getMediator().changed(this);
}
}
컴퓨터를 사용하여 영화를 감상하세요
일상생활에서 우리는 영화를 보기 위해 컴퓨터를 자주 사용합니다. 단순화한 후에는 다음과 같은 상호작용 과정이 있다고 가정합니다.
(1) 먼저 광 드라이브는 광 디스크의 데이터를 읽은 다음 상태가 변경되었음을 마더보드에 알려야 합니다.
(2) 마더보드는 광학 드라이브에서 데이터를 가져와 분석 및 처리를 위해 데이터를 CPU로 전달합니다.
(3) CPU는 처리가 끝나면 데이터를 비디오 데이터와 오디오 데이터로 나누어 처리가 완료되었음을 마더보드에 알립니다.
(4) 마더보드는 CPU에서 처리된 데이터를 얻어 그래픽 카드와 사운드 카드에 각각 전달하여 비디오를 표시하고 소리를냅니다.
중재자 패턴을 사용하여 예제를 구현하려면 동료 개체와 중재자 개체를 구분해야 합니다. 분명히 마더보드는 중재자이고 광학 드라이브, 사운드 카드, CPU, 그래픽 카드와 같은 액세서리는 모두 동료입니다.
소스 코드
추상 동료 클래스 복사 코드는 다음과 같습니다.
공개 추상 클래스 동료 {
//중재자 객체 보유
민간 중재자 중재자;
/**
*건설자
*/
공공 동료(중재자 중재자){
this.mediator = 중재자;
}
/**
* 현재 동료 클래스에 해당하는 중재자 개체를 가져옵니다.
*/
공개 중재자 getMediator() {
반환 중재자;
}
}
동료 수업 - 광 드라이브 복사 코드 코드는 다음과 같습니다.
공개 클래스 CDDriver는 동료를 확장합니다.{
//광 드라이브에서 읽은 데이터
개인 문자열 데이터 = "";
/**
*건설자
*/
공공 CDDriver(중재자 중재자) {
슈퍼(중개자);
}
/**
* 디스크에서 데이터를 읽어옵니다.
*/
공개 문자열 getData() {
데이터를 반환합니다.
}
/**
* CD 읽기
*/
공개 무효 readCD(){
//쉼표 앞은 영상에 표시되는 데이터이고, 쉼표 뒤는 소리입니다.
this.data = "원피스, 나는 해적왕이 되기로 결심한다";
//상태가 변경되었음을 마더보드에 알립니다.
getMediator().changed(this);
}
}
동료 수업——CPU
다음과 같이 코드 코드를 복사합니다.
공개 클래스 CPU는 동료 {를 확장합니다.
//분해된 비디오 데이터
개인 문자열 videoData = "";
//분해된 사운드 데이터
개인 문자열 soundData = "";
/**
*건설자
*/
공용 CPU(Mediator 중재자) {
슈퍼(중개자);
}
/**
* 분해된 비디오 데이터 얻기
*/
공개 문자열 getVideoData() {
비디오 데이터를 반환합니다.
}
/**
* 분해된 사운드 데이터를 가져옵니다.
*/
공개 문자열 getSoundData() {
사운드데이터를 반환합니다.
}
/**
* 데이터를 처리하고 데이터를 오디오 및 비디오 데이터로 나눕니다.
*/
공공 무효 실행 데이터(문자열 데이터){
//데이터를 분해합니다. 앞면은 비디오 데이터이고 뒷면은 오디오 데이터입니다.
String[] 배열 = data.split(",");
this.videoData = 배열[0];
this.soundData = 배열[1];
//CPU가 작업을 완료했음을 마더보드에 알립니다.
getMediator().changed(this);
}
}
동료 클래스 - 그래픽 카드 복사 코드 코드는 다음과 같습니다.
공개 클래스 VideoCard는 동료를 확장합니다.
/**
*건설자
*/
공개 VideoCard(중재자 중재자) {
슈퍼(중개자);
}
/**
* 비디오 데이터 표시
*/
공개 무효 showData(문자열 데이터){
System.out.println("당신은 보고 있습니다: " + data);
}
}
동료 클래스 - 사운드 카드 복사 코드 코드는 다음과 같습니다.
공개 클래스 SoundCard는 동료를 확장합니다.
/**
*건설자
*/
public SoundCard(중재자 중재자) {
슈퍼(중개자);
}
/**
* 오디오 데이터를 기반으로 소리 만들기
*/
public void soundData(문자열 데이터){
System.out.println("음성 해설: " + data);
}
}
추상 조정자 클래스 복사 코드는 다음과 같습니다.
공개 인터페이스 중재자 {
/**
* 동료 객체는 변경 시 중재자 메서드에 이를 알립니다.
* 중재자가 다른 동료 개체와의 해당 상호 작용을 담당하도록 합니다.
*/
공개 무효 변경됨(동료 c);
}
특정 조정자 클래스 복사 코드는 다음과 같습니다.
공개 클래스 MainBoard는 중재자 {를 구현합니다.
//동료 클래스와 상호작용할 광학 드라이브 클래스를 알아야 합니다.
개인 CDDriver cdDriver = null;
//CPU 클래스와 상호작용하려면 동료 클래스를 알아야 합니다.
개인 CPU CPU = null;
//상호작용할 동료 클래스(그래픽 카드 클래스)를 알아야 합니다.
개인 VideoCard videoCard = null;
//상호작용할 동료 클래스를 알아야 합니다 - 사운드 카드 클래스
개인 SoundCard soundCard = null;
공공 무효 setCdDriver(CDDriver cdDriver) {
this.cdDriver = cdDriver;
}
공공 무효 setCpu(CPU CPU) {
this.cpu = CPU;
}
공공 무효 setVideoCard(VideoCard videoCard) {
this.videoCard = 비디오카드;
}
공공 무효 setSoundCard(SoundCard soundCard) {
this.soundCard = 사운드카드;
}
@보수
공개 무효 변경됨(동료 c) {
if(c CDDriver 인스턴스){
//광학 드라이브가 데이터를 읽었음을 나타냅니다.
this.opeCDDriverReadData((CDDriver)c);
}else if(c CPU 인스턴스){
this.opeCPU((CPU)c);
}
}
/**
* 광학 드라이브가 데이터를 읽은 후 다른 개체와의 상호 작용을 처리합니다.
*/
개인 무효 opeCDDriverReadData(CDDriver CD){
//먼저 광학 드라이브에서 읽은 데이터를 가져옵니다.
문자열 데이터 = cd.getData();
//처리를 위해 이 데이터를 CPU로 전송합니다.
CPU.executeData(데이터);
}
/**
* 데이터 처리 후 CPU와 다른 개체 간의 상호 작용을 처리합니다.
*/
개인 무효 opeCPU(CPU CPU){
//CPU가 처리한 데이터를 먼저 가져옵니다.
String videoData = cpu.getVideoData();
String soundData = cpu.getSoundData();
//이 데이터를 그래픽 카드와 사운드 카드로 전송하여 표시합니다.
videoCard.showData(videoData);
soundCard.soundData(soundData);
}
}
클라이언트 클래스 복사 코드는 다음과 같습니다.
공개 클래스 클라이언트 {
공개 정적 무효 메인(String[] args) {
//중재자 생성 - 마더보드
메인보드 중재자 = new MainBoard();
//동료 클래스 생성
CDDriver cd = 새 CDDriver(중재자);
CPU CPU = 새로운 CPU(중재자);
VideoCard vc = 새 VideoCard(중개자);
SoundCard sc = 새 SoundCard(중개자);
//중재자에게 모든 동료를 알립니다.
mediator.setCdDriver(cd);
mediator.setCpu(cpu);
mediator.setVideoCard(vc);
mediator.setSoundCard(sc);
//영화 감상을 시작하고 디스크를 광 드라이브에 넣으면 광 드라이브가 디스크 읽기를 시작합니다.
cd.readCD();
}
}
실행 결과는 다음과 같습니다.
중재자 패턴의 장점
● 느슨한 결합
중재자 패턴은 여러 동료 개체 간의 상호 작용을 중재자 개체로 캡슐화하여 동료 개체를 느슨하게 결합하고 기본적으로 보완적인 종속성을 달성합니다. 이런 식으로 기존처럼 "한 곳을 옮겨 몸 전체에 영향을 미치는" 것이 아니라, 동료 개체를 독립적으로 변경하고 재사용할 수 있습니다.
● 중앙 집중식 상호작용 제어
여러 동료 개체의 상호 작용은 중앙 집중식 관리를 위해 중재자 개체에 캡슐화되므로 이러한 대화형 동작이 변경되면 중재자 개체만 수정하면 됩니다. 물론 이미 완성된 시스템인 경우 중재자 개체를 확장하면 됩니다. 각 동료 클래스를 수정할 필요가 없습니다.
● 다대다가 일대다로 바뀜
중재자 패턴을 사용하지 않는 경우 동료 개체 간의 관계는 대개 다대다입니다. 중재자 개체가 도입된 후에는 중재자 개체와 동료 개체 간의 관계가 일반적으로 양방향 일대다 관계가 됩니다. 이는 객체 간의 관계를 더 쉽게 이해하고 구현하도록 만듭니다.
중재자 패턴의 단점
중재자 모델의 잠재적인 단점 중 하나는 과도한 중앙 집중화입니다. 동료 개체의 상호 작용이 매우 크고 복잡하고 이러한 모든 복잡성이 중재자에 집중되면 중재자 개체는 매우 복잡해지고 관리 및 유지 관리가 어려워집니다.