1. Adapter Pattern


어댑터 패턴은 우리가 잘 알다시피 일상에서도 자주 볼 수 있다.


위의 이미지가 미국식 플러그라 조금 적응 안 되지만,

어쨌든 해외에 나가서 이처럼 국내 플러그의 인터페이스에 맞게 전기를 사용하려면

어댑터를 이용해서 해외 플러그에 맞게 변환해주어야 한다.



지금 알아보려는 어댑터 패턴도 이와 마찬가지이다.


왼쪽의 기존 시스템과 오른쪽의 업체에서 제공한 클래스 라이브러리.

이 2 개의 코드를 바꾸지 않고 중간에서 호환이 가능하게끔 변환해 주는 녀석을 어댑터라고 한다.


* Adapter Pattern :

한 클래스의 인터페이스를 클라이언트에서 사용하고자 하는 다른 인터페이스로 변환한다.

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


어댑터에는 2가지 종류가 있다.

이제 타겟(Target)과 어댑티(Adaptee) 라는 표현이 나올텐데,

타겟은 내가 사용하고 싶은 녀석, 어댑티는 내 의사와 관계없이 제공받은 녀석 이라고 생각하자.


(1) 객체 어댑터 :

- 객체 구성(composition) 을 사용한다.

- Adaptee 뿐만 아니라 그 서브 클래스에 대해서도 어댑터 역할을 할 수 있다는 장점이 있다.


(2) 클래스 어댑터 :

- 다중 상속을 사용한다. 즉, 자바에서는 적용할 수 없는 어댑터라 할 수 있다. (자바는 다중상속 불가)

Adaptee 전체를 다시 구현하지 않아도 된다는 장점이 있다.

- 또한 Adaptee 의 행동을 오버라이드 할 수 있다.

- 특정 Adaptee 클래스에서만 적용된다는 단점이 있다.


* 참고로 한 어댑터에 한 클래스만 감싸지 않아도 된다.

다중 어댑터도 얼마든지 가능하다. 대형 타켓 인터페이스를 구현해야 되는 경우

할 일이 많아진다고 생각할 수도 있지만, 그 경우에 어댑터를 사용하지 않는다고 하면

어차피 클라이언트 코드의 상당 부분을 고쳐야 한다. 결국 할 일이 많아진다는 건 변하지 않는다.


[예제 코드]

자바의 기존 콜렉션 인터페이스 'Enumeration', 새로운 콜렉션 인터페이스 'Iterator'.


<< interface >> Enumeration

 Adapter

<< interface >> Iterator 

hasMoreElements()

nextElement() 

hasNext()

next()

remove() 



Q. Enumeration 인터페이스를 사용하는 구형 코드를 사용해야 하는 경우가 종종 있지만

새로 만들 코드에서는 Iterator 만 사용할 계획이라면 어떻게 어댑터 패턴을 적용할 수 있을까?

public class EnumerationIterator implement Iterator 
{
    Enemeration enum;
    public EnumerationIterator (Enemeration enum) {
        this.enum = enum;
    }
    public boolean hasNext () {
        return enum.hasMoreElements();
    }
    public Object next() {
        return enum.nextElement();
    }
    public void remove() {
        throw new UnsupportedOperationException();
    }
}


Q. 반대로는 적용할 경우에는?

public class IteratorEnumeration implement Enumeration 
{
    Iterator iterator;
    public IteratorEnumeration (Iterator iterator) {
        this.iterator = iterator;
    }
    public boolean hasMoreElements () {
        return iterator.hasNext();
    }
    public Object nextElement() {
        return iterator.next();
    }
}



2. Facade Pattern

인터페이스를 단순화시키기 위해서 인터페이스를 변경한다.

(하나 이상의 클래스)의 복잡한 인터페이스를 깔끔하면서도 말쑥한 퍼사드(겉모양, 외관을 뜻함)로 덮어주자.

너무 설명만 하면 이해가 잘 안되니 예시를 들어보겠다.

집에 홈시어터가 있다고 가정하고 영화를 보려고 할 때, 어떤 절차를 거치게 될까?

생각보다 할 게 많다.


팝콘 기계on -> 팝콘 튀기기 -> 전등dim -> 스크린down -> 프로젝터on -> 프로젝터 input = DVD ->

