헤드 퍼스트 디자인 패턴 | 에릭 프리먼 | 한빛미디어- 교보ebook

14가지 GoF 필살 패턴!, 경력과 세대를 넘어 오랫동안 객체지향 개발자의 성장을 도와준 디자인 패턴 교과서의 화려한 귀환! 》 2005년부터 디자인 패턴 도서 분야 부동의 1위 》 디자인 패턴의 고

ebook-product.kyobobook.co.kr

어댑터 생각해보기

일상 생활에서 어댑터란 무언가를 호환되도록 도와준다.

한국의 220V 콘센트를 일본의 110V 콘센트와 호환되게 해주는 어댑터가 그 예다.

 

코드에서도 어댑터 패턴은 이 역할을 한다.

기존 시스템과 업체에서 제공한 코드가 있다고 치자. 이 사이를 어댑터 코드로 연결지어주면, 두 코드는 코드 변경이 없다.

어댑터 사용 방법 알아보기

package designpattern.headfirst.chapter7;

public class AdapterTest {
	//기존 코드
	static interface Duck{
		void quack();
		void fly();
	}
	static class MallardDuck implements Duck{
		@Override
		public void quack() {
			System.out.println("꽥!");
		}
		@Override
		public void fly() {
			System.out.println("날고 있어요!!");
		}
	}
	
	//새로 제공된 코드
	static interface Turkey{
		void gobble();
		void fly();
	}
	static class WildTurkey implements Turkey{
		@Override
		public void gobble() {
			System.out.println("골골");
		}
		@Override
		public void fly() {
			System.out.println("짧은 거리를 날고 있어요!");
		}
	}
	
	//어댑터
	static 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();
			}
		}
	}
	static class DuckAdapter implements Turkey{
		Duck duck;
		public DuckAdapter(Duck duck) {
			this.duck = duck;
		}
		public void gobble() {
			duck.quack();
		}
		public void fly() {
			duck.fly();
		}
	}
	
	public static void main(String[] args) {
		Duck duck = new MallardDuck();
		Turkey turkey = new WildTurkey();
		Duck turkeyAdapter = new TurkeyAdapter(turkey);
		Turkey duckAdapter = new DuckAdapter(duck);
		
		System.out.println("칠면조---");
		turkey.gobble();
		turkey.fly();
		System.out.println("\n오리---");
		testDuck(duck);
		System.out.println("\n칠면조 어댑터---");
		testDuck(turkeyAdapter);
		System.out.println("\n오리 어댑터---");
		testTurkey(duckAdapter);
		testTurkey(turkey);
		
		
	}
	static void testTurkey(Turkey turkey){
		turkey.gobble();
		turkey.fly();
	}
	static void testDuck(Duck duck) {
		duck.quack();
		duck.fly();
	}
}
칠면조---
골골
짧은 거리를 날고 있어요!

오리---
꽥!
날고 있어요!!

칠면조 어댑터---
골골
짧은 거리를 날고 있어요!
짧은 거리를 날고 있어요!
짧은 거리를 날고 있어요!
짧은 거리를 날고 있어요!
짧은 거리를 날고 있어요!

오리 어댑터---
꽥!
날고 있어요!!
골골
짧은 거리를 날고 있어요!

어댑터 패턴 알아보기

public class 어댑터패턴 {
	static interface Adaptee {
		void 적응이필요해();
	}
	static class AdapteeClass implements Adaptee{
		public void 적응이필요해() {
			System.out.println("적응이 필요한 코드 뭉치");
		}
	}
	//타겟 인터페이스
	static interface Origin{
		void 원본코드();
	}
	static class OriginClass implements Origin{
		public void 원본코드() {
			System.out.println("원본 코드");
		}
	}
	//핵심
	static class OriginAdapter implements Origin{
		Adaptee adaptee; // 어댑티 객체 인스턴스가 있다.
		
