Design Patterns

[구조 패턴]브릿지 패턴 (Bridge Pattern)

autumn-code 2024. 8. 29. 14:00

1. 브릿지 패턴 정의

구현부에서 추상층을 분리하여 각자 독립적으로 변형할 수 있게 하는 패턴이다. 

 브릿지 패턴 (Bridge Pattern)
객체의 구현부(implementation)와 추상층(abstraction)을 분리하여 각각 독립적으로 변형할 수 있게 하는 구조적인 디자인 패턴 중 하나이다. 이 패턴은 서로 독립된 두 개의 계층이 서로 연결되어 동작하게 하는 것을 중점으로 한다. 

 

'bridge' 란 '다리' 라는 의미로 다음의 두 장소에서 연결하는 역할을 한다.

  • 기능의 클래스 계층
  • 구현의 클래스 계층

각 구성요소의 설명

  • Client: Client는 Abstraction코드를 사용하는 주체이다.
  • Abstraction: Abstraction은 추상적인 로직을 담고 있는 클래스 이다.
  • Refined Abstraction: Abstraction의 다양한 변형체이고 이 자체가 하나의 또 다른 하나의 계층구조로 확장해 내려갈 수 있다.
  • Implementation: 구체적인 정보를 담고 있습니다. 상태, 액션, 플랫폼에 특화되어 있는 코드와 같은 것들을 담고 있는 클래스이다.
  • Concrete Implementation: Implementation의 또 다른 Implementation을 가지고 별도의 계층구조로 발전시킬 수 있는 클래스이다.

 

활용 상황

  • 런타임에 실제로 사용될 구체적인 구현체가 결정되어야 할때 유용합니다.
  • 구현할 클래스의 기능부 및 구현부가 지속적인 확장 가능성이 있을때 유용합니다.

 

 

Bridge 패턴의 장단점

장점

  • 추상화된 인터페이스와 구현된 클래스를 분리하여 유연한 확장이 가능하다. 새로운 클래스를 추가하거나 변경할 때, 기존 코드를 수정하지 않고도 새로운 클래스와 연결하여 쉽게 확장할 수 있게 해준다.
  • 코드의 재사용성을 높여준다. 구현된 클래스와 추상화된 인터페이스를 분리하여 구현 클래스를 재사용하거나 다른 구현 클래스와 연결하여 쉽게 재사용할 수 있습니다.
  • 코드의 가독성을 높여준다. 추상화된 인터페이스와 구현된 클래스를 분리하여 코드의 의미를 명확하게 전달할 수 았다.

단점

  • 코드의 복잡성이 증가한다. 브리지 패턴을 구현하려면 클래스 간의 연결이 필요하므로 코드의 복잡성이 증가할 수 있다.
  • 추상화된 인터페이스와 구현된 클래스를 분리하는 것이 코드의 중복을 일으킬 수 있다. 구현된 클래스 간의 공통점이 많은 경우 추상화된 인터페이스와 구현된 클래스를 분리하는 것이 중복된 코드를 일으킬 수 있다.
  • 브리지 패턴을 적용하면 런타임에 객체가 생성되므로 약간의 성능 저하가 발생할 수 있다. 하지만 대부분의 상황에서는 성능에 큰 영향을 미치지 않는다.

예제 코드

원격제어와 가전제품

RemoteControl(원격제어) 라는 클래스와 Device 라는 클래스를 사용하여 다양한 종류의 원격제어 바식과 가전제품을 조합
// Implementor (Device 인터페이스)
interface Device {
    void turnOn();
    void turnOff();
    void setVolume(int percent);
    boolean isOn();
}

// Concrete Implementor (구체적인 가전제품 클래스)
class TV implements Device {
    private boolean on = false;
    private int volume = 50;

    public void turnOn() {
        on = true;
        System.out.println("TV is turned on.");
    }

    public void turnOff() {
        on = false;
        System.out.println("TV is turned off.");
    }

    public void setVolume(int percent) {
        this.volume = percent;
        System.out.println("TV volume set to " + percent + "%.");
    }

    public boolean isOn() {
        return on;
    }
}

class Radio implements Device {
    private boolean on = false;
    private int volume = 50;