프로젝터 wideScreenMode -> 앰프on -> 앰프 set for DVD -> 앰프 set Volume -> DVD on -> DVD play ...


켤 때만 해도 더럽게 할 게 많다.

끄는 것도 이만큼은 아니더라도 꽤 번거롭다.


퍼사드를 활용하면 저 위의 모든 순서를 watchMovie() 메소드 하나로 묶을 수 있다.

그러면 클라이언트는 이 메소드 하나만 부르면 되니 간편하다.


* Facade Pattern :

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

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


Q. 퍼사드에서 서브시스템 클래스들을 캡슐화하면 저수준 기능을 원하는 클라이언트에서는

어떻게 서브시스템 클래스에 접근할까?

- 퍼사드 클래스에서는 서브시스템 클래스들을 캡슐화 하지 않는다 !

그저 서브시스템의 기능을 사용할 수 있는 간단한 인터페이스를 제공할 뿐이다.

클라이언트에서 특정 인터페이스가 필요하다면 서브시스템 클래스를 그냥 사용하면 된다.

이와 같은 면은 퍼사드 클래스의 대표적인 장점이라 할 수 있다.

단순화된 인터페이스를 제공하면서도, 클라이언트에서 필요로 한다면 서브시스템의 모든 기능을

사용할 수 있도록 해준다는 것이 장점이다. 또한 클라이언트 구현과 서브시스템을 분리시킬 수 있다.


Q. 한 서브시스템에 퍼사드는 하나씩만 생성이 가능한가?

- 퍼사드의 갯수에는 제한이 없다.



3. Adapter 패턴 vs. Facade 패턴 vs. Decorator 패턴


각 패턴의 용도들을 비교해 차이점을 알아보자 !


* Adapter 패턴 : 한 인터페이스를 다른 인터페이스로 변환 (for 호환성)

인터페이스를 변경해서 클라이언트에서 필요로 하는 인터페이스로 적응시키기 위한 용도.


* Facade 패턴 : 인터페이스를 간단하게 바꿈 (for 간편함)

어떤 서브시스템에 대한 간단한 인터페이스를 제공하기 위한 용도.


* Decorator 패턴 : 인터페이스를 바꾸지 않고 책임(기능)만 추가함 (for 기능 추가)

객체를 감싸서 새로운 행동을 추가하기 위한 용도.



4. 최소 지식 원칙 (Principle of Least Knowledge) 또는 데메테르의 법칙 (Law of Demeter)

- 참고로 둘 다 같은 말이다.

- 정말 친한 친구들하고만 얘기하라.

- 최소 지식 원칙에 따르면 객체 사이의 상호작용은 될 수 있으면 아주 가까운 '친구' 사이에서만 허용하는 것이 좋다 !

- 이를 잘 따르면 여러 클래스들이 복잡하게 얽힌 시스템의 한 부분을 변경했을 때, 다른 부분까지

줄줄이 고쳐야 되는 상황을 미리 방지할 수 있다.


- 이 원칙을 지키기 위한 가이드 라인 : 다음 4 종류의 객체의 메소드만 호출하면 된다.

(1) 객체 자체

(2) 메소드에 매개변수로 전달된 객체

(3) 그 메소드에서 생성하거나 인스턴스를 만든 객체

(4) 그 객체에 속하는 구성요소

구성요소는 인스턴스 변수에 의해 참조되는 객체를 의미. 즉, 'A에는 B가 있다' 관계에 있는 객체.


- 위 가이드 라인에 따르면 다른 메소드를 호출해서 리턴 받은 객체의 메소드를 호출하는 건 바람직하지 않다고 한다.


- 객체들 사이의 의존성을 낮추고, 소프트웨어 관리가 더 용이해질 수는 있으나 단점도 있다.

다른 구성요소에 대한 메소드 호출을 처리하기 위해 'Wrapper' 클래스를 더 만들어야 할 수도 있다.

그러다 보면 시스템이 더 복잡해지면서 개발 시간이 늘어나고 성능 저하를 불러일으키는 요인이 될 것이다.


[예제 코드]


- 원칙을 따르지 않은 경우 -

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

station 으로부터 객체를 받은 다음 그 객체의 메소드를 호출.

