반응형
반응형
반응형

브릿지 패턴은 소프트웨어 공학에서 사용되는 디자인 패턴인데, 이는 "구현(implementation)으로부터 추상(abstraction) 레이어를 분리하여 이 둘이 서로 독립적으로 변화할 수 있도록 한다."

브릿지는 캡슐화(encapsulation), 집합(aggregation)을 사용하고 또한 다른 클래스들로 책임을 분리시키기 위해 상속(inheritance)를 사용할 수 있다. 


어떤 클래스가 자주 바뀐다면(varies), 객체 지향 프로그래밍의 특징들은 아주 유용해질 수 있다. 왜냐하면 프로그램 코드를 수정하는데 프로그램에 대해 최소한만 알고도 쉽게 수정할 수 있기 때문이다. 브릿지 패턴은 어떤 클래스와 그 클래스가 하는 일이 자주 변화할 때 유용하게 사용될 수 있다. 여기에서 어떤 클래스는 구현(implementation)이라 생각할 수 있고, 그 클래스가 할 수 있는 일은 추상(abstraction)으로 생각할 수 있다. 브릿지 패턴은 추상화(abstraction)의 두 계층(layer)로 생각할 수도 있다.


브릿지 패턴은 종종 어댑터 패턴과 혼동될 수 있다. 사실, 브릿지 패턴은 종종 클래스 어댑터 패턴을 사용해서 구현되기도 한다. 아래에 나오는 자바 코드가 그 예가 될 것이다.


변종(Variant) : 추상(abstraction)이 사용되는 시점까지 구현의 존재를 미룸으로써 구현을 더욱 갈라놓을 수 있다.(정확히 이해는 안되지만 구현 객체를 최대한 늦게 생성함으로써 구현과 추상의 관계를 더욱 약하게 할 수 있다는 말인것 같네요;

원문 : The implementation can be decoupled even more by deferring the presence of the implementation to the point where the abstraction is utilized.)

구조 Structure

Abstraction - 추상 인터페이스를 정의한다. Implementor에 대한 레퍼런스를 유지한다.

RefinedAbstraction - Abstraction에 의해 정의된 인터페이스를 확장한다.(extends)

Implementor - 구현 클래스를 위한 인터페이스를 정의한다.

ConcreteImplementor - Implementor 인터페이스를 구현한다. 

예 Example

아래의 자바 프로그램은 'shape'를 이용한 예이다. 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
/** "Implementor" */
interface DrawingAPI {
    public void drawCircle(double x, double y, double radius);
}
  
/** "ConcreteImplementor"  1/2 */
class DrawingAPI1 implements DrawingAPI {
   public void drawCircle(double x, double y, double radius) {
        System.out.printf("API1.circle at %f:%f radius %f\n", x, y, radius);
   }
}
  
/** "ConcreteImplementor" 2/2 */
class DrawingAPI2 implements DrawingAPI {
   public void drawCircle(double x, double y, double radius) {
        System.out.printf("API2.circle at %f:%f radius %f\n", x, y, radius);
   }
}
  
/** "Abstraction" */
abstract class Shape {
   protected DrawingAPI drawingAPI;
  
   protected Shape(DrawingAPI drawingAPI){
      this.drawingAPI = drawingAPI;
   }
  
   public abstract void draw();                             // low-level
   public abstract void resizeByPercentage(double pct);     // high-level
}
  
/** "Refined Abstraction" */
class CircleShape extends Shape {
   private double x, y, radius;
   public CircleShape(double x, double y, double radius, DrawingAPI drawingAPI) {
      super(drawingAPI);
      this.x = x;  this.y = y;  this.radius = radius;
   }
  
   // low-level i.e. Implementation specific
   public void draw() {
        drawingAPI.drawCircle(x, y, radius);
   }
   // high-level i.e. Abstraction specific
   public void resizeByPercentage(double pct) {
        radius *= pct;
   }
}
  
