Design Pattern, Bridge

2022. 2. 20. 14:31ETC/Design Patterns

 

Object Structural Pattern

Bridge Pattern

 

-----------------    INDEX     -----------------

 

Bridge Pattern ?

Abstraction

Structure

Sample Code: Java

특징

관련 패턴

 

----------------------------------------------

 

 

Decouple an abstraction from its implementation so that the two can vary independently.

- GoF Design Patterns

 

 

브리지 패턴은 밀접한 클래스 집합을 독립적으로 변형가능 하도록 구현부에서 추상층을 분리하는 패턴입니다.

 

브리지 패턴은 객체의 확장성을 향상하기 위한 패턴으로,

객체에서 동작을 처리하는 구현부body와 확장을 위한 추상부를 분리합니다.

 

다른 용어로는 핸들 패턴handle pattern 또는 구현부 패턴이라고도 합니다.

 

 

왜 필요할까?

데스크탑 어플리케이션을 제작한다고 해봅시다.

어플리케이션은 macOS, windows, linux 운영체제에서 실행되어야 합니다.

그래서 Application 클래스에서 각각 MacApplication, WindowApplication, LinuxApplication 서브클래스를 생성했다고 할게요.

 

시간이 지나면서 Application을 업데이트할 시간이 왔습니다.

Application에 Dark 모드와 같은 테마기능이 생겨서 DarkModeApplication을 생성합니다.

 

이때, 각각의 운영체제에서 이 변경 사항을 반영하기 굉장히 어렵습니다.

문제를 해결하려면 DarkModeApplication아래에 DarkModeMacApplication, DarkModeWindowApplication, DarkModeLinuxApplication를 추가하는 방법 밖에 없어보입니다.

 

브리지 패턴은 구현부와 그 추상층을 분리한다고 했습니다.

브리지 패턴을 반영하면 아래와 같이 분리할 수 있습니다.

 

출처 : https://refactoring.guru/design-patterns/bridge

 

- 외형을 보여주는 GUI를 위한 Application 클래스

- 각 운영체제 위에서 실제 서비스될 기능을 수행할 API 클래스 (ApplicationImp)

 

Application은 DarkMode, LightMode를 서브 클래스로 두고

ApplicationImp는 MacApplicationImp, WindowApplicationImp, LinuxApplicationImp 를 서브 클래스로 둘 수 있습니다.

 

 

Abstraction

브리지 패턴은 복합 객체를 다시 재정의하여 추상 계층화된 구조입니다.

각각의 계층이 독립적으로 확장/변경 가능하도록 하기 위해 구성 클래스의 연결 부분을 추상 계층으로 변경합니다.

 

여기서 주의할 점은, Abstraction이 객체지향 프로그램의 인터페이스나 추상클래스를 의미하는 게 아니라는 점입니다.

Abstraction(혹은 interface)으로 분리되는 계층은 어떤 개체의 high-level 제어 계층입니다.

위의 예제에서 GUI를 분리한 것, 확인하셨죠?

이 층은 스스로 어떠한 실제 작업도 할 수 없게 되어 있습니다.

row-level 작업을 수행하기 위해 Implementator에 의존합니다.

 

 

 

Structure

 

GoF Design Patterns - Bridge

 

Abstraction

- 추상화의 인터페이스를 정의합니다.

- 구현체 타입 객체에 대한 참조를 가집니다.

 

RefinedAbstraction

- Abstraction 인터페이스를 확장합니다.

 

Implementor

- 구현체 클래스에 대한 인터페이스를 정의합니다.

Abstraction과 Implementor 인터페이스는 굉장히 다를 수 있습니다.

일반적으로 Implementor 인터페이스는 기본 연산만 제공하며,

Abstraction은 이런 기본 속성을 기반으로 high-level 연산을 정의합니다.

 

 

Concretelmplementor

- Implementor 인터페이스를 구현하고, 구체적인 구현을 정의합니다.

 

 

Sample Code: Java

간단한 코드를 확인하겠습니다.

 

도형을 그리는 어플리케이션이 있습니다.

도형을 정의하는 Shape 클래스의 서브 클래스로 Triangle과 Square 클래스를 둡니다.

 

ver1.0

각 객체를 생성자를 통해 쉽게 생성합니다.

Shape ← Triangle, Square

 

ver1.1 Color 추가

이 Shape에 빨간색 속성을 가지게 업데이트 됩니다.

red라는 color 속성을 가지며, fill 메서드로 각 도형을 색칠합니다.

Shape ← Red ← Triangle, Square

 

ver1.2 다양한 Color 추가

다양한 색을 칠할 수 있게 업데이트 됩니다.