즉, 위에 밑줄친 '다른 메소드를 호출해서 리턴 받은 객체의 메소드를 호출하는 형태'이므로 바람직하지 않다.

return station.getThermometer().getTemperature(); 와 별반 다를게 없다.


- 원칙을 따르는 경우 -

public float getTemp() {
    return station.getTemperature();
}

Station 클래스에 thermometer 에 요청하는 메소드를 추가하므로서 의존해야 하는 클래스 갯수를 줄인다.

가이드 원칙 (3) 에 따라 인스턴스를 만든 객체의 메소드를 호출 하였다.

public float getTemp() {
    Thermometer thermometer = station.getThermometer();
    return getTempHelper(thermometer);
}

public float getTempHelper(Thermometer thermometer) {
    return thermometer.getTemperature();
}

뭔가 교묘히 속이려고 하는 듯한 느낌의 코드이지만...

이 코드는 가이드 원칙 (2) 에 따라 원칙에 위배되지 않는 코드다.


위에 'return station.getThermometer().getTemperature();' 랑 뭐가 다르냐?! 할 수도 있지만....

다르다. 적어도 난 다르다고 생각한다.

사실 책에는 위의 코드가 원칙에 위배되지 않았다고만 하지 정작 자세한 이유는 다루지 않았다.


그래서 그냥 내 생각을 적어보자면....음..아마도,

객체 사이의 의존성을 낮추는 관점에서 봤을 때

'station.getThermometer() 로부터 리턴받은 Thermometer 객체의 메소드 호출' 과

'Thermomter 객체의 메소드 호출' 의 차이이지 않을까 하고 생각해본다. 

- 몇 개의 클래스하고 연결이 되어 있는지 생각해보면 이해할 수 있을 것이다 -



* 객체지향의 원칙 :

1) 바뀌는 부분은 캡슐화 한다.

2) 상속보다는 구성을 활용한다.

3) 구현이 아닌 인터페이스에 맞춰서 프로그래밍 한다.

4) 서로 상호작용을 하는 객체 사이에서는 가능하면 느슨하게 결합하는 디자인을 사용해야 한다.

5) 클래스는 확장에 대해서는 열려 있어야 하지만 코드 변경에 대해서는 닫혀 있어야 한다(OCP).

6) 추상화된 것에 의존해라. 구상 클래스에 의존해서는 안 된다(의존성 뒤집기 법칙).

7) 친한 친구들하고만 이야기한다(최소 지식 원칙).



* 이 장에서의 정리 :

- 기존 클래스를 사용하려고 하는데 인터페이스가 맞지 않으면 어댑터를 쓰자.

- 큰 인터페이스 or 여러 인터페이스를 단순화 시키거나 통합시켜야 되는 경우에는 퍼사드를 쓰자.


- 어댑터는 인터페이스를 클라이언트가 원하는 인터페이스로 바꿔주는 역할을 한다.

- 퍼사드는 클라이언트를 복잡한 서브시스템과 분리시켜주는 역할을 한다.


- 어댑터를 구현할 때는 Target 인터페이스의 크기와 구조에 따라 코딩해야 할 분량이 결정된다.

- 퍼사드 패턴에서는 서브시스템을 가지고 퍼사드를 만들고, 실제 작업은 서브클래스에 맡긴다.


- 어댑터 패턴에서는 객체 어댑터 패턴 vs. 클래스 어댑터 패턴(다중상속必)이 있다.

- 어댑터는 다중 어댑터로 만들 수 있고, 퍼사드는 한 서브시스템에 여러 개 만들어도 된다.


- 어댑터, 퍼사드 모두 인터페이스를 바꿔주는데 !

Adapter : 인터페이스를 변환

Facade : 인터페이스를 통합/단순화 시킴

+ Decorator : 인터페이스를 바꾸지 않고, 객체를 감싸서 새로운 기능을 추가할 수 있다.



(이미지 출처 :

http://librairie.immateriel.fr/fr/read_book/9780596007126/ch07

http://www.cnblogs.com/zhuqiang/archive/2012/04/24/2468240.html

http://www.cnblogs.com/zhuqiang/archive/2012/04/24/2468323.html)

by kelicia 2014. 5. 27. 21:16