/** "Client" */
class BridgePattern {
   public static void main(String[] args) {
       Shape[] shapes = new Shape[] {
           new CircleShape(1, 2, 3, new DrawingAPI1()),
           new CircleShape(5, 7, 11, new DrawingAPI2()),
       };
  
       for (Shape shape : shapes) {
           shape.resizeByPercentage(2.5);
           shape.draw();
       }
   }
}





반응형
반응형

일반적인 클래스 설계


우리는 유닛 클래스를 설계해야 합니다.
이 유닛은 마린과 매딕이 존재합니다.
마린은 총을 쏘고 매딕은 주사기로 치료를 합니다.


abstract class 유닛{
    void 무기사용();
}

class 마린 : 유닛{
    void 무기사용(){총.사용하기();}
}

class 매딕 : 유닛{
    void 무기사용(){주사기.사용하기();}
}

class 총{
    void 사용하기(){발사;}
}

class 주사기{
    void 사용하기(){치료;}
}



클라이언트의 요구사항


마린도 주사기를 사용하고 매딕도 총을 사용할 수 있게 하고 싶다는 요구사항이 들어왔습니다.
그래서 상속을 이용해 아래와 같이 해결하였습니다.


abstract class 유닛{
    void 무기사용();
}

class 마린 : 유닛{
    void 무기사용();
}

class 매딕 : 유닛{
    void 무기사용();
}

class 총쏘는마린 : 마린{
    void 무기사용(){총.사용하기();}
}

class 치료하는마린 : 마린{
    void 무기사용(){주사기.사용하기();}
}

//총쏘는매딕과 치료하는매딕의 구현은 생략

class 총{
    void 사용하기(){발사;}
}

class 주사기{
    void 사용하기(){치료;}
}



그런데 다시 새로운 요구사항이 들어왔습니다.

새로 추가된 요구사항



  1. 파이어뱃의 추가 - 화염방사기를 사용함

  2. 고스트의 추가 - 총을 사용함

  3. 치료하는 파뱃, 총쏘는 파뱃, 치료하는 고스트, 화염방사기 마린...등의 구현이 필요



처음 요구사항을 해결한 방식으로 추가된 요구사항을 해결할 수 있지만 유닛과 무기가 늘어날 수록 클래스의 숫자는 폭발적으로 늘어나게 됩니다.
우리는 이 문제를 디자인 패턴을 이용하여 해결하려 합니다.

Bridge 패턴이란?




사전적인 의미

추상화 개념과 구현을 분리하여 독립적으로 변화할 수 있도록 한다

패턴을 적용할 상황

파생 클래스의 폭발적 증가 없이 다양한 구현이 필요한 상황



'알기쉬운 디자인 패턴'에 나오는 'Bridge 패턴'은 다음과 같은 기본 전략을 따르고 있습니다.


  • 무엇이 변경되는지 찾아내고 그것을 캡슐화하자

  • 상속보다는 합성을 쓰도록 노력하자



위의 내용들이 지금 당장 이해되지 않더라도 괜찮습니다.
차근차근 풀어나가 보겠습니다.

Bridge 패턴의 적용


위에서 발생한 요구사항에 따른 문제는 'Bridge 패턴'을 적용할 상황(파생 클래스의 폭발적 증가 없이 다양한 구현이 필요한 상황)에 걸맞습니다.
사전적 의미는 일단 배제하고 두가지 기본 전략을 통해 위의 예제를 분석하겠습니다.

우선 첫번째 '무엇이 변경되는지 찾아내고 그것을 캡슐화하자'
위의 예제에서 변경되는 것은 유닛이 [마린과 매딕]으로, 무기가 [총과 주사기]로 변경됩니다.
마린과 매딕은 그대로 유닛으로 두고 총과 주사기를 무기화 시킵니다.


abstract class 무기{
    void 사용하기();
}

class 총 : 무기{
    void 사용하기(){발사;}
}

