본문 바로가기

Design Pattern

[디자인 패턴_Again] Adapter and Facade Pattern.

이번에 정리 해 볼 패턴은

어댑터 패턴과 퍼사드 패턴입니다.

어댑터 패턴은 하나의 클래스를 다른 클래스로 변경해주는 역할을 합니다.

이게 무슨 말인고하니..

플러그를 생각해보겠습니다.

구멍이 두개짜리가 우리가 쓰는 것이고, 유럽에서는 동그란 구멍두개와 네모 모양의 구멍이

하나있는 플러그를 사용합니다.

여기서 중간에 어댑터를 사용하여 국산 전자 제품도 유럽에서 사용 할 수 있습니다.

(물론 정격전압이 같아야 합니다 -_-)

이런 역할을 해주는 것이 어댑터 패턴입니다.

바로 예제 코드를 보겠습니다.

import java.util.Enumeration;
import java.util.Iterator;

public class EnumAdapter implements Iterator {
 Enumeration enumeration;
 
 public EnumAdapter(Enumeration enumeration) {
  this.enumeration = enumeration;
 }
 
 public boolean hasNext() {
  return enumeration.hasMoreElements();
 }
 
 public Object next() {
  return enumeration.nextElement();
 }
 
 public void remove() {
  throw new UnsupportedOperationException();
 }
}

<코드출처 : Head First Design Pattern>

Enumeration을 Itertator로 변경시킨 예제입니다.

이런식으로 서로 비슷한 행동을 하지만 (정격 전압은 맞아야 하는 것 처럼)

서로 완전히 다른 클래스 형일때 이런식으로 어댑터 패턴을 적용하여 사용 할 수 있습니다.

위의 반대의 경우도 가능하겠지요.

이 어댑터는 사용하는 측에서는 Iterator처럼 보여야 하기 때문에 Iterator를 구현하고

있습니다. (유럽에서 플러그가 유럽식처럼 생겨야 사용이 가능한 것 처럼요..)

remove() 메서드 같은 경우는 나름대로 구현을 하면 굳이 예외를 던지지 않아도 될지 모릅니다.


그리고 퍼사드 패턴이 있습니다.

이 패턴은 복잡한 행동들을 인터페이스를 사용해서 간단하게 만들어버리는 패턴입니다.

예를 들어서 자동차 원격 시동장치를 한번 생각해 보겠습니다.

원격 시동장치의 버튼을 누르면

시동이 걸리고, 사이드미러가 열리고, 히터가 작동하고, 네비게이션이 켜집니다.

사이드미러, 히터, 네비게이션을 클래스로 추출해내겠습니다.

public class SideMirror {
 public void open() {
  System.out.println("사이드 미러가 열립니다.");
 }
 
 public void close() {
  System.out.println("사이드 미러가 닫힙니다.");
 }
}

public class Navigation {
 public void turnOn() {
  System.out.println("네비게이션이 켜집니다.");
 }
 
 public void turnOff() {
  System.out.println("네비게이션이 꺼집니다.");
 }
}

public class Heater {
 public void on() {
  System.out.println("히터가 작동합니다.");
 }
 
 public void off() {
  System.out.println("히터가 꺼집니다.");
 }
}

Car 클래스입니다.

public class Car {
 public void startup() {
  System.out.println("시동을 겁니다.");
 }
 
 public void startoff() {
  System.out.println("시동을 끕니다.");
 }
}

그럼 한번 원격시동장치를 만들어보겠습니다.

public class RemoteControl {
 private Car car;
 private Heater heater;
 private Navigation navi;
 private SideMirror sideMirror;
 
 public RemoteControl(Car car, Heater heater, Navigation navi, SideMirror sideMirror) {
  this.car = car;
  this.heater = heater;
  this.navi = navi;
  this.sideMirror = sideMirror;
 }
 
 public void remoteStartUp() {
  car.startup();
  heater.on();
  navi.turnOn();
  sideMirror.open();
 }
}

사실 사이드미러나 히터등은 Car의 구성요소로 들어가는 것이 맞을지도 모르겠습니다.
아무튼, 하나의 행동을 하기위해 해야하는 여러 복잡한 행동을 하나의 메소드로 통합해서
제공하고 있습니다.

즉, 자동차의 시동은 켠다. 라는 것을 단순화 시켜서 제공하고 있는 것입니다.

그리고 그 작업은 실제 서브시스템에 들어있는 구성요소등에게 위임을 하고 있습니다.

실제로 위 리모컨을 사용하는 클라이언트 클래스에서는

Car라던가, SideMirror 등의 클래스에 대해서 전혀 몰라도 상관 없습니다. 단지

RemoteControl의 remoteStartup() 메소드만 알고 있으면 됩니다.

자동차의 구성요소나 서브시스템이 추가되었다고 해도, 혹은 내용 자체가 변경되었다고 하더라도

인터페이스로 제공되고 있는 remoteStartup() 메소드의 시그너쳐만 달라지지 않는다면

클라이언트는 아무런 변경이 필요가 없습니다.