따봉도치야 고마워

Head First Design Patterns : (7)어댑터 패턴과 퍼사드 패턴 본문

프로그래밍/공부

Head First Design Patterns : (7)어댑터 패턴과 퍼사드 패턴

따봉도치 2020. 9. 21. 16:57

어댑터 패턴

  • 한 클래스의 인터페이스를 다른 클라이너트에서 사용하고자 하는 다른 인터페이스로 변환한다.
  • 어탭터를 이용하면 인터페이스 호환성 문제 때문에 같이 쓸 수 없는 클래스들을 연결해서 사용 가능.
  1. 클라이언트는 타겟 인터페이스에 맞게 구현이 되어있고, 그걸 통해 메소드 호출
  2. 어댑터는 타겟 인터페이스를 구현하며 *어댑티 객체를 가지고 있음
  3. 어댑터는 어댑티 객체를 사용해 클라이언트의 요청을 응답
  4. 클라이언트는 결과를 받지만 중간에 어댑터가 껴있는지 알지 못함

*어댑티 : 어댑터를 가운데 두고 클라이언트(타겟 인터페이스)와 반대의 위치에 있는 것  ex) 아래 예제에서 Turkey

 

 

객체지향 어댑터

- 말 그대로, 어떤 인터페이스를 클라이언트가 요구하는 형태의 인터페이스에 적응 시켜주는 역할

 

ex) 기존 시스템에 새로운 클래스 라이브러리를 사용해야하는데, 둘의 인터페이스가 다른 경우

- 기존 코드들을 수정하지 않고 어댑터를 이용해 사용

 

예제

- Duck, Turkey 인터페이스를 생성

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

public interface Turkey{
    public void gobble(); //골골거리면서 욺
    public void fly(); //날 수 있지만 멀리 날아가진 못함
}

 

- Duck객체가 모잘라 Turkey 객체를 대신 사용해야하는 상황

- 바로 사용할 수 없으니 : TurkeyAdapter 생성

//변환할(적응시킬) 인터페이스를 구현
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();
        }
    }
}

 

- 위처럼 어댑터를 만들면 Duck 객체에 Turkey 객체를 넣을 수 있음

WildTurkey turkey = new WildTurkey(); //Turkey인터페이스를 구현한 클래스 객체
Duck turkeyAdapter = new TurkeyAdapter(turkey);

 

클래스 어댑터란?

- 어댑터엔 객체 어댑터클래스 어댑터가 있음 (위에 내용들은 객체 어댑터)

- 클래스 어댑터 형식으로 구현하려면 다중 상속이 필요하기 때문에 자바에선 불가능

- 객체 어댑터는 구성을 통해 어댑티에 요청 전달을 하지만, 클래스 어댑터에선 타겟/어댑티 클래스 둘 모두를 상속 받는 서브 클래스를 만들어 요청 처리

 

 

 Ojbect Adapter

Class Adapter 

장점 

상속이 아닌 구성(Composition)을 사용해 더 유연함

Adaptee 전체를 재구현하지 않아도 되고, Adaptee의 행동을 오버라이드 할 수 있음

Adaptee 객체를 만들지 않아도 됨

단점

 Adaptee 객체를 만들어야 사용가능

상속을 이용하기 때문에 특정 Adatee 클래스에만 적용가능

다중상속이 지원되는 언어에서만 사용가능

 


 

퍼사드 패턴

  • 어떤 서브시스템의 일련의 인터페이스에 대한 통합된 인터페이스를 제공한다
  • 퍼사드에서 고수준 인터페이스를 정의하기 때문에 서브 시스템을 더 쉽게 사용할 수 있음. 

ex) '복잡한 서브 시스템과 분리'  :: 홈 씨어터 

- 영화를 보기 위해 light.on > movie.on .. 처럼 여러 서브 시스템을 일일히 제어하는 게 아니라

- 퍼사드 클래스를 하나 생성해 놓고 watchMovie() 안에서 위의 서브 시스템 제어를 명시해 놓고 클라는 해당 메소드만 호출하는 것

 

 

디자인 원칙

  • 최소 지식 원칙 (=데메테르의 법칙, Law of Demeter) - 정말 친한 친구하고만 얘기해라
  • 객체 간의 상호작용을 줄이는 4가지 가이드라인 : 다음 4 종류 객체의 메소드만 호출
    1. 객체 자체
    2. 메소드에 매개변수로 전달된 객체
    3. 해당 메소드에서 생성하거나 인스턴스를 만든 객체
    4. 그 객체에 속하는 구성요소 (자신이 가지고 있는)
  • 왜? 어떤 메소드를 호출한 결과로 리턴받은 객체에 있는 메소드를 호출하는 것 -> 직접 알고 있는 객체의 수가 늘어 남
  • 그 객체 쪽에서 대신 요청을 하도록 만들어야 함.
//1.가이드라인을 따르지 않은 경우
public float getTemp(){
	Thermometer thermometer = station.getThermometer();
    return thermometer.getTemperature(); 
    //station으로 부터 Thermometer 객체를 받아 해당 객체의 메소드를 직접 호출
}

//2.가이드라인을 따른 경우
public float getTemp(){
	return station.getTemperature();
    //station 클래스에 thermometer에 요청을 해주는 메소드를 추가해 의존하는 클래스 개수 줄임
}

 

 

데코레이터 패턴 vs 어댑터 패턴 vs 퍼사드 패턴

  • 셋 다 클라이언트 코드를 고치지 않고 특정 동작을 수행
  • 데코레이터 : 객체의 행동/책임을 추가
  • 어댑터 : 인터페이스를 변환
  • 퍼사드 : 일련의 행동을 감싸서 단순화

 

Q&A

1. (어댑터) 대형 타겟 인터페이스를 구현해야하는 경우 할 일이 매우 많아지지 않나

맞음. 하지만 기존 코드를 새 인터페이스에 맞춰 고치는 것보다 변동 사항을 캡슐화한 하나의 클래스를 제공하는 법이 나음

 

2. (퍼사드) 최소 지식 원칙의 단점은?

다른 구성요소에 대한 메소드 호출을 처리하기 위해 '래퍼' 클래스를 생성해야 해, 시스템이 더 복잡해지고 개발 시간/성능이 안좋아 질 수 있음.

Comments