개발자 yeah의 Developer Story - 디자인 패턴
오늘은 저번 포스팅에 이어서 브리지 패턴 을 이야기해보려 합니다.
브리지 패턴 은 객체의 확장성을 향상시키기 위한 패턴으로, 객체에서 동작을 처리하는 구현부와 확장을 위한 추상부를 분리합니다.
이렇게 분리된 2개의 추상 계층과 구현 계층은 독립적인 확장 을 가능하게 합니다.
또한 클라이언트 코드에서는 추상 계층과 연결된 구현 계층을 변경하더라도 영향을 받지 않고 사용할 수 있게 됩니다.
그렇다면 어떻게 구현부와 추상부를 나뉘어서 객체의 확장성을 향상 시키는것일까요?
다음 Shape 를 상속받아 Rectangle, Circle 을 만드는 예시가 있습니다.
상위 클래스 Shape 을 상속하여 생성된 Rectangle, Circle 클래스가 존재하며,
해당 클래스들은 각각 Draw 라이브러리 Win_Draw / Mac_Draw 의 사용 여부에 따라
Win_Rectangle / Mac_Rectangle, Win_Circle / Mac_Circle 로 나눠질 수 있습니다.
해당 설계구조의 문제점은 무엇일까요?
현재 설계 디자인에서는 2개의 Shape 과 2개의 Draw 로 인해 종류가 4개(2*2)의 도형이 생겼습니다.
만약 Rectangle, Circle 이후로 다른 Shape이 추가되거나, 다른 Draw 가 추가된다면?
Shape * Draw 만큼의 추가로 클래스가 생기게 됩니다.
위의 예제의 문제점을 브리지 패턴 을 적용하여 해결해보도록 하겠습니다!!
Shape 에서 동작을 처리하는 구현부 Draw와 확장을 위한 추상부 Ractangle / Circle 을 분리하였습니다.
사용할 라이브러리 Draw(Win_Draw / Mac_Draw) 에 따라 UseWinDraw 와 UseMacDraw 로 파생되었으며,
Shape 에서 파생된 Rectangle / Circle 과 Draw가 결합함으로써 원하는 Shape 을 만들 수 있도록 설계하였습니다.
다음 코드를 통해 자세하게 설명하도록 하겠습니다.
public abstract class Shape {
private Draw mDraw; //구현부 Draw 와 연결 역할을 하는 브리지.
public Shape(Draw draw) {
mDraw = draw;
}
public abstract void draw();
/* 연결된 Draw 의 기능을 하위 클래스에서 사용 할수있도록 정의 */
public void drawLine() {
mDraw.drawLine();
}
/* 연결된 Draw 의 기능을 하위 클래스에서 사용 할수있도록 정의 */
public void drawCircle() {
mDraw.drawCircle();
}
}
Shape 클래스는 하위 클래스(Rectangle / Circle)에서 확장 / 변경을 가능하기 위해 추상계층 으로 구현합니다.
복합 객체로 선언된 Draw 객체는 구현부와 연결하는 브리지 역할 을 합니다.
Draw객체는 외부에서 접근 못하도록 선언하였기 때문에 하위 클래스에서 Draw 객체의 기능을 사용 할 수 있도록,
drawLine() 과 drawCircle() 을 정의해 주었습니다.
추상 메서드인 draw() 는 파생된 하위 클래스의 종류에 따라 정의해야 할 메서드입니다.
public class Rectangle extends Shape {
public Rectangle(Draw draw) {
super(draw);
System.out.print("Rectangle 생성 : ");
}
/* 상위 클래스의 Draw 가 제공하는 기능을 통해 draw 구현 */
@Override
public void draw() {
drawLine(); //Rectangle 이기 때문에 drawLine() 호출
}
}
public class Circle extends Shape{
public Circle(Draw draw) {
super(draw);
System.out.print("Circle 생성 : ");
}
/* 상위 클래스의 Draw 가 제공하는 기능을 통해 draw 구현 */
@Override
public void draw() {
drawCircle(); //Circle 이기 때문에 drawCircle() 호출
}
}
Shape 에서 파생된 Rectangle / Circle 클래스 입니다.
Shape 을 상속받기 때문에 생성자를 통하여 Shape에서 브리지 역할 을 하는 Draw 를 set 해주도록 합니다.
Shape 에서 정의된 추상메서드 draw() 를 파생된 클래스의 목적에 맞게 정의합니다.
Rectangle 은 선으로 이루어져 있기 때문에 drawLine(), Circle 은 원으로 이루어져 있기 때문에 drawCircle().
public abstract class Draw {
abstract public void drawLine();
abstract public void drawCircle();
}
Draw 는 사용하는 라이브러리 (Win_Draw / Mac_Draw) 에 따라 다양한 형태로 파생 될 수 있기 때문에,
추상클래스로 정의하고 하위 클래스에서 구현해야 할 추상 메서드를 정의합니다.
public class UseWinDraw extends Draw {
@Override
public void drawLine() {
Win_Draw.drawLine(); //Win_Draw 라이브러리를 사용하여 drawLine() 구현
}
@Override
public void drawCircle() {
Win_Draw.drawCircle(); //Win_Draw 라이브러리를 사용하여 drawCircle() 구현
}
}
public class UseMacDraw extends Draw {
@Override
public void drawLine() {
Mac_Draw.drawLine(); //Mac_Draw 라이브러리를 사용하여 drawLine() 구현
}
@Override
public void drawCircle() {
Mac_Draw.drawCircle(); //Mac_Draw 라이브러리를 사용하여 drawCircle() 구현
}
}
Draw에서 파생되는 하위 클래스 UseWinDraw / UseMacDraw 입니다.
해당 파생된 클래스는 사용한 라이브러리의 종류(Win / Mac)에 따라 파생되었으며,
해당 라이브러리의 함수를 호출하여 정의합니다.
public class Win_Draw {
/* Win Draw 라이브러리 */
public static void drawLine() {
System.out.println("Window 에서 Line 그리는 방식");
}
public static void drawCircle() {
System.out.println("Window 에서 Circle 그리는 방식");
}
}
public class Mac_Draw {
/* Mac Draw 라이브러리 */
public static void drawLine() {
System.out.println("Mac 에서 Line 그리는 방식");
}
public static void drawCircle() {
System.out.println("Mac 에서 Circle 그리는 방식");
}
}
라이브러리 역할을 하고 있는 Win_Draw / Mac_Draw 입니다.
또 다른 라이브러리가 추가됨에 따라 Draw의 파생클래스는 얼마든지 추가될 수 있습니다.
public class BridgePattern {
public static void main(String[] args) {
Shape rectangle;
Shape circle;
Draw draw;
draw = new UseWinDraw();
rectangle = new Rectangle(draw);
rectangle.draw();
draw = new UseWinDraw();
circle = new Circle(draw);
circle.draw();
draw = new UseMacDraw();
rectangle = new Rectangle(draw);
rectangle.draw();
draw = new UseMacDraw();
circle = new Circle(draw);
circle.draw();
}
}
마지막으로 main() 입니다.
Shape 클래스에 원하는 하위클래스(Rectangle / Circle) 를 생성하여 원하는 구현부 Draw를 연결하고 있습니다.
연결되는 Draw에 따라 Win_Draw / Mac_Draw 방식으로 표현되는 것을 확인할 수 있습니다.
브리지 패턴 으로 재설계됨에 Shape 으로부터 확장할 수 있는 추상부 와 구현부 Draw 를 나누게 되었는데요.
추상부와 구현부가 나눠져 있기 때문에 서로 독립적인 확장이 가능합니다.
또한 위에서 문제로 언급했던 파생되는 Shape / Draw 가 추가됨에 따라 무분별하게 클래스가 확장되는 문제는!
Draw 만 추가되고 파생되는 Shape 과 결합하는 형태로 해결할 수 있게 되었습니다.
이렇게 오늘은 브리지 패턴 을 이야기해봤는데요.
궁금하시거나 질문이 있으시면 댓글로 달아주세요 !!
감사합니다!
'Software Development > 디자인 패턴' 카테고리의 다른 글
[디자인 패턴의 정석] 구조패턴 - 어댑터 패턴 (Adapter) (21) | 2020.12.27 |
---|---|
[디자인 패턴의 정석] 생성패턴 - 프로토 타입 패턴 (Prototype) (15) | 2020.12.19 |
[디자인 패턴의 정석] 생성패턴 - 빌더 패턴 (Builder) (5) | 2020.12.18 |
[디자인 패턴의 정석] 생성패턴 - 싱글턴 패턴 (Singleton) (17) | 2020.12.15 |
[디자인 패턴의 정석] 생성패턴 - 추상 팩토리 패턴 (Abstract Factory) (23) | 2020.12.13 |