디자인 패턴 - 4. 커맨더 패턴, 어댑터 패턴, 퍼사드 패턴, 템플릿 메소드 패턴

크리링·2024년 12월 18일
0

디자인패턴

목록 보기
4/5
post-thumbnail

커맨드 패턴

깃허브 코드
일련의 행동을 특정 리시버와 연결함으로써 요청을 캡슐화한 것
명령으로 객체를 매개변수화할 수 있음

7개의 슬롯을 각각 ON 버튼과 OFF 버튼으로 제어 가능한 리모콘을 커맨더 패턴으로 만들어보려한다.

음식 주문 과정
1. 고객이 원하는 것 주문
2. 주문 내용으로 주문서가 구성
3. 종업원은 주문을 받음
4. 종업원이 주방으로 주문을 전달
5. 주방장은 지시에 맞는 음식을 준비

구현

Command

public interface Command {
	public void execute();
}

조명 on 커맨드

public class LightOnCommand implements Command {
	Light light;

	public LightOnCommand(Light light) {
		this.light = light;
	}

	@Override
	public void execute() {
		light.on();
	}
}

조명 off 커맨드

public class LightOffCommand implements Command {
	Light light;

	public LightOffCommand(Light light) {
		this.light = light;
	}

	@Override
	public void execute() {
		light.off();
	}
}

리모콘

public class RemoteControl {
	Command[] onCommands;
	Command[] offCommands;

	public RemoteControl() {
		onCommands = new Command[7];
		offCommands = new Command[7];
	}

	public void setCommand(int slot, Command onCommand, Command offCommand) {
		onCommands[slot] = onCommand;
		offCommands[slot] = offCommand;
	}

	public void onButtonWasPushed(int slot) {
		onCommands[slot].execute();
	}

	public void offButtonWasPushed(int slot) {
		offCommands[slot].execute();
	}
}

테스트 코드

public class Main {
	public static void main(String[] args) {
		RemoteControl remoteControl = new RemoteControl();
		Light light = new Light();
		LightOnCommand onLightOnCommand = new LightOnCommand(light);
		LightOffCommand offLightOnCommand = new LightOffCommand(light);
		GarageDoor garageDoor = new GarageDoor();
		GarageDoorOpenCommand onGarageDoor = new GarageDoorOpenCommand(garageDoor);
		GarageDoorCloseCommand offGarageDoor = new GarageDoorCloseCommand(garageDoor);

		remoteControl.setCommand(1, onLightOnCommand, offLightOnCommand);
		remoteControl.setCommand(4, onGarageDoor, offGarageDoor);

		remoteControl.onButtonWasPushed(1);
		remoteControl.onButtonWasPushed(4);
		remoteControl.offButtonWasPushed(1);
		remoteControl.offButtonWasPushed(4);
	}
}

결과






어댑터 패턴

특정 클래스 인터페이스를 클라이언트에서 요구하는 다른 인터페이스로 변환
인터페이스가 호환되지 않아 같이 쓸 수 없었던 클래스 사용 가능

클래스 어댑터

객체 어댑터

이전의 전략 패턴에서 만들었던 Duck과 비슷한 Turkey가 생겼습니다.


구현

Duck

public interface Duck {
	public void quack();
	public void fly();
}

MallardDuck

public class MallardDuck implements Duck {
	@Override
	public void quack() {
		System.out.println("꽥");
	}

	@Override
	public void fly() {
		System.out.println("날 수 있어요");
	}
}

Turkey

public interface Turkey {
	public void gobble();
	public void fly();
}

WildTurkey

public class WildTurkey implements Turkey {
	@Override
	public void gobble() {
		System.out.println("골골");
	}

	@Override
	public void fly() {
		System.out.println("짧은 거리를 날 수 있습니다.");
	}
}

TurkeyAdapter

public class TurkeyAdapter implements Duck {
	Turkey turkey;

	public TurkeyAdapter(Turkey turkey) {
		this.turkey = turkey;
	}

	@Override
	public void quack() {
		turkey.gobble();
	}

	@Override
	public void fly() {
		for (int i = 0; i < 5; i++) {
			turkey.fly();
		}
	}
}

테스트 코드

public class Main {
	public static void main(String[] args) {
		Duck mallardDuck = new MallardDuck();

		Turkey turkey = new WildTurkey();
		Duck turkeyAdapter = new TurkeyAdapter(turkey);

		System.out.println("------칠면조------");
		turkeyAdapter.quack();
		turkeyAdapter.fly();
		System.out.println();
		System.out.println("------오리------");
		mallardDuck.quack();
		mallardDuck.fly();
	}
}

