정의
- 브릿지 패턴(Bridge Pattern)이란 추상화 계층과 구현 계층을 분리하여 시스템의 확장성과 유지보수성을 높이는 패턴이다.
- 다양한 모양의 그림을 그릴 수 있는 어플리케이션을 개발하고 있다고 가정해보자. 이 어플리케이션은 원, 사각형, 삼각형 등 다양한 모양의 도형을 그릴 수 있고 빨강, 파랑, 초록 등의 색깔을 사용하여 도형의 색깔을 칠할 수 있다. 만약에 빨간색 원을 만든다고 가정한다면 다음과 같은 코드를 사용할 수 있을 것이다.
class RedCircle {
func rotate() {
print("Rotate RedCircle")
}
func move() {
print("Move RedCircle")
}
func upScale() {
print("Upscale RedCircle")
}
func downScale() {
print("Downscale RedCircle")
}
}
let redCircle = RedCircle()
redCircle.rotate()
- 하지만 해당 코드는 빨간 원이라는 도형 밖에 생성하지 못하는 확장성이 낮은 코드이다. 예를 들어, 파란색 사각형이 필요하다면? 녹색 삼각형이 필요하다면? 새로운 색깔이나 도형이 필요할 때마다 일일이 클래스를 생성하면 너무 많은 클래스들이 생겨나게 된다.
- 이 문제는 한 클래스가 ‘도형’ 과 ‘색깔’을 변경하는 책임을 모두 맡으려고 하기에 발생하는 문제이다. 브릿지 패턴(Bridge Pattern)은 해당 문제를 계층을 분리하는 방식으로 전환하여 해결한다. 즉, ‘도형’ 계층과 ‘색깔’ 계층을 분리하여 생각한다는 것이다. ‘도형’ 계층이 삼각형, 사각형, 원 등을 생성해내고 ‘색깔’ 계층이 빨강, 초록, 파랑 등의 색깔을 생성해낸 후, ‘도형’ 계층이 ‘색깔’ 계층을 참조하여 사용할 수 있는 방식이다. 서로 다른 계층을 연결(참조)해주는 역할을 바로 Bridge 라고 한다.
브릿지 패턴(Bridge Pattern) 의 구조

- Abstraction ⇒
- 추상 계층의 프로토콜 및 클래스를 정의한다.
- 구현 계층의 클래스 인스턴스를 참조하는 Bridge 영역을 생성한다.
- RefinedAbstraction ⇒
- Abstraction 내부의 기능을 추가하거나 확장한다.
- Implementor ⇒
- ConcreteImplementor ⇒
- Implementor 프로토콜을 구체적으로 구현한다.
브릿지 패턴 적용 과정 예시
- 구현 계층의 프로토콜 (Implementor)을 정의한다.
protocol Coloring {
var colorName: String { get set }
func createColor()
}
- Implementor 프로토콜을 구체적으로 구현 (ConcreteImplementor)한다. Implementor를 구체적으로 구현함으로써 해당 프로토콜로부터 파생되는 다양한 색깔 객체를 만들어낼 것이다.
class Green: Coloring {
var colorName: String
init(colorName: String) {
self.colorName = colorName
}
func createColor() {
print(colorName)
}
}
class Red: Coloring {
var colorName: String
init(colorName: String) {
self.colorName = colorName
}
func createColor() {
print(colorName)
}
}
- 그 후, 추상 계층의 프로토콜 및 클래스(Abstraction)를 정의한다. Abstraction 부분은 추상 계층과 구현 계층을 연결하는 Bridge가 존재해야 함을 기억해야 한다.
class Shape {
var coloring: Coloring
var shape: String
init(coloring: Coloring, shape: String) {
self.coloring = coloring
self.shape = shape
}
func create() {
print("\(coloring.colorName) \(shape) 생성")
}
}
let greenSquare = Shape(coloring: Red(colorName: "녹색"), shape: "사각형")
greenSquare.create()
- Abstraction 내부의 기능을 추가하거나 확장 (RefinedAbstraction)할 수 있다. 이 때 확장할 수 있는 경우는 두 가지인데,
- Abstraction을 상속받아서 메소드를 추가한다.
- extension을 이용해서 메소드를 추가한다. (Swift에서는 이 방법이 더 편한 것 같다.)
class RotateShape: Shape {
func rotate() {
print("\(coloring.colorName) \(shape) 돌아라")
}
}
extension Shape {
func rotate() {
print("\(coloring.colorName) \(shape) 돌아라")
}
}
패턴 사용 시기
- 하나의 클래스가 너무 많은 책임을 떠안고 있을 경우, 수행 역할을 알맞게 나눠야 할 때
패턴의 장점
- OCP를 만족한다 ⇒ 직접 메소드 안에서 코드를 수정하는 것이 아닌 추상화 계층 내에서 기능을 확장시켜나가는 방식을 취함.
- SRP를 만족한다 ⇒ 추상화 계층은 사용하는 쪽의 입장에만 관심이 있고, 구현 계층은 오직 구현에만 관심이 있다. 즉, 책임이 단일하다.
- 추상화 계층과 구현 계층의 관심사를 분리하여 하나를 변경해도 다른 하나에 영향을 주지 않게 하여 변경하기 쉽게 만든다.
패턴의 단점
- 계층 구조가 늘어나 코드가 복잡해진다.
- 코드를 이해하기 쉽게끔 세심하게 설계해야 함. 즉, 개발 시간이 증가한다.
참고 문헌
브릿지패턴은 개념은 알고있지만 적용해본적은 없는 디자인패턴인거같아요 ㅎㅎ...
역시 실제로 적용하는건 언제나 어려운일인것같네요
객체지향원칙을 준수할수있다는 장점이있지만 역시나 계층구조가 늘어나고 코드점핑이늘어나는부분이 도입을 망설이게되는 이유가될수도있겠다는생각이드네요
원칙을 지키기 위한 디자인패턴을 도입했을때 발생하는 추상화나 계층추가문제로인해 직관성이 떨어지고 시간이좀더 걸리는부분을 조율을 잘해야겠다는 생각이드네요 ㅎㅎ