		public OriginAdapter(Adaptee adaptee) {
			this.adaptee = adaptee;
		}
		@Override
		public void 원본코드() {
			adaptee.적응이필요해();
		}
	}
	public static void main(String[] args) {
		Origin origin = new OriginClass();
		Adaptee adaptee = new AdapteeClass();
		Origin originAdapter = new OriginAdapter(adaptee);
		action(origin);
		action(originAdapter);
	}
	//기존에 클라이언트가 사용하던 원본 코드는 변경이 없다.
	static void action(Origin origin) {
		origin.원본코드();
	}
}
원본 코드
적응이 필요한 코드 뭉치

클라이언트는 타겟 인터페이스로 다루니 코드 변경이 없다.

어댑터는 반드시 적응 시켜야할 어댑티 메서드와 일대일 매칭되리란 보장이 없다.

클라이언트는 중간에 어댑터가 있다는 사실도 모른다.

 

어댑터 패턴의 정의

특정 클래스 인터페이스를 클라이언트에서 요구하는 다른 인터페이스로 변환한다.

호환되지 않는 인터페이스를 사용하는 클라이언트를 그대로 활용할 수 있다. 

객체 어댑터와 클래스 어댑터

자바의 경우 단일 상속 언어로 클래스 어댑터를 쓰지 않는다.

클래스 어댑터는 다중 상속이 가능한 언어에서 쓰일 수 있다.

 

객체 어댑터는 구성관계를 사용하므로 어떤 언어도 상관없다. 단순히 호출을 전달만 하면 된다.

 

실전 사례

Enumberation , Iterator

public class EnumIterAdapter {
	static class EnumerationIterator implements Iterator<Object>{
		Enumeration<?> enumeration;
		public EnumerationIterator(Enumeration<?> enumeration) {
			this.enumeration = enumeration;
		}
		@Override
		public boolean hasNext() {
			return enumeration.hasMoreElements();
		}
		@Override
		public Object next() {
			return enumeration.nextElement();
		}
		/*Enumeration는 원래 없던 기능이라 구현이 불가능하다. 
		 * 이처럼 어댑터는 반드시 일대일 대응되리란 보장이 없다. */
		public void remove() {
			throw new UnsupportedOperationException();
		}
	}
	static class IteratorEnumeration implements Enumeration<Object>{
		Iterator<?> iterator;
		public IteratorEnumeration(Iterator<?> iterator) {
			this.iterator = iterator;
		}
		@Override
		public boolean hasMoreElements() {
			return iterator.hasNext();
		}
		@Override
		public Object nextElement() {
			return iterator.next();
		}
	}
}

퍼사드 패턴

기존 복잡한 시스템을 단순한 인터페이스로 변경하는 패턴

쓰기 쉬운 인터페이스를 제공하는 퍼사드 클래스를 구현하는 핵심

 

감싸고 있는 클래스 수가 퍼사드, 어댑터를 구분짓는 요소가 아니다.

어댑터가 여러 클래스를 적응시킬 수도 있고, 하나의 복잡한 인터페이스를 하나의 퍼사드로 처리할 수도 있다.

package designpattern.headfirst.chapter7;

public class FacadeTest {
	static class PopcornPopper {
		String description;

		public PopcornPopper(String description) {
			this.description = description;
		}
		public void on() {
			System.out.println(description + " on");
		}
		public void off() {
			System.out.println(description + " off");
		}
		public void pop() {
			System.out.println(description + " popping popcorn!");
		}
		public String toString() {
			return description;
		}
	}

	static class Projector {
		String description;
		StreamingPlayer player;

		public Projector(String description, StreamingPlayer player) {
			this.description = description;
			this.player = player;
		}
		public void on() {
			System.out.println(description + " on");
		}
		public void off() {
			System.out.println(description + " off");
		}
		public void wideScreenMode() {
			System.out.println(description + " in widescreen mode (16x9 aspect ratio)");
		}
		public void tvMode() {
			System.out.println(description + " in tv mode (4x3 aspect ratio)");
		}
		public String toString() {
			return description;
		}
	}

