의도

  • 구현에서 추상을 분리하여, 이들이 독립적으로 다양성을 가질 수 있도록 함

 

다른 이름

  • 핸들/구현부(Handle/Body)

 

언제 쓰는가?

  • 추상적 개념과 이에 대한 구현 사이의 지속적인 종속 관계를 피하고 싶을 때. 이를테면, 런타임에 구현 방법을 선택하거나 구현 내용을 변경하고 싶을 때.
  • 추상적 개념과 구현 모두가 독립적으로 서브클래싱을 통해 확장되어야 할 때. 이때, 가교 패턴은 개발자가 구현을 또 다른 추상적 개념과 연결할 수 있게 할 뿐 아니라, 각각을 독립적으로 확장 가능하게 함.
  • 추상적 개념에 대한 구현 내용을 변경하는 것이 다른 관련 프로그램에 아무런 영향을 주지 않아야 할 때. 즉, 추상적 개념에 해당하는 클래스를 사용하는 코드들은 구현 클래스가 변경되었다고 해서 다시 컴파일되지 않아야 함.
  • 사용자에게 구현을 완벽하게 은닉하길 원할 때. 소스코드에서도 클래스에 대한 참조자만이 정의되어 있으므로, 클래스의 구현과 속성에 대한 모든 표현 방식이 온전하게 은닉됨.
  • 클래스 수가 급증하는 것을 방지하고자 할 때. 
  • 여러 객체들에 걸쳐 구현을 공유하고자 하며, 또 이런 사실을 사용자 쪽에 공개하고 싶지 않을 때.

 

구조

GoF의 디자인 패턴 중 P.216

  • Abstraction : 추상적 개념에 대한 인터페이스를 제공하고 개게 구현자에 대한 참조자를 관리.
  • RefinedAbstraction : 추상적 개념에 정의된 인터페이스를 확장함.
  • Implementor : 구현 클래스에 대한 인터페이스를 제공함. 다시 말해, 실질적인 구현을 제공한 서브클래스들에 공통적인 연산의 시그니처만을 정의함. Abstraction의 인터페이스와 다른 형태일 수 있음. Abstraction이 더 추상적인 형태.
  • ConcreteImplementor : Implementor 인터페이스를 구현하는 것으로 실제적인 구현 내용을 담음.

 

협력 방법

  • Abstraction 클래스가 사용자 요청을 Implementor 객체에 전달.

 

결과

  1. 인터페이스와 구현 분리 : 구현이 인터페이스에 얽매이지 않게 됨. 추상적 개념에 대한 어떤 방식의 구현을 택할지가 런타임에 결정될 수 있음.Abstraction과 Implementor의 분리는 컴파일 타임 의존성 제거. 더 잘 구조화된 시스템을 이끄는 계층화도 가능. 시스템의 상위 수준에서는 Abstraction과 Implementor만 알면 됨.
  2. 확장성 제고 : Abstraction과 Implementor를 독립적으로 확장.
  3. 구현 세부 사항을 사용자에게 숨기기 : 상세한 구현 내용을 사용자에게서 은닉 가능.

 

구현 / 고려 사항

  • Implementor 하나만 둠 : 구현 방법이 오로지 하나일 때 Implementor를 추상 클래스로 정의하는 것을 불필요. 그럼에도 분리는 의미가 있음. 컴파일 독립 가능.
  • 정의한 Implementor 객체를 생성 : Implementor 객체가 여러개라면 언제, 어떻게, 몇 개나 생성해야 하는가? 생성자에서 단 한 개를 골라도 되고, 여러개 중 골라야 한다면, 소규모일 때 연결 리스트를, 대규모일 때 해시 테이블을 이용할 수 있음.
  • Implementor 공유 : Shared Pointer 처럼 공유 카운트를 관리하며 공유할 수도 있음.
  • 다중 상속 이용 : Abstraction은 public, ConcreteImplementor를 private으로 상속. 하지만 진정한 의미의 Bridge구현이라고 볼 수 없음.

 

