본문 바로가기

Software Development/디자인 패턴

[디자인 패턴의 정석] 구조패턴 - 브리지 패턴 (Bridge)

 

개발자 yeah의 Developer Story - 디자인 패턴

 

오늘은 저번 포스팅에 이어서 브리지 패턴 을 이야기해보려 합니다.

 

 

 

 

브리지 패턴 은 객체의 확장성을 향상시키기 위한 패턴으로, 객체에서 동작을 처리하는 구현부와 확장을 위한 추상부를 분리합니다.

 

이렇게 분리된 2개의 추상 계층과 구현 계층은 독립적인 확장 을 가능하게 합니다.

 

또한 클라이언트 코드에서는 추상 계층과 연결된 구현 계층을 변경하더라도 영향을 받지 않고 사용할 수 있게 됩니다.

 

 

 

그렇다면 어떻게 구현부추상부를 나뉘어서 객체의 확장성을 향상 시키는것일까요?

 

 

 

다음 Shape 를 상속받아 Rectangle,  Circle 을 만드는 예시가 있습니다.

 

 

브릿지 패턴을 적용하기 좋은 예

 

 

상위 클래스 Shape 을 상속하여 생성된 Rectangle, Circle  클래스가 존재하며, 

 

해당 클래스들은 각각 Draw 라이브러리 Win_Draw / Mac_Draw  사용 여부에 따라

 

Win_Rectangle / Mac_Rectangle,    Win_Circle / Mac_Circle 로 나눠질 수 있습니다. 

 

 

 

해당 설계구조의 문제점은 무엇일까요?

 

 

현재 설계 디자인에서는 2개의 Shape2개의 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 과 결합하는 형태로 해결할 수 있게 되었습니다.

 

 

 

 

 

 

 

이렇게 오늘은 브리지 패턴 을 이야기해봤는데요.

 

 

궁금하시거나 질문이 있으시면 댓글로 달아주세요 !!

 

 

감사합니다!