class 주사기 : 무기{
    void 사용하기(){치료;}
}




두번째 전략 '상속보다는 합성을 쓰도록 노력하자'를 적용해 보겠습니다.
합성이란 것은 복잡한 것이 아니라 하나의 클래스가 다른 클래스를 포함하여 사용하게 하는 것입니다.
그렇다면 '무기가 유닛을 사용하는 것'과 '유닛이 무기를 사용하는 것'중 무엇이 맞을까요?
당연히 유닛이 무기를 사용하는 것이 맞겠죠.


//무기 클래스를 사용하도록 변경된 유닛관련 클래스
abstract class 유닛{
    무기 arms;
    유닛(무기 a){arms = a}; //생성자
    void 무기사용(){arms.사용하기();}
}

class 마린 : 유닛{
    마린(무기 a){super(a);} //생성자, super는 부모클래스
}

class 매딕 : 유닛{
    매딕(무기 a){super(a);} //생성자, super는 부모클래스
}

//무기관련 클래스는 변경사항 없슴
abstract class 무기{
    void 사용하기();
}

class 총 : 무기{
    void 사용하기(){발사;}
}

class 주사기 : 무기{
    void 사용하기(){치료;}
}



생성자를 통해서 무기를 인수로 받고 유닛 클래스의 파생 클래스(마린과 매딕)는 무기의 종류는 모르지만 사용합니다.
위의 클래스를 이용한 간단한 예제를 만들어 보겠습니다.


무기 arms = new 총();
유닛 marine = new 마린(arms);

marine.무기사용();



마린이 주사기를 사용하는 것도 문제 없겠죠?

패턴이 적용된 모습에 대한 분석


위에서 'Bridge 패턴'의 사전적 의미는 "추상화 개념과 구현을 분리하여 독립적으로 변화할 수 있도록 한다" 였습니다.
패턴의 적용으로 아래와 같은 구조를 가지게 되었습니다.

[유닛]-[마린,매딕] ---사용---> [무기]-[총,주사기]

추상화 개념은 유닛이 마린과 매딕으로 분류된다는 것이고 유닛이 무기사용에 대한 구현을 무기클래스로써 분리한 것입니다.

요구사항 해결하기


이제 'Bridge 패턴'을 적용한 클래스 설계에 새로 추가된 요구사항을 적용해 보겠습니다.


abstract class 유닛{
    무기 arms;
    유닛(무기 a){arms = a}; //생성자
    void 무기사용(){arms.사용하기();}
}

class 마린 : 유닛{
    마린(무기 a){super(a);} //생성자, super는 부모클래스
}

class 매딕 : 유닛{
    매딕(무기 a){super(a);} //생성자, super는 부모클래스
}

class 파이어뱃 : 유닛{
    파이어뱃(무기 a){super(a);} //생성자, super는 부모클래스
}

class 고스트 : 유닛{
    고스트(무기 a){super(a);} //생성자, super는 부모클래스
}

abstract class 무기{
    void 사용하기();
}

class 총 : 무기{
    void 사용하기(){발사;}
}

class 주사기 : 무기{
    void 사용하기(){치료;}
}

class 화염방사기 : 무기{
    void 사용하기(){태우기;}
}



'알기쉬운 디자인 패턴'에서의 예제는 저의 예제와 다릅니다. 책의 예제는 Adapter 패턴도 혼합되어 있습니다.
'Bridge 패턴'에 대한 설명은 이로써 마치겠습니다.
최대한 쉽고 짧게 쓴다고 노력했는데 'Bridge 패턴'을 이해하는데 도움이 되었을지 궁금하네요.
'Bridge 패턴'을 더욱 자세하게 공부하시고 싶으시다면 반드시 책을 참고하시기 바랍니다.


출처 : http://ani2life.egloos.com/2904131

이렇게 정리를 하는 님들의 열정에 정말.... 감동받습니다.

그 어떤 사실을 떠나, 박수~~~

반응형

+ Recent posts