(본 강의 노트는 한빛 미디어의 [Head First Design Patterns]책을 기반으로 하고 있습니다)
07 어댑터 패턴과 퍼사드 패턴
Adapter Pattern & Facade Pattern
07-01. Adapter Pattern
목적
클래스의 인터페이스를 클라이언트가 원하는 형태의 또다른 인터페이스로 변환. 어댑터는 호환되지 않는 인터페이스 때문에 동작하지 않는 클래스들을 함께 동작할 수 있도록 만들어줌 (aka. Wrapper in Java)
어댑터 패턴 정의
디자인 패턴 요소
요소 |
설명 |
이름 |
어댑터(Adapter) |
문제 |
사용 객체의 API가 서로 다름 |
해결방안 |
함수를 변환하는 객체를 중간에 넣음 |
결과 |
변경 최소화 |
객체를 감싸는 역할을 함(Object Wrapping)
- 서로 호환되지 않는 두 개의 인터페이스를 연결하는 작업
- 서로 다른 인터페이스를 동일하게 변환
예:) 전기 플러그, 나라마다 서로 다른 플러그의 경우 어댑터를 사용해서 변환시킬 수 있음
객체지향 어댑터
- 일상 생활에서 쓰이는 어댑터하고 똑같은 역할을 함
- 어떤 인터페이스를 클라이언트에서 요구하는 형태의 인터페이스에 적응시켜주는 역할을 함
가정1.
기존 소프트웨어 시스템에서 새로운 업체에서 제공한 클래스 라이브러리를 사용해야 한다고 가정
문제 상황.
새로 채택한 업체에서 사용하는 인터페이스가 기존 업체에서 사용하던 인터페이스와 다름
해결 방안1.
기존 코드를 바꿀 수 없다면, 업체에서 사용하는 인터페이스를 기존에 사용하던 인터페이스에 적용시켜주는 클래스를 만들어 사용
이 경우 어댑터는 클라이언트로부터 요청을 받아서 업체에서 제공하는 클래스에서 수용할 수 있는 형태로 변환시켜주는 중개인 역할을 함
사례1 : 오리 탈을 쓴 칠면조
Duck interface & MallardDuck class(01 디자인 패턴 소개 코드)
public interface Duck {
public void quack();
public void fly();
}
public class MallardDuck implements Duck {
public void quack() {
System.out.println("Quack");
}
public void fly() {
System.out.println("I'm flying");
}
}
Turkey interface & WildTurkey class
public interface Turkey {
public void gobble();
public void fly();
}
public class WildTurkey implements Turkey {
public void gobble() {
System.out.println("Gobble gobble");
}
public void fly() {
System.out.println("I'm flying a short distance");
}
}
상황1. Duck 객체가 모자라서 터키 객체를 대신 사용해야 하는 상황
어댑터 구현
public class TurkeyAdapter implements Duck {
Turkey turkey;
public TurkeyAdapter(Turkey turkey) {
this.turkey = turkey;
}
public void quack() {
turkey.gobble();
}
public void fly() {
for (int i = 0; i < 5; i++) {
turkey.fly();
}
}
}
public class DuckTestDrive {
public static void main(String[] args) {
MallardDuck duck = new MallardDuck();
WildTurkey turkey = new WildTurkey();
Duck turkeyAdapter = new TurkeyAdapter(turkey);
System.out.println("The Turkey says…");
turkey.gobble();
turkey.fly();
System.out.println("\nThe Duck says…");
testDuck(duck);
System.out.println("\nThe TurkeyAdapter says…");
testDuck(turkeyAdapter);
}
static void testDuck(Duck duck) {
duck.quack();
duck.fly();
}
}
사례2 : Enumeration을 Iterator에 적응시키기
Enumeration 예제 코드
import java.util.*;
public class Enumeration1 {
public static void printEnumeration(Enumeration e) {
while (e.hasMoreElements()) {
System.out.println("" + e.nextElement());
}
}
public static void main(String[] args) {
Vector v = new Vector();
for (int i = 0; i < 10; i++) {
v.add(i);
}
Enumeration e = v.elements();
Enumeration1.printEnumeration(e);
}
}
Iterator 예제 코드
import java.util.*;
public class Iterator1 {
public static void printIterator(Iterator it) {
while (it.hasNext()) {
System.out.println("" + it.next());
}
}
public static void main(String[] args) {
Vector v = new Vector();
for (int i = 0; i < 10; i++) {
v.add(i);
}
Iterator it = v.iterator();
Iterator1.printIterator(it);
}
}
Enumeration을 Iterator에 적응시킬 경우 생기는 문제점
코드
public class EnumerationIterator implements Iterator {
Enumeration enumeration;
public EnumerationIterator(Enumeration enmt) {
this.enumeration = enmt;
}
public boolean hasNext() {
return enumeration.hasMoreElements();
}
public Object next() {
return enumeration.nextElement();
}
public void remove() {
throw new UnsupportedOperationException(); // 예외 처리
}
}
public class Iterator2 {
public static void printIterator(Iterator it) {
while (it.hasNext()) {
System.out.println("" + it.next());
}
}
public static void main(String[] args) {
Vector v = new Vector();
for (int i = 0; i < 10; i++) {
v.add(i);
}
Enumeration e = v.elements();
EnumerationIterator it = new EnumerationIterator(e);
Iterator2.printIterator(it);
}
}
어댑터 사용 예
자바에서 배열을 고정 크기의 리스트로 변환
- Arrays.asList() 함수 사용
- 변환된 리스트는 ArraysAdapter로서 Arrays의 특징을 가지게 됨
1. 리스트로 변환하더라도 고정 크기이므로 add(), remove() 사용 불가
2. 리스트의 set() 함수를 이용해서 요소 내용 변경 가능(단, 원래 배열의 내용도 변경됨)
코드
import java.util.Arrays;
import java.util.List;
public class ArraysAdapter {
public static void main(String[] args) {
String[] cities = { "Seoul", "Incheon", "Busan", "Sejong" };
List<String> cityList = Arrays.asList(cities);
System.out.printf("cities.length = %d\n", cities.length);
System.out.printf("cityList.size = %d\n", cityList.size());
cityList.set(0, "Suwon");
System.out.println("\nPrint out cities");
for (String s : cities) {
System.out.println(s);
}
System.out.println("\nPrint out cityList");
for (String s : cityList) {
System.out.println(s);
}
}
}
07-02. Facade Pattern
목적
서브시스템에 있는 여러 개의 인터페이스를 통합하는 한 개의 인터페이스를 제공.
퍼사드는 서브 시스템을 쉽게 사용할 수 있도록 해주는 고급 수준의 인터페이스를 정의
요소 |
설명 |
이름 |
퍼사드(Facade) |
문제 |
서브시스템이 너무 많고 사용하기가 복잡함 |
해결방안 |
단순한 인터페이스를 제공하는 객체를 중간에 넣음 |
결과 |
최소 지식 원칙에 입각해 의존성 최소화 |
퍼사드 패턴의 정의
홈 씨어터
홈 씨어터 구축 가정
- 디비디 플레이어, 프로젝터, 자동 스크린, 서라운드 음향 시스템 및 팝콘 기계를 갖춘 시스템을 구성
영화 보기(복잡한 방법)
- 팝콘 기계를 켜고 튀기기 시작
- 전등을 어둡게 조절, 스크린을 내림
- 프로젝트를 켜고 프로젝터로 디비디 신호 입력
- 프로젝터를 와이드 스크린 모드로 전환
- 엠프를 켜고 디비디로 전환
- 엠프를 서라운드 음향 모드로 전환
- 엠프 볼륨을 중간(5)로 설정
- 디비디 플레이어를 켜고 재생 시작
클래스 다이어그램
영화 보기를 코드로 구현
popper.on();
popper.pop();
lights.dim(10);
screen.down();
projector.on();
projector.setInput(dvd);
projector.wideScreenMode();
amp.on();
amp.setDvd(dvd);
amp.setSurroundSound();
amp.setVolume(5);
dvd.on();
dvd.play(movie);
퍼사드 패턴을 이용한 홈 씨어터 구현
- 퍼사드 패턴을 이용해서 쓰기 쉬운 인터페이스를 제공
- 복잡한 시스템을 직접 건드리고 싶다면 기존 인터페이스 사용 가능
- 퍼사드 클래스 제공
- 홈 씨어터 시스템 용 퍼사드 클래스 제공
- 퍼사드 클래스에서는 홈 씨어터 구성요소들을 하나의 서브 시스템으로 간주하고, watchMovie() 메소드에서는 서브시스템의 메소드들을 호출하여 필요한 작업을 처리
- 클라이언트는 서브 시스템이 아닌 홈 씨어터 퍼사드에 있는 메소드를 호출
- 퍼사드를 쓰더라도 서브시스템에는 여전히 직접 접근 가능
코드
public class HomeTheaterFacade {
private Amplifier amp;
private Tuner tuner;
private DvdPlayer dvd;
private CdPlayer cd;
private Projector projector;
private TheaterLights lights;
private Screen screen;
private PopcornPopper popper;
public HomeTheaterFacade(Amplifier a, Tuner t,
DvdPlayer d, CdPlayer c, Projector p,
Screen s, TheaterLights l, PopcornPopper pp) {
this.amp = a; this.tuner = t; this.dvd = d;
this.cd = c; this.projector = p; this.lights = l;
this.screen = s; this.popper = pp;
}
public void watchMovie(String movie) {
System.out.println("Get ready to watch a movie...");
popper.on();
popper.pop();
lights.dim(10);
screen.down();
projector.on();
projector.wideScreenMode();
amp.on();
amp.setDvd(dvd);
amp.setVolume(5);
dvd.on();
dvd.play(movie);
}
public void endMovie() {
System.out.println("Shutting movie theater down...");
projector.off();
amp.off();
dvd.stop();
dvd.eject();
dvd.off();
}
public void listenToRadio(double frequency) {
System.out.println("Tuning in the airwaves...");
tuner.on();
tuner.setFrequency(frequency);
amp.on();
amp.setVolume(5);
amp.setTuner(tuner);
}
public void endRadio() {
System.out.println("Shutting down the tuner...");
tuner.off();
amp.off();
}
}
public void endMovie() {
System.out.println("Shutting movie theater down..");
popper.off();
lights.on();
screen.up();
projector.off();
amp.off();
dvd.stop();
dvd.eject();
dvd.off();
}
… // 기타 메소드
}
퍼사드 패턴 사용
public class HomeTheaterTestDrive {
public static void main(String[] args) {
// 컴포넌트 객체 생성 코드
HomeTheaterFacade homeTheater
= new HomeTheaterFacade(amp, tuner, dvd, cd,
projector, screen, lights, popper);
homeTheater.watchMovie("Raiders of the Lost Ark");
homeTheater.endMovie();
}
}
'Computer Science > 디자인패턴' 카테고리의 다른 글
[Head First Design Patterns] 06 커맨드 패턴 (0) | 2020.11.03 |
---|---|
[Head First Design Patterns] 05 싱글톤 패턴 (0) | 2020.10.20 |
[Head First Design Patterns] 04 팩토리 패턴 (0) | 2020.10.20 |
[Head First Design Patterns] 01 디자인 패턴 소개 (0) | 2020.10.18 |
Introduction Advanced OOP (0) | 2020.10.18 |