Pattern - Bridge

3 minute read

세상을 바꿀 한 문장은 무엇인가 ? 편하지 않은 흥분 상태에서 항상 열심히 일하는 것이다.

  • 래리페이지 ( 구글 공동 창업자 )

의도

구현에서 추상을 분리하여, 이들이 독립적으로 다양성을 가질 수 있도록 합니다.


동기

하나의 추상적 개념이 여러가지 구현으로 구체화될 수 있을 때, 대부분은 상속을 통해서 이 문제를 해결합니다. 추상 클래스로 추상적 개념에 대한 인터페이스를 정의하고, 구체적인 서브 클래스에서 서로 다른 방식으로 이들 인터페이스를 구현합니다. 그러나 이 방법만으로는 충분한 융통성을 얻을 수 없습니다. 상속은 구현과 추상적 개념을 영구적으로 종속시키기 때문에, 추상적 개념과 구현을 분리해서 재사용하거나 수정, 확장하기가 쉽지 않습니다.


활용성

  • 추상적 개념과 이에 대한 구현 사이의 지속적인 종속 관계를 피하고 싶을 때, 이를테면, 런타임에 구현 방법을 선택하거나 구현 내용을 변경하고 싶을 때가 여기에 해당합니다.
  • 추상적 개념과 구현 모두가 독립적으로 서브클래싱을 통해 확장되어야 할 때, 이때, 가교 패턴은 개발자가 구현을 또 다른 추상적 개념과 연결할 수 있게 할 뿐 아니라, 각각을 독립적으로 확장가능하게 합니다.
  • 추상적 개념에 대한 구현 내용을 변경하는 것이 다른 관련 프로그램에 아무런 영향을 주지 않아야 할 때, 즉, 추상적 개념에 해당하는 클래스를 사용하는 코드들은 구현 클래스가 변경되었다고 해서 다시 컴파일 되지 않아야 합니다.

항목별 설명

  • Abstraction
    Window
    추상적 개념에 대한 인터페이스를 제공하고 객체 구현자(implementer)에 대한 참조자를 관리합니다.

  • RefinedAbstraction
    IconWindow
    추상적 개념에 정의된 인터페이스를 확장합니다.

  • implementer
    WindowImp
    구현 클래스에 대한 인터페이스를 제공합니다. 다시 말해, 실질적인 구현을 제공한 서브 클래스 들에 공동적인 연산의 시그너처 만을 정의합니다. 이 인터페이스는 Abstraction 클래스에 정의된 인터페이스에 정확하게 대응할 필요가 없습니다. 즉, 두 인터페이스를 서로 다른 형태 일 수 있습니다. 일반적으로 implementer 인터페이스는 기본적인 구현 연산을 수행하고, Abstraction는 더 추상화된 서비스 관점의 인터페이스를 제공합니다.

  • ConcreteImplementer
    XWindowImp, PMWindowImp
    implementer 인터페이스를 구현하는 것으로 실제적인 구현 내용을 담았습니다.


코드 예제 - 기본

package DesignPattern.gof_bridge.sample_01;

public class Client {
    public static void main(String[] args) {

        Abstraction abstraction = new RefineAbstraction(new ConcreteImplementer1());

        abstraction.workwithImplementer();
    }
}   
package DesignPattern.gof_bridge.sample_01;

public interface Implementer {
    public void implement();
}

package DesignPattern.gof_bridge.sample_01;

// 서비스를 위한 실질적인 처리 방식 
public class ConcreteImplementer1 implements Implementer {

    public void implement() {
        System.out.println("전혀 다른 Implementer 1에 대한 프로세스 ");
    }
}

package DesignPattern.gof_bridge.sample_01;

// 서비스를 위한 실질적인 처리 방식 
public class ConcreteImplementer2 implements Implementer {

    public void implement() {
        System.out.println("전혀 다른 Implementer 2에 대한 프로세스 ");
    }
}

package DesignPattern.gof_bridge.sample_01;

// 추상화된 서비스의 관점에서 Abstraction 구성 
public abstract class Abstraction {

    protected final Implementer implementer;

    public Abstraction(Implementer implementer){
        this.implementer = implementer;
    }

    public abstract void workwithImplementer();
} 

package DesignPattern.gof_bridge.sample_01;

// 추상화된 서비스의 실질적인 처리 
public class RefineAbstraction extends Abstraction {

    public RefineAbstraction(Implementer implementer){
        super(implementer);
    }

    @Override
    public void workwithImplementer() {
        implementer.implement();
    }
}

코드 예제 - 추가 ( 다른 방식 )


/** "Implementor" */
interface BankAPI
{
    public void withdraw();
}
/** "ConcreteImplementor" 1/2 */
class KookminBankAPI1 implements BankAPI
{
    public void withdraw()
    {
        System.out.printf("API1.withdraw");
    }
}
/** "ConcreteImplementor" 2/2 */
class HanaBankAPI2 implements BankAPI
{
    public void withdraw()
    {
        System.out.printf("API2.withdraw");
    }
}

/** "Abstraction" */
interface Developer
{
    public void withdraw();   
}

/** "Refined Abstraction" */
class APIDeveloper implements Developer
{
    private BankAPI bankAPI;
    public APIDeveloper(BankAPI bankAPI)
    {
        this.bankAPI = bankAPI;
    }
    // low-level i.e. Implementation specific
    public void withdraw()
    {
      bankAPI.withdraw(;
    }
    
}

/** "Client" */
class BridgePattern {
    public static void main(String[] args)
    {
      Developer[] bankDevelopers = new Developer[2];
      // 객체의 생성 시점에 역할이 정해지며, 이는 객체의 행동시점에 따라 달라지는 전략 패턴과는 차이가 있다. 
      bankDevelopers[0] = new APIDeveloper(new KookminBankAPI1());
      bankDevelopers[1] = new APIDeveloper(new HanaBankAPI2());
        for (Developer developer : bankDevelopers)
        {
            developer.withdraw();
        }
    }
}


Kotlin - Bridge


package bong.lines.patterns.bridge.inf

interface Implementer {
    fun implement()
}


package bong.lines.patterns.bridge.abstract

import bong.lines.patterns.bridge.inf.Implementer

abstract class Abstraction(val implementer: Implementer) {
    abstract fun workWithImplementer()
}


package bong.lines.patterns.bridge.impl

import bong.lines.patterns.bridge.inf.Implementer

class ConcreteImplementer1 : Implementer {
    override fun implement() {
        print("전혀 다른 Implementer 1에 대한 프로세스 ")
    }
}

package bong.lines.patterns.bridge.impl

import bong.lines.patterns.bridge.inf.Implementer

class ConcreteImplementer2 : Implementer {
    override fun implement() {
        print("전혀 다른 Implementer 2에 대한 프로세스 ")
    }
}

package bong.lines.patterns.bridge.impl

import bong.lines.patterns.bridge.abstract.Abstraction
import bong.lines.patterns.bridge.inf.Implementer

class RefineAbstraction(implementer: Implementer) : Abstraction(implementer) {
    override fun workWithImplementer() {
        implementer.implement()
    }
}


package bong.lines.patterns.bridge

import bong.lines.patterns.bridge.abstract.Abstraction
import bong.lines.patterns.bridge.impl.ConcreteImplementer1
import bong.lines.patterns.bridge.impl.RefineAbstraction

fun main() {
    var abstraction: Abstraction = RefineAbstraction(ConcreteImplementer1())
    
    abstraction.workWithImplementer()
}