red뿐만 아니라 blue가 추가됩니다.

Shape ← Red ← RedTriangle, RedSquare

            ← Blue ← BlueTriangle, BlueSquare

 

ver1.3 Circle 도형 추가, yellow 색상 추가

Shape ← Red ← RedTriangle, RedSquare, RedCircle

            ← Blue ← BlueTriangle, BlueSquare, BlueCircle

            ← Yellow ← YellowTriangle, YellowSquare, YellowCircle

 

 

작은 변화에 객체가 급격하게 많아지는 것이 보이나요?

브리지 패턴을 적용하기 전 코드는 Github에서 확인하실 수 있습니다.

 

이제 "색상"이라는 특성을 따로 분리해서 연결bridge해보겠습니다.

 

 

Abstraction

abstract class Shape {
    Color color;

    public Shape(Color color) {
        this.color = color;
    }

    public abstract void draw();
}

 

 

Implementor

public interface Color {
    String fill();
}

 

Concretelmplementor

public class Triangle extends Shape {
    public Triangle(Color color) {
        super(color);
    }

    @Override
    public void draw() {
        System.out.println("RedTriangle drawn. " + color.fill());
    }
}

public class Square extends Shape {
    public Square(Color color) {
        super(color);
    }

    @Override
    public void draw() {
        System.out.println("RedSquare drawn. " + color.fill());
    }
}

 

 

RefinedAbstraction

public class Red implements Color {
    @Override
    public String fill() {
        return "Color is Red";
    }
}

public class Blue implements Color {
    @Override
    public String fill() {
        return "Color is Blue";
    }
}

 

 

Client

public class Client {
    public static void main(String[] args) {
        ArrayList<Shape> shapes = new ArrayList<>();

        shapes.add(new Square(new Red()));
        shapes.add(new Square(new Blue()));
        shapes.add(new Triangle(new Red()));
        shapes.add(new Triangle(new Blue()));

        for (Shape shape: shapes) {
            shape.draw();
        }
    }
}

 

훨씬  유연해진 구조를 확인할 수 있습니다.

참고로 아래와 같이 출력됩니다.

 

 

Output

RedSquare drawn. Color is Red
RedSquare drawn. Color is Blue
RedTriangle drawn. Color is Red
RedTriangle drawn. Color is Blue

 

 

 

특징

Decoupling

인터페이스와 구현의 분리

구현이 인터페이스와 영구적으로 바인딩되었던 코드가 해결됩니다.

또, 추상화 및 구현자를 분리하면 구현에 대한 컴파일 시간 의존성도 제거됩니다. 

 

Extensibility

분리된 추상 계층과 구현 계층은 독립적인 확장할 수 있습니다.

 

Hiding implementation

Client에게 구현 세부 로직이나 정보를 숨깁니다.

 

 

 

관련 패턴

C- Creational Patterns  |  S - Structural Patterns  |  B - Behavioral Patterns

 

 

C: Template Method

템플릿 메서드 패턴은 추상화를 통해 구현 계층을 분리하여 사용합니다.

상위 추상 클래스에서는 선언 작업을 하며, 상속받은 하위 클래스에서는 실제 구현 작업을 합니다.

이때 분리된 추상 계층은 템플릿 메서드 패턴과 유사합니다.

 

C: Abstract Factory

브리지 패턴을 생성하고 복합화될 때 추상 팩토리 패턴이 같이 적용될 수 있습니다.

 

S: Adapter

어댑터 패턴과 브리지 패턴이 유사해보입니다.

어댑터 패턴이 완성된 코드를 통합하고 결합할 때 사용되는 패턴이라면,

브리지 패턴은 처음 설계 단계에서 추상화 및 구현을 위해 확장을 고려한 패턴입니다.

 

 

 

 

 

그럼 지금까지 Bridge Pattern에 대해 알아보았습니다.

오타나 잘못된 내용이 있다면 댓글로 남겨주세요!

감사합니다 ☺️ 

 

 

 

모든 Design Patterns 모아보기

 

Design Patterns

안녕하세요. GoF 디자인 패턴을 정리하고자 합니다. 디자인 패턴은 일주일 전부터 공부를 시작했는데, 스스로 설명하듯 적는게 익히는데 도움이 클 것같아 정말 오랜만에 시리즈로 포스팅하려

gngsn.tistory.com

 

 

'ETC > Design Patterns' 카테고리의 다른 글

Design Pattern, Interpreter  (0) 2022.02.21
Design Pattern, Chain of Responsibility  (0) 2022.02.19
Design Pattern, Flyweight  (0) 2022.02.18
Design Pattern, Prototype  (0) 2022.02.17
Design Pattern, Memento  (0) 2022.02.17