	static class StreamingPlayer {
		String description;
		int currentChapter;
		Amplifier amplifier;
		String movie;

		public StreamingPlayer(String description, Amplifier amplifier) {
			this.description = description;
			this.amplifier = amplifier;
		}

		public void on() {
			System.out.println(description + " on");
		}
		public void off() {
			System.out.println(description + " off");
		}
		public void play(String movie) {
			this.movie = movie;
			currentChapter = 0;
			System.out.println(description + " playing \"" + movie + "\"");
		}
		public void play(int chapter) {
			if (movie == null) {
				System.out.println(description + " can't play chapter " + chapter + " no movie selected");
			} else {
				currentChapter = chapter;
				System.out.println(description + " playing chapter " + currentChapter + " of \"" + movie + "\"");
			}
		}
		public void stop() {
			currentChapter = 0;
			System.out.println(description + " stopped \"" + movie + "\"");
		}
		public void pause() {
			System.out.println(description + " paused \"" + movie + "\"");
		}
		public void setTwoChannelAudio() {
			System.out.println(description + " set two channel audio");
		}
		public void setSurroundAudio() {
			System.out.println(description + " set surround audio");
		}
		public String toString() {
			return description;
		}
	}

	static class CdPlayer {
		String description;
		int currentTrack;
		Amplifier amplifier;
		String title;

		public CdPlayer(String description, Amplifier amplifier) {
			this.description = description;
			this.amplifier = amplifier;
		}
		public void on() {
			System.out.println(description + " on");
		}
		public void off() {
			System.out.println(description + " off");
		}
		public void eject() {
			title = null;
			System.out.println(description + " eject");
		}
		public void play(String title) {
			this.title = title;
			currentTrack = 0;
			System.out.println(description + " playing \"" + title + "\"");
		}
		public void play(int track) {
			if (title == null) {
				System.out.println(description + " can't play track " + currentTrack + ", no cd inserted");
			} else {
				currentTrack = track;
				System.out.println(description + " playing track " + currentTrack);
			}
		}
		public void stop() {
			currentTrack = 0;
			System.out.println(description + " stopped");
		}
		public void pause() {
			System.out.println(description + " paused \"" + title + "\"");
		}
		public String toString() {
			return description;
		}
	}

	static class TheaterLights {
		String description;

		public TheaterLights(String description) {
			this.description = description;
		}
		public void on() {
			System.out.println(description + " on");
		}
		public void off() {
			System.out.println(description + " off");
		}
		public void dim(int level) {
			System.out.println(description + " dimming to " + level + "%");
		}
		public String toString() {
			return description;
		}
	}

	static class Tuner {
		String description;
		Amplifier amplifier;
		double frequency;

		public Tuner(String description, Amplifier amplifier) {
			this.description = description;
		}
		public void on() {
			System.out.println(description + " on");
		}
		public void off() {
			System.out.println(description + " off");
		}
		public void setFrequency(double frequency) {
			System.out.println(description + " setting frequency to " + frequency);
			this.frequency = frequency;
		}
		public void setAm() {
			System.out.println(description + " setting AM mode");
		}
		public void setFm() {
			System.out.println(description + " setting FM mode");
		}
		public String toString() {
			return description;
		}
	}

	static class Screen {
		String description;

		public Screen(String description) {
			this.description = description;
		}
		public void up() {
			System.out.println(description + " going up");
		}
		public void down() {
			System.out.println(description + " going down");
		}
		public String toString() {
			return description;
		}
	}

	static class Amplifier {
		String description;
		Tuner tuner;
		StreamingPlayer player;

