-
[Design Pattern] Bridge PatternLearn/Architecture 2022. 9. 25. 07:58
# 문제 상황
사각형을 그리는 라이브러리가 있다고 가정해보자. (Drawing Program)
그리는 방식이 두 가지가 있다고 하면 아래와 같이 abstract class를 구성할 것이다.
그런데 추후에 사각형 뿐만 아니라 원도 추가될 것으로 예상된다면?
아래와 같이 Shape이라는 클래스로 추상화를 한 단계 더 할 수 있다.
문제는 다른 종류의 도형이 계속 추가된다고 가정했을 때
도형 하나 당 3개의 클래스가 늘어나므로 클래스가 너무 많아질 수 있다.
# 해결
원인: Abstraction과 Implementation의 커플링이 너무 심하다.
해결: Abstraction과 Implementation을 분리한다.
- 공통적인 것은 abstract class에, variation은 concrete class에 표현한다.
아래와 같이 공통적인 것에 해당하는 Shape, Drawing을 각각 abstract class로 만든다.
- 클라이언트는 Shape, Drawing만 바라보므로 변종에 영향을 받지 않는다.
그러면 Shape와 Drawing의 관계는 어떻게 표현할 것인가?
두 가지 방법이 있을 수 있다.
1. Drawing이 Shape를 사용한다.
- Information Expert Design 관점에서 좋지 않다. (정보를 가진 쪽이 책임을 가진다)
- Encapsulation을 위반하여 커플링이 심해지고 응집도가 낮아진다.
2. Shape이 Drawing을 사용한다.
- 위의 이유로 이게 더 나은 옵션이다.
## 결과
- 변하는 것은 Encapsulation 한다.
- Inheritance보다는 Composition을 사용한다. (전략 패턴, 상태 패턴과 유사함)
# Bridge Pattern
커플링을 제거하기 위해 abstraction과 implementation을 분리하고 bridge로 연결한다.
구조를 표현해보면 아래와 같다.
- Abstraction
- 인터페이스를 정의한다.
- implementor쪽에 대한 레퍼런스를 가진다.
- implementor쪽으로 forwards request를 보낸다. (collaboration)
- RefinedAbstraction
- 인터페이스가 여러 가지 변종이 필요할 때 확장해주는 역할 (예제에서 사각형, 원 ..)
- Implementor
- Implementation쪽의 인터페이스 정의
- ConcreteImplementor
- 실제 implementor의 인터페이스를 구현
# 구현 코드
클라이언트는 아래와 같이 어떤 도형을 그릴 것인지 결정한다.
어떤 shape이냐에 관계 없이 draw함수를 쓸 수 있다.
class Client { public static void main (String argv[]) { Shape r1, r2; Drawing dp; dp = new V1Drawing(); r1 = new Rectangle( dp, 1, 1, 2, 2 ); dp = new V2Drawing(); r2 = new Circle( dp, 2, 2, 3 ); r1.draw(); r2.draw(); } }
Shape에서 draw는 결정할 수 없으므로 채울 수 없다.
대신 drawLine이나 drawCircle은 constructor에서 받아서 위임할 수 있다.
abstract class Shape { abstract public void draw(); private Drawing _dp; Shape (Drawing dp) { _dp = dp; } public void drawLine (double x1, double y1, double x2, double y2) { _dp.drawLine(x1,y1,x2,y2); } public void drawCircle (double x, double y, double r) { _dp.drawCircle(x,y,r); } }
Draw에서는 drawLine이나 drawCircle을 결정할 수 없다.
하위 클래스에 결정해야 하므로 abstract class, method로 만들어준다.
abstract public class Drawing { abstract public void drawLine(double x1, double y1,double x2, double y2); abstract public void drawCircle (double x, double y, double r); }
V1Drawing, V2Drawing에서는 어떤 DP를 사용할지 구현해준다. (Implementation)
class V1Drawing extends Drawing { public void drawLine(double x1, double y1, double x2, double y2) { DP1.draw_a_line(x1,y1,x2,y2); } public void drawCircle(double x, double y, double r) { DP1.draw_a_circle(x,y,r); } } class V2Drawing extends Drawing { public void drawLine(double x1, double y1, double x2, double y2) { DP2.drawline(x1,x2,y1,y2); } public void drawCircle (double x, double y, double r) { DP2.drawcircle(x,y,r); } }
아래는 concrete class의 implementation이다.
class Rectangle extends Shape { public Rectangle (Drawing dp, double x1, double y1, double x2, double y2) { super(dp); _x1 = x1; _x2 = x2; _y1 = y1; _y2 = y2; } public void draw() { drawLine(_x1,_y1,_x2,_y1); drawLine(_x2,_y1,_x2,_y2); drawLine(_x2,_y2,_x1,_y2); drawLine(_x1,_y2,_x1,_y1); } } class Circle extends Shape { public Circle (Drawing dp, double x, double y, double r) { super(dp); _x = x; _y = y; _r = r; } public void draw() { drawCircle(_x,_y,_r); } }
# Bridge Pattern vs Adapter Pattern
공통점
구현을 감춰준다. (구조적인 문제 해결)
차이점
어댑터 패턴은 인터페이스가 달라서 같이 일을 하지 못하는 경우에 사용한다.
- 이미 구조적인 문제가 발생을 했고 이를 해결하기 위해 사용된다. (reengineering, interface engineering)
브릿지 패턴은 구조적인 문제가 생길 것을 예상하여 미리 적용한다. (up-front in a design)
어댑터 패턴은 간단하게 단일 인터페이스를 사용하는데 반해 브릿지 패턴은 더 복잡하다.
'Learn > Architecture' 카테고리의 다른 글
Architectural Styles (1) - 개요 (0) 2023.04.07 [Design Pattern] MVC Pattern (0) 2022.09.25 [Design Pattern] Composite Pattern (0) 2022.09.25 [Design Pattern] Decorator Pattern (0) 2022.09.24 [Design Pattern] Adapter Pattern (0) 2022.09.04