Bridge는 비즈니스 논리나 큰 클래스를 독립적으로 개발할 수 있는 별도의 클래스 계층으로 구분하는 Structural Design Pattern이다.
이러한 계층 구조중 하나(종종 추상화라고 함)는 두 번째 계층 구조(구현)의 객체에 대한 참조를 가져온다. 추상화는 구현 객체에 호출의 일부(때로는 대부분)을 위임할 수 있다. 모든 구현체는 공통 인터페이스를 갖게되어 추상화 내부에서 상호 호환이 가능하다.
원과 정사각형처럼 한 쌍의 하위 클래스가 있는 기하학적 Shape
클래스가 있다고 가정해보자. 색상을 통합하도록 이 클래스 계층을 확장하려는 경우 Red
및 Blue
모양 하위 클래스를 만들 계획이다. 그러나 이미 두개의 하위 클래스(색상과 모양)가 있으므로 BlueCircle
및 RedSquare
와 같은 네 개의 클래스 조합을 만들어야 한다.
클래스 조합의 수는 기하급수적으로 증가한다.
새 모양과 색상을 계층에 추가하면 추가할수록 계층 구조가 기하 급수적으로 증가하여 점점 더 나쁜 구조가 될 것이다.
Bridge 패턴은 상속에서 객체 합성으로 전환하여 이 문제를 해결한다. 즉, 한 클래스 내에서 모든 상태와 동작을 포함하는 대신 원래 클래스가 새 계층의 객체를 참조하도록 계층 중 하나를 별도의 클래스 계층 구조로 추출한다.
이를 통해 클래스 계층이 기하 급수적으로 증가하는 것을 방지할 수 있다.
이 접근법에 따라 구조를 보면 Shape
라는 클래스를 추출하여 Color
클래스와 나누고 색상 객체 중 하나를 가리키는 참조 필드를 가져온다. 이 참조는 일종의 "다리"역할을 하게된다.
맨 위에서 언급한 "추상화"와 "구현"이라는 용어에 대해서 이야기해보자. GOF 책에서는 이 Bridge 패턴의 일부로 위 용어들을 소개하는데, 이 용어의 숨겨진 의미들을 파악해보자.
추상화(interface)는 일부 엔티티를 위한 높은 수준의 제어 계층이다. 이 층은 스스로 어떤 실제 작업도 할 수 없게 되어있다. 작업(플랫폼이라고도 함)을 구현 계층에 위임한다.
프로그래밍 언어의 인터페이스나 추상 클래스에 대해 이야기하는 것이 아니다.
실제 앱에 비유해서 추상화를 설명하면 GUI(그래픽 사용자 인터페이스)로 추상화이고 구현체는 사용자 상호작용에 대한 응답으로 GUI 계층이 호출하는 기본 운영 체제 코드(API)가 될 수 있다.
클래스를 추출하지 않고 모든 기능을 한곳에서 관리한다면(브릿지 패턴 안쓴경우) 단일 코드베이스로 단순하게 변경하는 것조차 상당히 어려워 질 수 있다. 더 작고 잘 정의된 모듈을 변경하는 것이 훨씬 쉽다.
정리하면 브릿지 패턴에서 앱에 비유한 추상화와 구현은 다음과 같다.
추상화 객체는 앱의 모양을 제어하여 실제 작업을 연결된 구현 객체에 위임한다. 다른 구현체들을 공통 인터페이스를 따르는 한 상호 교환이 가능해 동일한 GUI가 윈도우와 리눅스에서 작동할 수 있다.
따라서 API 관련 클래스를 건드리지 않고 GUI 클래스를 변경할 수 있다. 또한 다른 운영체제에 대한 지원을 추가하는 것은 구현 계층 구조에서 하위 클래스만 생성하면 된다.
추상화는 높은 수준의 제어 논리를 제공한다. 실제로의 작업은 구현 객체에 의존!
구현은 모든 구체적인 구현에 공통적인 인터페이스를 선언한다. 추상화는 여기서 선언된 메서드를 통해서만 구현 객체와 통신 가능
추상화는 구현과 같은 메서드들을 나열할 수 있지만, 대개 추상화는 구현에 의해 선언되는 광범위한 원시적인 기능들에 의존하는 몇몇 복잡한 행동(메서드)들을 선언한다.
구체적인 구현에는 플랫폼별 코드가 포함되어 있다.
정제된 추상화는 제어 논리의 변형을 제공한다. 그들의 부모처럼 그들은 일반 구현 인터페이스를 통해서 다른 구현들과 함께 작동한다.
일반적으로 고객은 추상화 작업에만 관심이 있다. 그러나 추상화 객체를 구현 객체 중 하나와 연결하는 것은 클라이언트의 일이다.
Bridge 패턴을 사용하여 일부 기능의 여러 변형(클래스가 다양한 데이터베이스 서버와 함께 작동할 수 있는 경우)이 있는 단일 클래스를 분할하고 구성할 수 있다.
여러 독립 차원의 클래스를 확장해야 할 경우 패턴을 사용한다.
런타임에 구현을 전환해야 하는 경우 브릿지 패턴을 사용한다.(추상화 내부의 구현 객체를 바꿀 수 있다 → 운영체제를 확인하고 운영체제에 맞는 API를 사용해야할 때를 가정하는 건가?)
이 부분은 이해가 잘 안가 스터디때 이야기해보기
복잡도: ★★★
인기: ★☆☆
사용 예: Bridge 패턴은 교차 플랫폼 앱을 다루거나 여러 종류의 데이터베이스 서버를 지원하거나 특정 종류의 API 공급자와 작업할 때 특히 유용하다.
식별: Bridge 패턴은 일부 제어 엔티티와 Bridge가 의존하는 여러 다른 플랫폼 간의 명확한 구별에 의해 인식될 수 있다.
index.ts
// 추상화는 두 클래스 계층 부분의 "제어"를 위한 인터페이스를 정의한다.
// 구현 계층에의 객체에 대한 참조를 유지하고,구현들을 모두 위임한다.
class Abstraction {
protected implementation: Implementation;
constructor(implementation: Implementation) {
this.implementation = implementation;
}
public operation(): string {
const result = this.implementation.operationImplementation();
return `Abstraction: Base operation with:\n${result}`;
}
}
// 추상화를 구현 클래스의 변화 없이 확장할 수 있다.
class ExtendedAbstraction extends Abstraction {
public operation(): string {
const result = this.implementation.operationImplementation();
return `ExtendedAbstraction: Extended operation with:\n${result}`;
}
}
// 구현은 모든 구현 클래스의 인터페이스를 정의한다.
// 이 인터페이스는 추상 인터페이스와 다르며 원시적인 기능에 대해서만 제공한다.
// 추상화는 이 기능들을 기반으로 더 높은 수준의 기능들을 정의
interface Implementation {
operationImplementation(): string;
}
// 구체적인 기능은 특정 플랫폼에 대응하고 플랫폼 API를 용한 인터페이스 기능을 구현한다.
class ConcreteImplementationA implements Implementation {
public operationImplementation(): string {
return "ConcreteImplementationA: Here's the result on the platform A";
}
}
class ConcreteImplementationB implements Implementation {
public operationImplementation(): string {
return "ConcreteImplementationB: Here's the result on the platform A";
}
}
function clientCode(abstraction: Abstraction) {
// ..
console.log(abstraction.operation());
// ..
}
let implementation = new ConcreteImplementationA();
let abstraction = new Abstraction(implementation);
clientCode(abstraction);
console.log('');
implementation = new ConcreteImplementationB();
abstraction = new ExtendedAbstraction(implementation);
clientCode(abstraction);
실행 결과
Bridge 패턴은 추상화와 구현이라는 개념을 통해 큰 클래스를 독립적으로 개발할 수 있는 별도의 클래스 계층으로 구분한다.
→ 상속에서 객체 합성으로 전환하여 기하급수적으로 증가하는 클래스 조합을 줄이고 구현 객체에 위임하여 실제 작업을 진행한다.
추상화부분과 구현부분을 나누어 구현 객체를 생성하고 클라이언트 코드 부분에서 추상화 클래스로 구현 객체를 넘겨 프로그램이 실행되도록 함
주로 여러 종류의 데이터베이스 서버 지원, 특정 종류의 API 공급자와 작업할 때 유용하다.