ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Design Pattern] Bridge Pattern
    Learn/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

    댓글

Designed by Tistory.