    public void turnOn() {
        on = true;
        System.out.println("Radio is turned on.");
    }

    public void turnOff() {
        on = false;
        System.out.println("Radio is turned off.");
    }

    public void setVolume(int percent) {
        this.volume = percent;
        System.out.println("Radio volume set to " + percent + "%.");
    }

    public boolean isOn() {
        return on;
    }
}

// Abstraction (RemoteControl 추상 클래스)
abstract class RemoteControl {
    protected Device device;

    public RemoteControl(Device device) {
        this.device = device;
    }

    abstract void power();
    abstract void volumeUp();
    abstract void volumeDown();
}

// Refined Abstraction (구체적인 리모컨 클래스)
class BasicRemote extends RemoteControl {

    public BasicRemote(Device device) {
        super(device);
    }

    public void power() {
        if (device.isOn()) {
            device.turnOff();
        } else {
            device.turnOn();
        }
    }

    public void volumeUp() {
        device.setVolume(60);
    }

    public void volumeDown() {
        device.setVolume(40);
    }
}

class AdvancedRemote extends RemoteControl {

    public AdvancedRemote(Device device) {
        super(device);
    }

    public void power() {
        if (device.isOn()) {
            device.turnOff();
        } else {
            device.turnOn();
        }
    }

    public void volumeUp() {
        device.setVolume(80);
    }

    public void volumeDown() {
        device.setVolume(20);
    }

    public void mute() {
        device.setVolume(0);
        System.out.println("Device is muted.");
    }
}

// 클라이언트 코드
public class BridgePatternExample {
    public static void main(String[] args) {
        Device tv = new TV();
        RemoteControl basicRemote = new BasicRemote(tv);
        basicRemote.power();
        basicRemote.volumeUp();

        Device radio = new Radio();
        RemoteControl advancedRemote = new AdvancedRemote(radio);
        advancedRemote.power();
        advancedRemote.volumeUp();
        ((AdvancedRemote)advancedRemote).mute();
    }
}
  • Abstraction(추상화): RemoteControl 클래스는 원격 제어의 공통 인터페이스를 정의
  • Refined Abstraction(구체적인 추상화): BasicRemote(기본 리모컨), AdvancedRemote(고급 리모컨)과 같은 구체적인 원격 제어 클래스들이 RemoteControl 클래스를 상속
  • Implementor(구현자): Device 인터페이스는 가전제품을 정의하는 메서드를 제공
  • Concrete Implementor(구체적인 구현자): TV, Radio와 같은 클래스들이 Device 인터페이스를 구현하여 실제 가전제품을 정의

출력 결과 

TV is turned on.
TV volume set to 60%.
Radio is turned on.
Radio volume set to 80%.
Device is muted.

 

 

  •  RemoteControl(리모컨)과 Device(가전제품)는 서로 독립적으로 확장 가능. 새로운 리모컨이나 새로운 가전제품을 추가할 때, 기존 클래스에 영향을 주지 않음.
  • 브릿지 역할: RemoteControl 클래스는 Device 인터페이스를 통해 실제 가전제품과의 연결을 담당하며, 다양한 리모컨 방식에 대한 구현과 가전제품 제어를 분리.
  • 유연성: 이 패턴을 통해 기본 리모컨과 고급 리모컨을 같은 가전제품에 적용할 수 있고, 새로운 리모컨과 가전제품을 쉽게 추가할 수 있다.

 

 

브리지 패턴을 사용하면 기존의 코드를 변경하지 않고도 새로운 기능을 추가하거나 수정할 수 있기때문에 유지보수성이 우수해집니다. 또한 코드의 재사용성을 높여 개발 시간과 비용을 절감할 수 있습니다. 하지만 구현할 때 클래스 간의 결합도를 최소화하면서도 추상화와 구현의 분리를 유지하는 것이 중요 하다고 생각 합니다. 그렇지 않으면 코드의 복잡성이 증가할 수 있고, 중복된 코드가 발생할 수 있게 됩니다. 따라서 구현할 기능이 복잡하거나 확장성과 유지보수성을 고려해야 하는 경우에 적절하게 사용해야 합니다.