브릿지 패턴?
구현부에서 추상층을 분리하여 각자 독립적으로 변형할 수 있게 하는 패턴이다
또 무슨 말인지 어렵네요
우선 UML 클래스와 함께 살펴볼게요~!
- Abstraction : Implementor를 인스턴스 변수로 합성된 상태이고, function이 추상화된 상태
- Implementor : 추상화된 인터페이스
- ConcreteImplementor : 인터페이스(Implementor)를 구체화한 객체 = 상속 받아서 구현
- RefinedAbstraction : 추상화 클래스(Abstraction)를 구체화한 객체
한마디로, Abstraction 클래스는 Implementor에 의존되어 있는 상태고,
이 의존되어 있는 상태를 추상화하여 실제 구현은 자식 클래스(RefinedAbstraction)에 맡기는 전체 구조입니다
아직도 어렵죠?
예시와 함께 살펴보도록 할게요!
우리는 그림을 그리는 인터페이스(DrawAPI -> Implementor)를 만들었어요
빨간색원과 초록색원을 그리는 각자의 객체(ConcreteImplementor)를 만들고,
해당 도형에 대한 그림을 그리고 싶은데 각자 그리는 방법이 다르겠죠?
빨간색원은 빨간색으로 해야되고, 초록색원은 초록색으로 해야되니까요!!
그럼 우선은, 원(Circle)말고도 나중에 사각형(Rectangle)을 그리라고 할 수 있으니
이를 전체를 아우루는 도형(Shape -> Abstraction)을 만들까 해요!
그 후에 이를 상속받은 원(Circle -> RefinedAbstraction)을 만드는 거죠!
그림을 그리기 위해서는 길이(x,y)와 각도(radius)정보를 알아야 하니
이를 draw() 메서드 인자로 넘기게 되면, 우리가 원하는 그림을 그릴 수 있게 되는 겁니다!
Why 브릿지 패턴?
DrawAPI를 Shape에 합성시킴으로써 조금 더 유연한 설계가 만들어지기 때문이에요
도형이라는 객체와, 그림을 그리는 객체를 분리함으로써 도메인을 나눴는데요
그렇게 되면 Domain 로직에 도형이라는 객체는 그림을 그리는 것과 상관없이 로직을 짤 수 있고,
그림을 그리는 객체는 그리는 것만 집중할 수 있게 되죠
한마디로, SRP(Single Resposibility Principle)을 지킨 객체지향적인 코드가 될 것 같아요
도형이라는 객체(Shape)는 도형을 나타내는 책임만 가지게 되고,
그림을 그리는 객체(DrawAPI)는 도형을 그리는 책임만 가지게 되는 것이죠!
제가 다른 예시로 한번 설명해볼게요~!
예제 코드
저는 게임을 좋아하는 게이머에요
그래서 게임을 접속하고 싶은데, 하는 게임이 너무 많아서 걱정이네요
내가 하는 모든 게임마다 동일한 메서드로 기능을 동작하게 할 수 있을까요?
package designpattern.bridge;
interface PlayGameAPI {
String playGame(final String id, final String password);
}
1.Implementor 인 PlayGameAPI 구현
주어진 id와 password만 있다면 쉽게 게임에 접속할 수 있겠죠?
다음은 제가 하는 게임들이에요!
package designpattern.bridge;
class LOL implements PlayGameAPI {
@Override
public String playGame(final String id, final String password) {
return "LOL Connect complete! [id=" + id + " password=" + password + "]";
}
}
package designpattern.bridge;
class Overwatch implements PlayGameAPI {
@Override
public String playGame(final String id, final String password) {
return "Overwatch Connect complete! [id=" + id + " password=" + password + "]";
}
}
2.ConcreteImplementor 인 LOL, Overwatch 구현
저는 주로 LOL, Overwatch를 주로 하기 때문에 각자 객체를 만들어 봤어요~!
다음은 게임을 하는 Player를 추상화 해볼까요~?
package designpattern.bridge;
abstract class GamePlayer {
protected PlayGameAPI playGameAPI;
public GamePlayer(final PlayGameAPI playGameAPI) {
this.playGameAPI = playGameAPI;
}
public abstract String play();
}
3. Abstraction 인 GamePlayer구현
API를 합성하여 나타낸 객체고, 실제 게임을 play하는 기능은 추상화해서 자식에게 맡겼어요!
GamePlayer는 어떤 게임을 선택할 것인지에 대한 책임만 가지게 되는거죠!
마지막으로 실제 게임을 하기 위한 객체를 만들어볼게요!
package designpattern.bridge;
class OnlineGamePlayer extends GamePlayer {
private final String id;
private final String password;
public OnlineGamePlayer(final PlayGameAPI playGameAPI, final String id, final String password) {
super(playGameAPI);
this.id = id;
this.password = password;
}
@Override
public String play() {
return playGameAPI.playGame(id, password);
}
}
4. RefinedAbstraction 인 OnlineGamePlayer구현
주어진 id와 password만 있으면 게임에 접속할 수 있으니
API를 통해서 게임에 접속하는 객체에요~!
이 OnlineGamePlayer는 게임에 접속하는 책임만 가지게 되는 것이죠!
한번 제대로 되는지 테스트해볼까요?
package designpattern.bridge;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
class OnlineGamePlayerTest {
private GamePlayer gamePlayer;
@Test
void LOL이_올바르게_실행되는지_테스트() {
/* Given */
final PlayGameAPI LOL = new LOL();
final String id = "Ashe";
final String password = "ad";
/* When */
gamePlayer = new OnlineGamePlayer(LOL, id, password);
/* Then */
assertThat(gamePlayer.play()).isEqualTo("LOL Connect complete! [id=Ashe password=ad]");
}
@Test
void Overwatch가_올바르게_실행되는지_테스트() {
/* Given */
final PlayGameAPI LOL = new Overwatch();
final String id = "Ashe";
final String password = "bob!!";
/* When */
gamePlayer = new OnlineGamePlayer(LOL, id, password);
/* Then */
assertThat(gamePlayer.play()).isEqualTo("Overwatch Connect complete! [id=Ashe password=bob!!]");
}
}
테스트가 너무나도 잘 통과 되네요 ㅎㅎ
제가 작성한 코드의 전체 Diagram입니다!
브릿지 패턴의 장점!
이제 마지막으로 장점을 정리하면서 게시글을 마무리할까 해요!
- 브릿지 패턴은 구현에서 추상화를 분리하여 두 가지 객체가 독립적으로 되게 한다!
- 브릿지 패턴은 런타임 의존성과 컴파일 의존성이 다르므로 확장에 유연하다!
- 브릿지 패턴은 직교적인 설계다!
참고
Bridge Pattern - tutorialspoint
Geeks for geeks - Bridge Pattern
'Developer > Design Pattern' 카테고리의 다른 글
Decorator Pattern - 데코레이터 패턴 (2) | 2020.01.26 |
---|---|
Proxy Pattern - 프록시 패턴 (0) | 2020.01.25 |
Composite Pattern - 컴포지트 패턴 (0) | 2019.11.06 |
Command Pattern - 커맨드 패턴 (2) | 2019.10.19 |
Adapter Pattern - 어댑터 패턴 (0) | 2019.06.23 |