브릿지(Bridge) 패턴
클래스 기반 브릿지 패턴 이해
브릿지(Bridge) 패턴
브릿지 패턴은 추상화(Abstraction)와 구현(Implementation)을 분리하여 각각 독립적으로 확장할 수 있도록 하는 구조적(Structural) 디자인 패턴이다.
즉, 기능 계층과 구현 계층을 분리하여 유지보수성과 확장성을 높이는 것이 목적이다.
주요 개념
- 추상화(Abstraction)
- 기능을 정의하는 상위 계층 (인터페이스 역할)
- 구현(Implementation)
- 실제 동작을 담당하는 하위 계층
- Bridge
- 추상화 계층이 직접 구현을 참조하는 것이 아닌, 구현 계층을 포인터나 참조(Composition)로 연결 → 느슨한 결합(Loose Coupling)을 유지
classDiagram
class Abstraction {
- Implementation* impl
+ operation()
}
class RefinedAbstraction {
+ operation()
}
class Implementation {
<<interface>>
+ operationImpl()
}
class ConcreteImplementationA {
+ operationImpl()
}
class ConcreteImplementationB {
+ operationImpl()
}
Abstraction --> Implementation : "브릿지 (Bridge)"
RefinedAbstraction --|> Abstraction : "추상화 확장"
Implementation <|.. ConcreteImplementationA
Implementation <|.. ConcreteImplementationB
장점
- 기능과 구현을 독립적으로 확장 가능
- 기능을 추가하면서도 구현을 수정할 필요가 없음
- 코드 유지보수 용이
- 변경 사항이 생겨도 최소한의 수정으로 해결 가능
- 다형성을 활용하여 동적으로 구현 변경 가능
- 런타임에 다른 구현을 교체하는 것이 가능
주의점
- 추상화와 구현의 관계 명확화
- 브릿지 패턴의 핵심은
추상화와구현의 분리 - 너무 세분화하면 코드가 복잡해지고, 너무 단순하면 브릿지 패턴을 쓰는 의미가 줄어들음
- 브릿지 패턴의 핵심은
- 객체 간 결합도(Coupling)를 적절히 조정
- 브릿지 패턴은 Composition(구성)을 사용하여 구현을 위임하는 방식이라 동적으로 객체를 교체가능
- 하지만, 너무 많은 계층을 도입하면 관리가 어려워짐 → 필요한 만큼만 분리 필요
- 적절한 인터페이스 설계
- 구현 인터페이스(Implementation)가 너무 일반적이면 유연성은 높지만 불필요한 메서드 발생 가능성 있음
- 반대로 너무 특정 기능에 맞춰 설계하면 재사용성이 떨어짐
- 객체의 생명주기, 메모리 관리
추상화는구현을 포인터로 관리 필요- 스마트 포인터 (std::shared_ptr) 사용 → 객체가 안전하게 공유되도록 관리
- 소멸자에서 적절히 해제 → 메모리 누수 방지
- 너무 작은 프로젝트에서는 오버헤드가 발생할 수 있음
- 단순한 기능이라면 굳이 브릿지 패턴을 사용할 필요 없음.
- 브릿지 패턴을 사용하면 코드 구조가 복잡해지므로, 확장 가능성이 클 때만 적용하는 것을 추천
예시
C++을 이용하여 예시 코드를 작성하였다. 아래 링크에서 전체를 확인할 수 있다.
https://github.com/grade-e/bridge-cpp-container
- 장치(Device): TV, 라디오 같은 전자기기 (구현부)
- 리모컨(RemoteControl): 전자기기를 조작하는 컨트롤러 (추상화 계층)
- Bridge 역할: 리모컨은 특정 장치의 구현을 직접 사용하지 않고, 인터페이스를 통해 연결
Class diagram
classDiagram
class Device {
+void turnOn()
+void turnOff()
+void setVolume(int volume)
+int getVolume()
}
class TV {
+void turnOn()
+void turnOff()
+void setVolume(int volume)
+int getVolume()
}
class Radio {
+void turnOn()
+void turnOff()
+void setVolume(int volume)
+int getVolume()
}
class RemoteControl {
- Device* device
+void SetDevice()
+void turnOn()
+void turnOff()
+void volumeUp()
+void volumeDown()
}
class AdvancedRemoteControl {
+void mute()
}
Device <|-- TV
Device <|-- Radio
RemoteControl *-- Device
AdvancedRemoteControl --|> RemoteControl
코드
Device()
- 구현의 인터페이스 역할
- 구체적인 장치들이 공통적으로 가져야 할 기본 동작을 정의
- 추상 클래스이므로, 직접 객체로 생성되지 않음
1
2
3
4
5
6
7
8
class Device {
public:
virtual void TurnOn() = 0;
virtual void TurnOff() = 0;
virtual void SetVolume(int volume) = 0;
virtual int GetVolume() = 0;
virtual ~Device() = default;
};
TV, Radio
- TV와 Radio는 Device 인터페이스를 상속받아 구체적인 동작을 구현
- 새로운 장치를 추가할 경우 Device 인터페이스를 구현하기만 하면 됨
- 리모컨(RemoteControl) 코드 수정 없이 새로운 장치 추가가능
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class Radio : public Device {
private:
int volume = 5;
public:
void TurnOn() override { std::cout << "Radio on" << std::endl; }
void TurnOff() override { std::cout << "Radio off" << std::endl; }
void SetVolume(int volume) override {
this->volume = volume;
std::cout << "Radio volume: " << volume << std::endl;
}
int GetVolume() override { return volume; }
};
class TV : public Device {
private:
int volume = 10;
public:
void TurnOn() override { std::cout << "TV on" << std::endl; }
void TurnOff() override { std::cout << "TV off" << std::endl; }
void SetVolume(int volume) override {
this->volume = volume;
std::cout << "TV volume: " << volume << std::endl;
}
int GetVolume() override { return volume; }
};
RemoteControl
- RemoteControl은 추상화 계층을 담당
- 직접 TV나 Radio를 포함하지 않고, Device 인터페이스를 통해 동작을 위임함.
- RemoteControl이 Device의 동작을 직접 수행하지 않고 “연결” 만 담당
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class RemoteControl {
protected:
std::shared_ptr<Device> device; // 장치와 연결 (Bridge 역할)
public:
explicit RemoteControl(std::shared_ptr<Device> dev)
: device(std::move(dev)) {}
void SetDevice(std::shared_ptr<Device> newDevice) {
device = std::move(newDevice); // 새로운 장치로 변경
}
virtual void TurnOn() { device->TurnOn(); }
virtual void TurnOff() { device->TurnOff(); }
virtual void VolumeUp() {
int vol = device->GetVolume();
device->SetVolume(vol + 1);
}
virtual void VolumeDown() {
int vol = device->GetVolume();
device->SetVolume(vol - 1);
}
}
AdvancedRemoteControl
- AdvancedRemoteControl은 RemoteControl을 확장한 추상화의 확장(Refined Abstraction)
- mute() 기능을 추가했지만, 기존 Device 인터페이스는 수정할 필요 없음
- 추상화 계층을 독립적으로 확장가능
1
2
3
4
5
6
7
8
9
10
class AdvancedRemoteControl : public RemoteControl {
public:
explicit AdvancedRemoteControl(std::shared_ptr<Device> dev)
: RemoteControl(std::move(dev)) {}
void Mute() {
std::cout << "command: mute" << std::endl;
device->SetVolume(0);
}
};
main()
- RemoteControl이 Device 객체(TV, Radio)를 참조하여 동작 수행
- RemoteControl이 Device와 직접적으로 연결되는 것이 아니라 인터페이스를 통해 간접적으로 연결
- RemoteControl이 Device 인터페이스를 직접 포함(Composition)하면서도, 특정 구현(TV, Radio)에 의존하지 않음
- RemoteControl을 수정하지 않고도 Device를 확장 가능
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int main() {
std::shared_ptr<Device> tv = std::make_shared<TV>();
std::shared_ptr<Device> radio = std::make_shared<Radio>();
RemoteControl remote(tv); // connect TV ↔ basicRemote
remote.TurnOn();
remote.TurnOff();
std::cout << "--------Change device--------" << std::endl;
remote.SetDevice(radio);
remote.TurnOn();
remote.TurnOff();
std::cout << "-------Advanced remote-------" << std::endl;
AdvancedRemoteControl Remote_mk2(radio); // connect Radio ↔ advancedRemote
Remote_mk2.TurnOn();
Remote_mk2.Mute();
Remote_mk2.TurnOff();
return 0;
}
실행 결과
1
2
3
4
5
6
7
8
9
10
TV on
TV off
--------Change device--------
Radio on
Radio off
-------Advanced remote-------
Radio on
command: mute
Radio volume: 0
Radio off
This post is licensed under CC BY 4.0 by the author.