본문 바로가기

IT Book Summary/디자인패턴

어댑터 패턴과 퍼사드 패턴 The Adapter and Facade Patterns: Being Adaptive

어댑터 패턴 Adapter Pattern

 

일반적 어댑터의 역할 : 소켓의 인터페이스를 플러그에서 필요로하는 인터페이스로 바꿔줌.

 

객체지향 어댑터의 역할 : 어떤 인터페이스를 클라이언트에서 요구하는 형태의 인터페이스에 적응시켜주는 역할.

 


객체지향 어댑터

 

 

Adapter system

어떤 시스템에 새로 제공한 클래스 라이브러리를 사용해야하지만 인터페이스가 다를경우, 

어댑터는 클라이언트로부터 요청을 받아 새로운 업체에서 제공한 클래스에서 받아들일수 있는 행태의 요청으로

변환시켜주는 중개인 역할을 한다.

 

 

클라이언트에서 어댑터를 사용하는 방법

 

1. 클라이언트에서 타겟 인터페이스를 사용해 메소드를 호출함으로써 어댑터에 요청을 한다.

2. 어댑터에서는 어댑티 Adaptee 인터페이스를 사용해 그 요청을 어댑티에  대한 (하나 이상의) 메소드 호출로 변환한다.

3. 클라이언트에서는 호출결과를 받긴 하지만 중간에 어댑터가 껴 있는지 알지 못함.

 

어댑티 Adaptee - 어댑터를 가운데 두고 클라이언트와 정반대 위치에 있는 것.

Adapter picture

 

어댑터 패턴의 정의

 

어댑터 패턴 Adapter Pattern - 한 클래스의 인터페이스를 클라이언트에서 사용하고자 하는 다른 인터페이스로 변환.

어댑터를 이용해 인터페이스 호환성 문제 때문에 같이 쓸 수 없는 클래스들을 연결해서 쓸 수 있다.

 

Adapter Pattern Class Diagram

 

 

어댑티 Adaptee를 새로 바뀐 인터페이스로 감쌀 때는 객체구성(composition) 을 사용한다.

어댑티의 어떤 서브클래스에 대해서도 어댑처를 쓸수있다는 장점이 있다. 

 

 

객체와 클래스 어댑터

지금은 객체 어댑터에 대해서 알아보고 있지만, 다른 종류인 클래스 어댑터도 있다.

클래스 어댑터 패턴을 쓰려면 다중 상속이 필요하며, 다중상속이 가능한 언어를 사용하는 경우 쓸수있다.

 

Class Adapter Diagram

 

클래스 어댑터에서는 어댑터를 만들 때 타겟과 어댑티 모두의 서브클래스를 만들고, 

객체 어댑터에서는 구성을 통해 어댑티에 요청을 전달한다는 점이 다르다.

 

 

데코레이터 패턴과 어댑터 패턴의 차이점

데코레이터 패턴

  • 책임과 관련된 일을 맡음. 데코레이터 적용은 새로운 책임, 행동이 디자인에 추가됨을 의미.
  • 기존의 코드를 고치지 않고 클래스에 새로운 행동 추가 가능.
  • 메소드 호출이 전달되어도 어떤 데코레이터를 거져왔는지, 어떤 반환값을 받을지 알 수 없음.
  • 객체를 감싼다. 객체의 행동이나 책임을 확장.

 

어댑터 패턴

  • 여러 클래스들을 클라이언트에서 원하는 인터페이스에 맞도록 변환.
  • 클라이언트가 코드를 고치지 않고도 새로운 라이브러리를 쓸수 있음.
  • 클라이언트가 어댑터의 존재를 알지 못함.
  • 감싸고 있는 인터페이스를 변환.

퍼사드 패턴 Facade Pattern

휠씬 쓰기 쉬운 인터페이스를 제공하는 퍼사드 클래스를 구현함으로써 복잡한 시스템을 쉽게 사용할 수 있다.

하나 이상의 클래스의 복잡한 인터페이스를 Facade 로 덮어준다.

 

ex) 홈시어터 시스템

 

Facade Pattern Diagram

1. 몇가지 간단한 메소드만 들어있는 HomeTheaterFacade라는 클래스를 새로 만든다

2. 퍼사드 클래스에서는 홈시어터 구성요소들을 하나의 서브시스템으로 간주하고, watchMovie() 메소드에서는 서브시스템의 메소드들을 호출하여 필요한 작업을 처리

3. 클라이언트 코드에서는 서브시스템이 아닌 홈 씨어터 퍼사드에 있는 메소드를 호출.

이제 watchMovie() 메소드만 호출하면 알아서 모든 시스템이 준비된다.

4. 퍼사드를 쓰더라도 서브 시스템에느 여전히 직접 접근 가능하다.

서브시스템 크래스의 고급 기능이 필요하면 언제든지 마음대로 쓸 수 있다.

 

 

홈 시어터 퍼사드 구축

 

퍼사드에서 모든 구성요소에 접근가능하게 한다.

 

public 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 class HomeTheaterTestDrive {
	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");
		homeTheater.endMovie();
	}
}

 

퍼사드 패턴의 정의

- 어떤 서브시스템의 일련의 인터페이스에 대한 통합된 인터페이스를 제공한다.

퍼사드에서 고수준 인터페이스를 정의하기 때문에 서브시스템을 더 쉽게 사용할 수 있다.

 

디자인 원칙 
최소 지식 원칙 - 정말 친한 친구하고만 얘기하라.
어떤 객체든 그 객체와 상호작용하는 클래스의 개수에 주의해야 한다.

 

친구는 만들지 않으면서 다른 객체에 영향력으 행사하는 방법

- 어떤 메소드에서든지 다음 네 종류의 객체의 메소드만들 호출하면 된다.

  • 객체 자체
  • 메소드에 매개변수로 전달된 객체
  • 그 메소드에서 생성하거나 인스턴스를 만든 객체
  • 그 객체에 속하는 구성요소
// 원칙을 따르지 않는 경우.
public float getTemp() {
	Thermometer thermometer = station.getThermometer();
    return thermometer.getTemperature();
}

// 원칙을 따르는 경우
// 최소지식원칙을 적용하여 Station 클래스에 thermometer에 요청을 해주는 메소드를 추가함.
public float getTemp() {
	return station.getTemperature();
}

 

절친한 친구한테만 메소드 호출하기

 

public class Car {
	Engine engine;

	public Car() {
    	//엔진 초기화 처리.
    }
    
    public void start(Key key) {
    	Doors doors = new Doors();
        
        boolean authorized = key.turns(); // 매개변수로 전달된 객체 메소드 ok
        
        if(authorized) {
        	engine.start(); // 객체 구성요소 메소드 ok
            updateDashboardDisplay(); // 객체 내 메소드 ok
            doors.lock(); // 직접 생성하거나 인스턴스 만든 객체 메소드 ok
        }
    }
    
    public void updateDashboardDisplay() {
    	//디스플레이 갱신.
    }
}