결과






퍼사드 패턴

서브시스템에 있는 일련의 인터페이스를 통합 인터페이스로 묶어 줍니다. 고수준 인터페이스도 정의하므로 서브시스템을 더 편리하게 사용할 수 있게 만듬

영화 보는데에 몇가지 일이 필요합니다.
1. 팝콘 기계를 킨다.
2. 팝콘을 튀긴다.
3. 조명을 어둡게 조절한다.
4. 스크린을 내린다.
5. 프로젝터를 켠다.
6. 프로젝터 입력을 스트리밍 플레이어로 설정한다.
7. 프로젝터를 와이드 스크린 모드로 전환한다.
8. 앰프를 서라운드 음향 모드로 전환한다.
9. 앰프 볼륨을 중간으로 설정한다.
10. 스트리밍 플레이어를 켠다.
11. 영화를 재생한다.


구현

이전 코드

popper.on();
popper.pop();

lights.dim(10)l

screen.down();

projector.on();
projector.setInput(player);
projector.wideScreenMode();

amp.on();
amp.setDvd(player);
amp.setSurroundSound();
amp.setVolume(5);

player.on();
player.play(movie);

클래스 6개 필요

홈시어터 퍼사드 생성

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

	public void watchMovie(){
        ...
    }
	
	public void endMovie(){
        ...
    }
}
  • HomeTheaterFacade는 클라이언트 대신 모든 서브시스템 구성 요소를 관리
  • 홈시어터 구성 요소를 업그레이드해도 클라이언트는 아무 영향을 받지 않음




최소 지식 원칙

객체 사이의 상호작용은 될 수 있으면 아주 가까운 사이에서만 허용하는 편이 좋음

원칙을 따르지 않는 경우

public float getTemp(){
	Thermometer thermometer = station.getThermmometer();
	return thermometer.getTemperature();
}

원칙을 따른 경우

public float getTemp(){Thermometer thermometer = station.getThermmometer();
	return station.getTemperature();
}






템플릿 메소드 패턴

알고리즘의 일부 단계를 서브클래스에서 구현
알고리즘 구조는 그대로 유지, 특정 단계의 서브클래스 재정의

커피와 차를 묶어서 관리해보려 한다.


구현

BEFORE

커피

public class Coffee {

	void prepareRecipe() {
		boilWater();
		brewCoffeeGrinds();     // 차와 다름
		pourInCup();
		addSugarAndMilk();      // 차와 다름
	}

	public void boilWater() {
		System.out.println("물 끓이는 중");
	}

	public void brewCoffeeGrinds() {
		System.out.println("필터로 커피를 우려내는 중");
	}

	public void pourInCup() {
		System.out.println("컵에 따르는중");
	}

	public void addSugarAndMilk() {
		System.out.println("설탕과 우유를 추가");
	}
}

public class Tea {
	
	void prepareRecipe() {
		boilWater();
		steepTeaBag();      // 커피와 다름
		pourInCup();
		addLemon();         // 커피와 다름
	}

	public void boilWater() {
		System.out.println("물 끓이는 중");
	}

	public void steepTeaBag() {
		System.out.println("찻잎을 우러내는 중");
	}

	public void addLemon() {
		System.out.println("레몬 추가");
	}

	public void pourInCup() {
		System.out.println("컵에 따르는중");
	}
}



AFTER

템플릿

public abstract class CaffeineBeverage {

	final void prepareRecipe() {
		boilWater();
		brew();
		pourInCup();
		addCondiments();
	}

	abstract void brew();

	abstract void addCondiments();

	public void boilWater() {
		System.out.println("물 끓이는 중");
	}

	public void pourInCup() {
		System.out.println("컵에 따르는중");
	}
}

커피

public class Coffee extends CaffeineBeverage {

	void brew() {
		System.out.println("필터로 커피 우려내는 중");
	}

	void addCondiments() {
		System.out.println("설탕과 우유 추가");
	}
}

public class Tea extends CaffeineBeverage {

	public void brew() {
		System.out.println("찻잎을 우러내는 중");
	}

	public void addCondiments() {
		System.out.println("레몬 추가");
	}
}




할리우드 원칙

의존성 부패 방지

  • 고수준 구성 요소가 언제, 어떻게 쓰일지 결정
  • 저수준 구성 요소도 컴퓨테이션에 참여할 수 있음
  • 저수준 구성 요소는 절대 고수전 구성 요소 호출 불가

0개의 댓글