디자인 패턴 - Bridge Pattern

Lee Seung Jae·2022년 2월 21일
0

Bridge 패턴이란?

브릿지 패턴은 구현부에서 추상층을 분리하여 각자 독립적으로 변형 및 확장이 가능하도록 만드는 패턴이다.

이렇게 보면 너무 추상적인 문장이었다.

기능과 구현에 대해 두개의 별도 클래스로 구현한다.

Bridge의 뜻은 다리이다. 다리가 떨어진 장소를 연결하듯이 Bridge 패턴도 두 장소를 연결하는 역할을 한다고 보면된다.
이것이 Bridge 패턴의 핵심이다.
다리역할을 해주는 두 곳은 기능 클래스구현 클래스 이다.

두가지 경우로 나눠서 생각해보자.

  • 새로운 기능을 추가하고 싶은 경우
  • 새로운 구현을 추가하고 싶은 경우

어떤 새로운 클래스 ex가 있다고 예를 들자.

이 ex 클래스에 새로운 기능(=새로운 메소드)를 추가하고 싶을 때 어떻게 할것인가?

여기서 ex를 상속한 하위 클래스에 exChild 클래스를 만든다.
여기서 작은 클래스 계층이 생성된다.

ex ← exChild

이건 기능을 추가하기 위해 만들어진 계층이다.
이렇게 되면, 상위 클래스는 기본 기능은 가지고 있고, 자식 클래스에서는 새로운 기능을 추가할 수가 있게된다.

이러한 클래스 계층을 기능 클래스 계층 이라고 한다.

추상클래스는 메소드들을 추상 메소드로 선언하고 인터페이스를 정한다.
그리고 하위 클래스에서는 그 추상 메소드를 실제로 구현하는 역할을 한다.

이와같이 상위 클래스와 하위 클래스의 역할 분담에 의해 부품으로서의 가치가 높은 클래스를 만들 수 있는데, 여기서 두번째의 경우가 나온다.

상위 클래스는 추상 메소드에 의해 인터페이스를 규정
하위 클래스는 구체적인 메소드에 의해 그 인터페이스를 구현

이러한 클래스 계층을 구현 클래스 계층 이라고 한다.

새로운 구현을 만들기 위해선 추상클래스의 자식 클래스를 만들어 추상 메소드를 Override 하여 구현하면 된다.

기능 추가 or 구현 추가 선택

클래스 계층이 하나만 있다면 기능 클래스와 구현 클래스가 하나의 계층구조 안에서 섞이기 마련이다.

그래서 기능과 구현의 계층을 독립된 클래스 계층으로 분리하고 그 분리한 두 개의 클래스를 다리를 놓아 연결해주는것이 Bridge 패턴이다.

클래스 다이어그램

image

클래스 다이어그램으로 보면 다음과 같다.

  • Abstraction
    • 추상화 개념의 부모 클래스, 구현에 대한 참조자를 관리한다.
  • RefinedAbstraction
    • 추상화 개념의 확장된 기능을 정의한다.
  • Implementor
    • 인터페이스로써 구현 클래스에 대한 선언을 제공함.
    • ImplementorAbstraction의 메소드 이름은 다를 수가 있다.
  • ConcreteImplementor
    • Implementor에 선언된 기능을 구현하는 콘크리트 클래스이다.

클래스 다이어그램을 가만히 보고있으면 RefinedAbstraction 이 여러개라면,

추상 팩토리메소드가 구현이 될것 같은 느낌이다.

결국 Operation 인터페이스에 따라 또 정의된 Abstraction을 입맛에 맞게 끼워넣어

해당 기능을 사용하는 구조를 만들 수가 있다고 본다.

저 브릿지라는 개념을 생성하고 합성하는 것이 추상 팩토리 메소드 패턴과 관련이 있는것 같고

어댑터 패턴과도 관련이 있는것 같다.

아래는 예전에 구현했던 예제이고

최신으로 구현한 것은 깃허브에 있다.

Display.java

//기능 클래스 계층
public class Display {
    //이 필드가 두 클래스 계층의 다리역할을 한다.
    private DisplayImpl impl;

    public Display(DisplayImpl impl){
        this.impl = impl;
    }

    public void open(){
        impl.rawOpen();
    }

    public void print(){
        impl.rawPrint();
    }

    public void close(){
        impl.rawClose();
    }

    public final void display(){
        open();
        print();
        close();
    }

}

이 클래스는 기능 클래스 최상에 있는 클래스이다. DisplayImpl 필드는 Display 클래스의 구현을 담당하는 인스턴스이다.

이 Impl 필드가 Bridge가 되는것이다.

CountDisplay.java

//기능클래스
public class CountDisplay extends Display{

    public CountDisplay(DisplayImpl impl) {
        super(impl);
    }

    public void multiDisplay(int times){
        open();
        IntStream.range(0, times).forEach(i -> print()); //times 회수만큼 반복하여 표시
        close();
    }
}

Display에 times의 횟수만큼 표시하라는 기능을 추가한 기능 클래스이다.

DisplayImpl.java

//구현 클래스
public abstract class DisplayImpl {
    public abstract void rawOpen();
    public abstract void rawPrint();
    public abstract void rawClose();
}

추상클래스이며 처음,중간,끝에 해당하는 open,print,close 에 대응하여 처리된다.

StringDisplayImpl.java

public class StringDisplayImpl extends DisplayImpl{

    private final String string;
    private final int width;

    public StringDisplayImpl(String string){
        this.string = string;
        this.width = string.getBytes().length;
    }

    @Override
    public void rawOpen() {
        printLine();
    }

    @Override
    public void rawPrint() {
        System.out.println("|" + string + "|");
    }

    @Override
    public void rawClose() {
        printLine();
    }

    private void printLine() {
        System.out.print("+");
        //for문 stream형식 한번 sout 실행될때 '-' 출력
        IntStream.range(0, width).mapToObj(i -> "-").forEach(System.out::print);
        System.out.println("+");
    }
}

DisplayImpl을 상속받은 구현 클래스이다. 문자열을 표시하는 클래스 임과 동시에 DisplayImpl의 하위클래스로써 Override한 rawOpen, rawPrint, rawClose를 구현하였다.

Main.java

public class Main {
    public static void main(String[] args) {
        Display d1 = new Display(new StringDisplayImpl("Hello, Korea"));
        Display d2 = new Display(new StringDisplayImpl("Hello, world"));

        CountDisplay d3 = new CountDisplay(new StringDisplayImpl("Hello, universe"));

        d1.display();
        d2.display();
        d3.display();

        d3.multiDisplay(5);
    }
}

Main클래스에서는 전체 클래스를 조합하여 문자열을 표시한다.
d1,d2,d3 전부 Display의 일종이므로 display 메소드를 호출할 수 있으며, d3은 CountDisplay에 구현된 multiDisplay메소드를 호출 가능하다. Display 인스턴스 안에 StringDisplayImpl 클래스의 인스턴스가 구현을 담당한다.

Bridge 패턴의 핵심

이 패턴의 핵심은 기능 클래스구현 클래스를 분리하는 것이다.
이 두 클래스 계층을 분리해두면 각각의 클래스 계층을 독립적으로 확장이 가능하다.

profile
💻 많이 짜보고 많이 경험해보자 https://lsj8367.tistory.com/ 블로그 주소 옮김

0개의 댓글