예제 코드

  • 책보다 더 간단하게 예제 구성해봤습니다. Abstraction(Bridge) 클래스는 Implementor에 대한 포인터 변수를 가지고 있는 것이 핵심입니다. Implementor가 가지고 있는 연산을 호출하여 자신의 연산을 대체하게 됩니다.
  • 이러한 구성을 통해 얻을 수 있는 이점은 Implementor가 다양한 서브 클래스로 분화하고 어떤 Implementor를 Abstraction이 갖고 있는지에 따라 다양한 연산이 가능하다는 점입니다. 추가로 Implementor를 다른 파일로 분리하게 된다면 Implementor 파일에 변경이 일어난 것은 시스템 전반에 영향을 주지 않아 컴파일 타임을 아낄 수 있다는 점입니다.
  • Abstraction 은 RectureBridge, Implementation은 Teacher 라는 클래스로 구성해봤습니다. 따로 서브 클래스는 없고 teacher의 name 변수가 달라지면서 다른 서브클래스로 교체하여 RectureBridge의 동작이 달라진 점을 강조했습니다.
#include "Bridge1.h"
#include<iostream>

int main()
{
	Teacher teacher1("Tom");
	Teacher teacher2("John");

	RectureBridge recture_bridge;

	recture_bridge.SetTeaher(&teacher1);

	recture_bridge.ClassStart();
	recture_bridge.TakeBreakTime();
	recture_bridge.GiveGrades();
	std::cout << '\n';

	recture_bridge.SetTeaher(&teacher2);

	recture_bridge.ClassStart();
	recture_bridge.TakeBreakTime();
	recture_bridge.GiveGrades();

	return 0;
}

  • Bridge가 Teacher1에서 Teacher2로 갈아타면서 같은 메서드에 대해 다른 동작을 할 수 있습니다.
//Bridge1.h

#pragma once
#include <string>

class RectureBridge
{
public:
	void ClassStart();
	void TakeBreakTime();
	void GiveGrades();
	void SetTeaher(class Teacher* teacher);

private:
	class Teacher* teacher = nullptr;
};

class Teacher
{
public:
	Teacher(std::string name) : name(name) {}

	void StartTeach();
	void StopTeach();
	void ScoreStudent();

private:
	std::string name;
};
  • 인터페이스 간 시그니처가 맞지 않아도 사용 가능하고 위처럼 메서드 이름이 달라도 상관없습니다.
//Bridge1.cpp

#include "Bridge1.h"
#include <iostream>

void RectureBridge::ClassStart()
{
	if (teacher)
	{
		teacher->StartTeach();
	}
}

void RectureBridge::TakeBreakTime()
{
	if (teacher)
	{
		teacher->StopTeach();
	}
}

void RectureBridge::GiveGrades()
{
	if (teacher)
	{
		teacher->ScoreStudent();
	}
}

void RectureBridge::SetTeaher(Teacher* teacher)
{
	this->teacher = teacher;
}

void Teacher::StartTeach()
{
	std::cout << name << "선생님이 가르치기 시작합니다." << std::endl;
}

void Teacher::StopTeach()
{
	std::cout << name << "선생님이 가르치기를 멈춥니다." << std::endl;
}

void Teacher::ScoreStudent()
{
	std::cout << name << "선생님이 학생에게 점수를 줍니다." << std::endl;
}
  • Bridge는 단순히 Teacher의 특정 메서드를 호출하는 모습입니다.
  • Teacher가 다양한 서브 클래스가 있고 호출되는 함수가 가상함수라면 teacher 인스턴스가 무엇인지에 따라 연산이 달라질 것이며, RectureBridge 또한 서브클래스로 분화하게 되면 다른 구현을 가질 수 있게 됩니다.

'디자인 패턴 > 구조' 카테고리의 다른 글

퍼사드(Facade)  (1) 2023.12.22
장식자(Decorator)  (0) 2023.12.22
복합체(Composite)  (2) 2023.12.21
적응자(Adapter)  (2) 2023.12.20
구조 패턴  (0) 2023.12.20

+ Recent posts