본문 바로가기
Programming

디자인패턴 시리즈 9. 브리지 패턴 (Bridge Pattern)

by LeeJ1Hyun 2023. 1. 24.

브리지 패턴 (Bridge Pattern)

기능을 처리하는 클래스와 구현을 담당하는 추상 클래스를 구별한다. 구현과 더불어 추상화 부분까지 변경해야 한다면 사용하면 좋을 패턴이다.

코드에 적용해보기

만능 리모컨을 만들기로 해보자. 리모컨의 기능은 똑같은 추상화 부분을 바탕으로 하지만 TV의 모델마다 많은 구현 코드를 사용해야 하므로 객체 지향적으로 설계해야 한다.

 

 

RemoteControl 인터페이스 또는 추상 클래스를 만들고 제조사마다 이를 상속 받는 구현 클래스를 만들었다. 사용자 인터페이스는 이미 추상화 했으므로 리모컨 사용자가 쓸 다양한 TV 종류에 따라 구상 클래스를 바꿔 쓸 수 있다. 하지만 사용자들이 제공하는 정보에 맞춰서 리모컨을 수정하다 보면 추상화 부분도 바꿔야 할 일이 생긴다. 구체적인 구현 부분과 추상화 부분을 모두 바꿀 수 있게 하려면 브리지 패턴을 이용하면 된다.

 

public abstract class TV {
	public abstract void on();
	public abstract void off();
	public abstract void tuneChannel(int channel);
	public abstract int getChannel(); 
}

 

TV 추상 클래스를 만들어 제조사마다 이를 상속 받도록 한다.

 

public class Samsung extends TV {
	int station = 0;
    
	public void on() {
		System.out.println("삼성 티비 켜기");
	}
	public void off() {
		System.out.println("삼성 티비 끄기");
	}
	public void tuneChannel(int channel) {
		this.station = channel;
		System.out.println("삼성 티비 방송국 설정: " + this.station);
	}
	public int getChannel() {
		return station;
	}
}

 

public class LG extends TV {
	int channel = 1;
    
	public void on() {
		System.out.println("엘지 티비 켜기");
	}
	public void off() {
		System.out.println("엘지 티비 끄기");
	}
	public void tuneChannel(int channel) {
		this.channel = channel;
		System.out.println("엘지 티비 방송국 설정: " + this.channel);
	}
	public int getChannel() {
		return channel;
	}
}

 

이제 기존의 RemoteControl 추상 클래스에 TV 추상 클래스를 인스턴스 변수로 선언해보자.

 

public abstract class RemoteControl {
	TV tv;
	TVFactory tvFactory;
    
	public RemoteControl(TVFactory tvFactory) {
		this.tvFactory = tvFactory;
	}
	public void on() {
		this.tv.on();
	}
	public void off() {
		this.tv.off();
	}
	public void setChannel(int channel) {
		tv.tuneChannel(channel);
	}
	public int getChannel() {
		return tv.getChannel();
	}
	public void setTV(String type) {
		try {
			tv = tvFactory.getTV(type);
		} catch (Exception e) {
			System.out.println(e);
		}
	}
}

 

public class GenericRemote extends RemoteControl {
	public GenericRemote(TVFactory tvFactory) {
		super(tvFactory);
	}
	public void nextChannel() {
		int channel = this.getChannel();
		this.setChannel(channel+1);
	}
	public void prevChannel() {
		int channel = this.getChannel();
		this.setChannel(channel-1);
	}
}

 

public class SpecialRemote extends RemoteControl {
	public SpecialRemote(TVFactory tvFactory) {
		super(tvFactory);
	}
	public void up() {
		int channel = this.getChannel();
		this.setChannel(channel+1);
	}
	public void down() {
		int channel = this.getChannel();
		this.setChannel(channel-1);
	}
}

 

이때 사용하는 TVFactory 인스턴스 변수는 각 제조사에 맞는 인스턴스를 생성해주기 위한 팩토리 패턴을 이용한 클래스이다.

 

public class TVFactory {
	public TV getTV(String type) throws Exception {
		if (type.equals("LG")) {
			return new LG();
		} else if (type.equals("Samsung")) {
			return new Samsung();
		} else {
			throw new Exception("존재하지 않는 제조사입니다.");
		}
	}
}

 

이제 리모컨, TV 각각 2개의 계층 구조를 만들었다.

 

 

한쪽은 리모컨을 나타내는 부분이고, 한쪽은 종류 별로 다른 TV를 나타내는 부분이다. RemoteControl 추상 클래스와 TV 추상 클래스 사이의 관계를 브리지라고 부른다. 이 다리를 이용하여 양쪽을 독립적으로 바꿀 수 있어졌다. 추상화된 부분에 들어있는 메소드 setChannel()은 구현 클래스인 TV에 있는 tuneChannel()과 같은 메소드를 통해 구현된다. 

 

추상화된 부분에 들어있는 메소드는 구현 클래스에 있는 메소드를 통해 구현이 된다. 또한 구상 서브 클래스는 구현 클래스 계층 구조의 메소드가 아닌 추상 클래스의 메소드로 구현이 된다. 즉, RemoteControl 추상 클래스를 구현한 서브 클래스인 GenericRemote 클래스에서 nextChannel() 메소드 내에서 setChannel()과 같은 추상 클래스의 메소드를 이용하여 구현된다는 말이다.

 

구현과 인터페이스를 완전히 결합하지 않으므로 구현과 추상화 부분을 분리할 수 있다. 추상화된 부분과 실제 구현 부분을 독립적으로 확장할 수 있고, 추상화 부분을 구현한 구상 클래스가 바뀌어도 클라이언트에 영향을 미치지 않는다. 인터페이스와 실제 구현할 부분을 서로 다른 방식으로 변경해야 할 때 유용하게 사용된다.

 

 

 

 

 

* 아래의 자료들을 참고하였습니다.

에릭 프리먼 · 엘리자베스 롭슨 · 케이시 시에라 · 버트 베이츠, 『헤드퍼스트 디자인패턴 개정판』, 서환수 옮김, 한빛미디어(2022), p632-633.

댓글