반응형
브릿지 패턴이란?
큰 클래스나 클래스들의 집합을 추상화, 구현을 이용해서 계층구조로 작성하여 독립적으로 개발할 수 있도록 만든 디자인패턴이다.
마치 다리처럼 프로그램의 추상화 부분과 구현 부분을 연결을 해주어 서로 독립적으로 변경하거나 확장할 수 있도록 하는 것이다.
왜 사용해야 하나요?
- 추상화와 구현 부분을 독립적으로 확장할 수 있다 => 기능을 확장하면서도 기존 코드를 건들지 않는다는 말이다!
- 특히 복잡한 계층구조에서 이를 활용하면 많은 수의 클래스를 줄일 수 있습니다.
- 런타임시 구현을 바꿀 수 있는 유연성을 갖게 됩니다.
- 클라이언트에게 구현부의 변경을 숨길 수 있게 되어 안정성이 증가합니다!
어떤 경우에 사용하나요?
- 복잡한 클래스의 계층구조를 가지고 있어 많은 수의 클래스가 생겨 추가하거나 변경하기 어려울 때 사용합니다
예를 들어 전자제품과 리모컨을 설계한다고 했을때 브릿지 패턴을 사용하지 않으면 리모콘을 모든 전자기기 마다 만들어야 해서 많은 수의 리모콘이 생겨나서 관리하기 쉽지 않게 된다... 사용자 입장에서도 하루종일 리모콘 찾느라 시간을 다 보낼 것같다 :(
하지만 브릿지 패턴을 적용해서 리모컨과 전자제품의 결합도를 낮추어 분리해 주면 하나의 리모컨으로 여러 전자제품들을 조작할 수 있게 된다. (슈퍼리모컨!!!) - 추상화와 구현이 동시에 변경될 수 있는 상황에 사용하면 좋다.
여러 플랫폼에서 동일한 응용 프로그램을 실행할 때, 각 플랫폼은 고유한 GUI 구현을 가지고 있기 때문에 브릿지 패턴을 이용해서 분리를 해두면. 플랫폼 간의 차이를 해결할 수 있게 된다!
어댑터 패턴은 이미 만들어져 있는 앱과 함께 사용됩니다. 원래 호환되던 것을 호환되게 하는 것.
브릿지 패턴은 설계단계에서 의도적으로 독립시키는 것! 헷갈릴 수 있으니 잘 구분하자.
문제점
- 처음부터 필요하지 않을 수도 있는 추사화 계층과 구현계층을 나눠야 해서 설계가 복잡해질 수 있다.
- 너무 많은 클래스가 생성될 경우 코드의 가독성과 유지보수가 어려워진다.
구현 방법
먼저 전자제품들을 만들어 보자.
전자제품들의 기능들을 추상화하여 인터페이스로 만든다.
package com.example.designpattern.stuctural.bridge.devices;
public interface Device {
boolean isEnabled();
void enable();
void disable();
int getVolume();
void setVolume(int volume);
int getChannel();
void setChannel(int channel);
void printStatus();
}
간단하게 전원, 볼륨, 채널로 구성되도록 하였고
해당 인터페이스를 기반으로 라디오와 티비를 만들어 주자.
package com.example.designpattern.stuctural.bridge.devices;
public class Radio implements Device{
private boolean on = false;
private int volume = 30;
private int channel = 1;
@Override
public boolean isEnabled() {
return this.on;
}
@Override
public void enable() {
this.on = true;
}
@Override
public void disable() {
this.on = false;
}
@Override
public int getVolume() {
return this.volume;
}
@Override
public void setVolume(int volume) {
this.volume = volume;
}
@Override
public int getChannel() {
return this.channel;
}
@Override
public void setChannel(int channel) {
this.channel = channel;
}
@Override
public void printStatus() {
System.out.println("--------------------");
System.out.println("| 라디오");
System.out.println("| on = " + on);
System.out.println("| volume = " + volume);
System.out.println("| channel = " + channel);
System.out.println("--------------------");
}
}
package com.example.designpattern.stuctural.bridge.devices;
public class Tv implements Device{
private boolean on = false;
private int volume = 30;
private int channel = 1;
@Override
public boolean isEnabled() {
return this.on;
}
@Override
public void enable() {
this.on = true;
}
@Override
public void disable() {
this.on = false;
}
@Override
public int getVolume() {
return this.volume;
}
@Override
public void setVolume(int volume) {
this.volume = volume;
}
@Override
public int getChannel() {
return this.channel;
}
@Override
public void setChannel(int channel) {
this.channel = channel;
}
@Override
public void printStatus() {
System.out.println("--------------------");
System.out.println("| 티비");
System.out.println("| on = " + on);
System.out.println("| volume = " + volume);
System.out.println("| channel = " + channel);
System.out.println("--------------------");
}
}
해당 제품들을 조작할 수 있는 리모컨을 만들어보자
리모콘 또한 인터페이스로 먼저 기능들을 작성해 주었다.
package com.example.designpattern.stuctural.bridge.remote;
public interface Remote {
void power();
void volumeDown();
void volumeUp();
void channelDown();
void channelUp();
}
이를 이용해 기본적인 기능들을 제공하는 기본형 리모컨을 만들어보자
package com.example.designpattern.stuctural.bridge.remote;
import com.example.designpattern.stuctural.bridge.devices.Device;
public class BasicRemote implements Remote{
protected Device device;
public BasicRemote(Device device) {
this.device = device;
}
@Override
public void power() {
System.out.println("리모콘으로 전원 조작하기");
if (device.isEnabled()) {
device.disable();
} else {
device.enable();
}
}
@Override
public void volumeDown() {
System.out.println("리모콘으로 볼륨 낮추기");
device.setVolume(device.getVolume() - 10);
}
@Override
public void volumeUp() {
System.out.println("리모콘으로 볼륨 높이기");
device.setVolume(device.getVolume() + 10);
}
@Override
public void channelDown() {
System.out.println("리모콘으로 채널 낮추기");
device.setVolume(device.getVolume() - 1);
}
@Override
public void channelUp() {
System.out.println("리모콘으로 채널 높이기");
device.setVolume(device.getVolume() + 1);
}
}
기본형 리모콘을 직접 사용해 보자!
package com.example.designpattern.stuctural.bridge;
import com.example.designpattern.stuctural.bridge.devices.Device;
import com.example.designpattern.stuctural.bridge.devices.Radio;
import com.example.designpattern.stuctural.bridge.devices.Tv;
import com.example.designpattern.stuctural.bridge.remote.AdvancedRemote;
import com.example.designpattern.stuctural.bridge.remote.BasicRemote;
public class BridgeMain {
public static void main(String[] args) {
controlTestDevice(new Tv());
System.out.println("====================");
controlTestDevice(new Radio());
}
private static void controlTestDevice(Device device) {
System.out.println("리모콘 테스트");
BasicRemote basicRemote = new BasicRemote(device);
basicRemote.power();
basicRemote.volumeUp();
device.printStatus();
}
}
결과는 이러하다
리모콘 테스트
리모콘으로 전원 조작하기
리모콘으로 볼륨 높이기
--------------------
| 티비
| on = true
| volume = 40
| channel = 1
--------------------
====================
리모콘 테스트
리모콘으로 전원 조작하기
리모콘으로 볼륨 높이기
--------------------
| 라디오
| on = true
| volume = 40
| channel = 1
--------------------
이렇게 전자제품 - 리모콘으로 분리하여 구현한 결과 각 제품마다 리모콘을 만들지 않아도 되고 전자제품이나 리모콘을 추가하게 되어도 기존의 코드에는 영향을 미치지 않게 된다.
한번 고급 리모콘을 만들어 보도록 하자
package com.example.designpattern.stuctural.bridge.remote;
import com.example.designpattern.stuctural.bridge.devices.Device;
public class AdvancedRemote extends BasicRemote{
private int preMuteVolume = 0;
public AdvancedRemote(Device device) {
super(device);
}
public void mute() {
System.out.println("리모콘으로 무음모드 전환");
if(this.device.getVolume() == 0) {
this.device.setVolume(this.preMuteVolume);
this.preMuteVolume = 0;
} else {
this.preMuteVolume = this.device.getVolume();
this.device.setVolume(0);
}
}
}
음소거 버튼이 추가되어 있는 고오급 리모콘이다. 이렇게 기능을 추가하게 되어도 기존의 코드를 건들지 않고 손쉽게 추가할 수 있다.
이를 사용하는 것 또한 간단하다.
package com.example.designpattern.stuctural.bridge;
import com.example.designpattern.stuctural.bridge.devices.Device;
import com.example.designpattern.stuctural.bridge.devices.Radio;
import com.example.designpattern.stuctural.bridge.devices.Tv;
import com.example.designpattern.stuctural.bridge.remote.AdvancedRemote;
import com.example.designpattern.stuctural.bridge.remote.BasicRemote;
public class BridgeMain {
public static void main(String[] args) {
controlTestDevice(new Tv());
System.out.println("====================");
controlTestDevice(new Radio());
}
private static void controlTestDevice(Device device) {
System.out.println("리모콘 테스트");
BasicRemote basicRemote = new BasicRemote(device);
basicRemote.power();
basicRemote.volumeUp();
device.printStatus();
System.out.println("고급 리모콘 테스트");
AdvancedRemote advancedRemote = new AdvancedRemote(device);
advancedRemote.power();
advancedRemote.mute();
device.printStatus();
advancedRemote.mute();
device.printStatus();
}
}
해당 코드의 결과 화면이다.
리모콘 테스트
리모콘으로 전원 조작하기
리모콘으로 볼륨 높이기
--------------------
| 티비
| on = true
| volume = 40
| channel = 1
--------------------
고급 리모콘 테스트
리모콘으로 전원 조작하기
리모콘으로 무음모드 전환
--------------------
| 티비
| on = false
| volume = 0
| channel = 1
--------------------
리모콘으로 무음모드 전환
--------------------
| 티비
| on = false
| volume = 40
| channel = 1
--------------------
====================
리모콘 테스트
리모콘으로 전원 조작하기
리모콘으로 볼륨 높이기
--------------------
| 라디오
| on = true
| volume = 40
| channel = 1
--------------------
고급 리모콘 테스트
리모콘으로 전원 조작하기
리모콘으로 무음모드 전환
--------------------
| 라디오
| on = false
| volume = 0
| channel = 1
--------------------
리모콘으로 무음모드 전환
--------------------
| 라디오
| on = false
| volume = 40
| channel = 1
--------------------
해당 게시물의 예제는 https://refactoring.guru/ko/design-patterns/bridge/java/example를 참고하였습니다!
반응형
'Design Pattern > 구조 패턴(Structural patterns)' 카테고리의 다른 글
Flyweight Pattern, 플라이웨이트 패턴 (0) | 2024.02.14 |
---|---|
Facade Pattern, 퍼사드 패턴 (0) | 2024.02.07 |
Decorator Pattern, 데코레이터 패턴 (1) | 2024.02.06 |
Composite Pattern, 복합체 패턴 (0) | 2024.02.05 |
Adapter Pattern, 어댑터 패턴 (0) | 2024.02.01 |