		public Amplifier(String description) {
			this.description = description;
		}
		public void on() {
			System.out.println(description + " on");
		}
		public void off() {
			System.out.println(description + " off");
		}
		public void setStereoSound() {
			System.out.println(description + " stereo mode on");
		}
		public void setSurroundSound() {
			System.out.println(description + " surround sound on (5 speakers, 1 subwoofer)");
		}
		public void setVolume(int level) {
			System.out.println(description + " setting volume to " + level);
		}
		public void setTuner(Tuner tuner) {
			System.out.println(description + " setting tuner to " + tuner);
			this.tuner = tuner;
		}
		public void setStreamingPlayer(StreamingPlayer player) {
			System.out.println(description + " setting Streaming player to " + player);
			this.player = player;
		}
		public String toString() {
			return description;
		}
	}
	//퍼사드
	static class HomeTheaterFacade {
		Amplifier amp;
		Tuner tuner;
		StreamingPlayer player;
		CdPlayer cd;
		Projector projector;
		TheaterLights lights;
		Screen screen;
		PopcornPopper popper;

		public HomeTheaterFacade(Amplifier amp, Tuner tuner, StreamingPlayer player, Projector projector, Screen screen,
				TheaterLights lights, PopcornPopper popper) {
			this.amp = amp;
			this.tuner = tuner;
			this.player = player;
			this.projector = projector;
			this.screen = screen;
			this.lights = lights;
			this.popper = popper;
		}

		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.setStreamingPlayer(player);
			amp.setSurroundSound();
			amp.setVolume(5);
			player.on();
			player.play(movie);
		}

		public void endMovie() {
			System.out.println("Shutting movie theater down...");
			popper.off();
			lights.on();
			screen.up();
			projector.off();
			amp.off();
			player.stop();
			player.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 static void main(String[] args) {
		Amplifier amp = new Amplifier("Amplifier");
		Tuner tuner = new Tuner("AM/FM Tuner", amp);
		StreamingPlayer player = new StreamingPlayer("Streaming Player", amp);
		CdPlayer cd = new CdPlayer("CD Player", amp);
		Projector projector = new Projector("Projector", player);
		TheaterLights lights = new TheaterLights("Theater Ceiling Lights");
		Screen screen = new Screen("Theater Screen");
		PopcornPopper popper = new PopcornPopper("Popcorn Popper");
 
		HomeTheaterFacade homeTheater = 
				new HomeTheaterFacade(amp, tuner, player, 
						projector, screen, lights, popper);
 
		homeTheater.watchMovie("Raiders of the Lost Ark");
		System.out.println("=========================");
		homeTheater.endMovie();
	}
}

퍼사드 패턴의 정의

서브시스템에 있는 일련의 인터페이스를 통합 인터페이스로 묶어준다.

 

최소 지식 원칙(==디메테르 원칙)

객체 사이의 상호 작용은 될 수 있으면 긴밀한 사이에서만 허용

어떤 객체든 그 객체와 상호작용하는 클래스의 수와 상호작용 방식에 주의를 기울여야 한다.

시스템의 한 부분을 변경했을 때 다른 부분까지 줄줄이 고쳐야하는 상황이라면, 최소 지식 원칙이 잘 지켜지지 않고 있는 것

public class 최소지식 {
	static class A1{
		A2 a2 = new A2();
		void callA1() {System.out.println("A1 야호!");}
		void callA2() {a2.callA2();}
		void callA3() {a2.a3.callA3();}
	}
	static class A2{
		A3 a3 = new A3();
		void callA2() {System.out.println("A2 야호!");}
	}
	static class A3{
		void callA3() {System.out.println("A3 야호!");}
	}
	public static void main(String[] args) {
		A1 a1 = new A1();
		a1.callA1();
		a1.callA2();
		a1.a2.callA2();
		a1.callA3();
		a1.a2.a3.callA3();
	}
	
}

핵심정리

인터페이스가 맞지 않으면 어댑터를 쓰면 된다.

큰 인터페이스와 여러 인터페이스를 단순하게 바꾸거나 통합해야 하면 퍼사드를 쓰면 된다.

 

+ Recent posts