Bridge[Design Pattern]

SnowCat·2023년 3월 6일
0

Design Pattern

목록 보기
8/23
post-thumbnail

의도

  • 큰 클래스 또는 밀접하게 관련된 클래스 집합을 두 개의 개별 계층구조로 나누어 독립적으로 개발할 수 있도록 하는 구조 디자인 패턴

문제

  • 원과 직사각형 모양이 있는 Shape 클래스가 있다 가정해보자
  • Shape 클래스를 확장해 빨강, 파란색이 있는 원, 사각형을 만들고자 한다.
  • 지금 당장이야 괜찮을지 몰라도 유형들이 많아지면 많아질수록 코드가 복잡해지게 된다.

해결책

  • 객체를 상속하지 말고 합성을 하여 문제를 해결할 수 있음
  • 여러 차원 중 하나를 별도의 클래스 계층구조로 추출해 새 계층구조의 객체를 참조하도록 함
  • 색상 관련 코드를 자체 클래스로 추출하고, 모양 클래스에서는 색상 개체를 참조하는 방식을 통해 색상 추가코드를 분리 가능

구조


/** 상위 수준에서 제어와 관련된 로직들을 처리
 * 직접 로직들을 제어하지 않고, 실제 로직이 구현되는 구현 객체에 의존해서 작업을 처리함
 */
class Abstraction {
    protected implementation: Implementation;

    constructor(implementation: Implementation) {
        this.implementation = implementation;
    }

    public operation(): string {
        const result = this.implementation.operationImplementation();
        return `Abstraction: Base operation with:\n${result}`;
    }
}

/** 추상화 객체에 추가로 기능을 확장하게 되더라도 밑의 구현 객체에 영향을 주지 않음
 */
class ExtendedAbstraction extends Abstraction {
    public operation(): string {
        const result = this.implementation.operationImplementation();
        return `ExtendedAbstraction: Extended operation with:\n${result}`;
    }
}

/** 실제 구현 형식을 정의하는 인터페이스
 * 추상화된 객체는 여기 정의된 인터페이스를 통해서만 구현 객체와 소통할 수 있음
 */
interface Implementation {
    operationImplementation(): string;
}

/* 실제 구현된 객체들로 객체가 추가되더라도 코드가 복잡해지지 않음
 */
class ConcreteImplementationA implements Implementation {
    public operationImplementation(): string {
        return 'ConcreteImplementationA: Here\'s the result on the platform A.';
    }
}

class ConcreteImplementationB implements Implementation {
    public operationImplementation(): string {
        return 'ConcreteImplementationB: Here\'s the result on the platform B.';
    }
}

function clientCode(abstraction: Abstraction) {
    console.log(abstraction.operation());
}

let implementation = new ConcreteImplementationA();
let abstraction = new Abstraction(implementation);
/*
Abstraction: Base operation with:
ConcreteImplementationA: Here\'s the result on the platform A.
*/
clientCode(abstraction);

console.log('');

implementation = new ConcreteImplementationB();
abstraction = new ExtendedAbstraction(implementation);
/*
Abstraction: Base operation with:
ConcreteImplementationA: Here\'s the result on the platform B.
*/
clientCode(abstraction);

적용

  • 기능의 여러 변형들을 분리하고 정리하고자 할 때 사용
    클래스의 크기가 커지면 작동 방식 파악과 리팩토링이 어려워짐
    브리지 패턴을 사용해 클래스를 여러 계층구조로 쪼개 유지관리를 용이하게 함
  • 독립적인 차원에서 클래스를 확장하고자 할 때 사용
    원래 클래스를 내비두고 새로운 계층구조의 클래스만 수정 가능
  • 런타임에 구현을 전환할 수 있어야 할 때 사용
    필요하다면 브리지 패턴을 사용해 필드값을 바꾸는 것 만으로도 추상화 내부의 구현 객체를 바꿀 수 있음

구현 방법

  1. 클래스에서 직교 차원 식별
    추상화, 플랫폼, 인터페이스, 구현 등 여러가지가 있음
  2. 클라이언트가 필요로 하는 작업들을 확인하고 기초 추상 클래스에서 정의
  3. 모든 플랫폼들에 제공되야 하는 작업들을 결정하고 이를 구현 인터페이스에서 선언함
  4. 도메인의 모든 플랫폼에 대해 구상 구현 클래스들을 생성하고 구현 인터페이스를 따르도록 함
  5. 추상화 클래스 내에서 구현 유형에 대한 참조 필드를 추가함
  6. 상위 수준 논리의 변형들이 여러개 있으면 기초 추상화 클래스를 확장해 변형된 클래스 생성
  7. 클라이언트 코드는 구현 객체를 추상화에 생성자에 전달해 연관시켜야 함

장단점

  • 플랫폼 독립적인 클래스, 앱 제작 가능
  • 클라이언트 코드는 상위수준의 추상화를 통해 작동하며, 플랫폼 세부 정보에 노출되지 않음
  • 새로운 추상화 클래스와 구현 클래스를 상호 독립적으로 도입해 개방, 폐쇄 원칙 및 단일 책임 원칙 준수
  • 결합도가 높은 클래스에 패턴을 적용할 경우 코드가 더 복잡해질 수 있음

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

profile
냐아아아아아아아아앙

0개의 댓글