Adapter Pattern
어댑터 패턴(Adapter Pattern)은 특정 클래스 인터페이스를 클래스에서 요구하는 다른 인터페이스로 변환하여, 호환되지 않는 클래스를 사용할 수 있도록 한다.
Adapter?
어댑터란, 우리가 해외를 갈 때 110V로 변환하기 위해 챙기는 어댑터와 목적이 거의 동일하다.
어떠한 인터페이스를 클라이언트에서 요구하는 형태로 변환하는 역할을 한다.
예를 어떠한 업체의 라이브러리를 사용하는데, 이 라이브러리에서 지원하는 인터페이스와 기존의 인터페이스가 호환되지 않는다고 가정해보자. 더군다나 기존의 코드를 수정할 수 없는 상황이라면?

이 두 가지를 적응 시켜주는 것이 어댑터의 역할이다.
어댑터 사용 방법
public class MallardDuck implements Duck {
public void quack() {
System.out.println("Quack");
}
public void fly() {
System.out.println("I'm flying");
}
}
public class WildTurkey implements Turkey {
public void gobble() {
System.out.println("Gobble gobble");
}
public void fly() {
System.out.println("I'm flying a short distance");
}
}
MallardDuck 객체와 WildTurkey라는 객체가 있다.
둘은 각각 Duck과 Turkey라는 다른 인터페이스를 구현하고 있다.
만약 Duck 객체가 모자라서 Turkey라는 객체를 사용해야 할 상황이 오면 어떻게 해야 할까?
각각 다른 인터페이스를 구현하기 때문에 지금 상태 그대로 사용할 수가 없다.
이러한 경우에 사용하는 것이 Adapter이다.
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();
}
}
}
울음 소리는 turkey 객체의 소리를 그대로 가져와서 사용한다.
칠면조는 하늘을 날지 못하기 때문에 짧은 거리를 5번 반복해서 날아간다.
*여기서 TurkeyAdapter와 Turkey는 Composite 관계로 연관되어 있다.
이제 테스트를 해보자.
public class DuckTestDrive {
public static void main(String[] args) {
Duck duck = new MallardDuck();
Turkey turkey = new WildTurkey();
Duck turkeyAdapter = new TurkeyAdapter(turkey);
System.out.println("칠면조가 말하길");
turkey.gobble(); // 골골
turkey.fly(); // 짧은 거리를 날고 있어요!
System.out.println("\n오리가 말하길);
testDuck(duck); // 꽥, 날고 있어요!
System.out.println("\n 칠면조 어댑터가 말하길");
testDuck(turkeyAdapter); // 골골, 짧은 거리를 날고 있어요! (5번 반복)
}
static void testDuck(Duck duck) {
duck.quack();
duck.fly();
}
}
Adapter를 이용하여 칠면조를 오리로 바꾸어줬기 때문에 testDuck() 메서드는 오리와 칠면조를 구분하지 못한다.
*클라이언트는 중간에 어댑터가 있다는 사실을 알 수 없다. 타깃 인터페이스만 볼 수 있음.

클래스 어댑터
어댑터 패턴은 객체 어댑터와 클래스 어댑터 두 가지로 나뉜다.
여태 살펴본 내용은 객체 어댑터에 해당하는 내용이다. 클래스 어댑터 패턴을 사용하려면 다중 상속이 필요한데, 자바에서는 다중 상속을 지원하지 않기 때문에 다른 언어를 활용해야 한다.

객체 어댑터는 Composite으로 요청을 전달하는 반면, 클래스 어댑터는 Target과 Adaptee를 모두 서브클래스로 만들어서 사용한다.
Facade Pattern
퍼사드 패턴(Facade Pattern)은 서브 시스템의 인터페이스를 통합 인터페이스로 묶어준다.
또한, 고수준 인터페이스도 정의하므로 서브시스템을 더 편리하게 사용할 수 있다.
어댑터 패턴은 원하는 인터페이스의 형태로 변환하기 위해 사용했다면, 퍼사드 패턴은 하나의 인터페이스를 단순하게 바꾸기 위한 패턴이다.

위와 같이 다양한 작업을 하는 프로그램은 동작 하나하나를 이렇게 나눠놓으면 한 눈에 봐도 복잡해 보인다.
위 다이어 그램은 홈시어터 프로그램인데 만약 TV를 off 할 경우에는 모든 작업을 역순으로 다시 실행해야 할 것이다.

이걸 하나의 퍼사드로 간단하게 만들어주는 것이 퍼사드 패턴이다.
디자인 패턴이라고 칭하기에는 과하고 하나의 방법론으로 보는 것이 좋을 것 같다.

최소 지식 원칙(Principle of Least Knowledge)
객체 사이의 상호작용은 될 수 있으면 아주 가까운 '친구' 사이에서만 허용해야 한다.
시스템을 디자인 할 때 객체와 상호작용을 하는 방식과 클래스의 개수에 주의를 기울여야 한다는 뜻이다.
무분별한 객체 친구를 만들지 않기 위한 4가지 가이드라인이 있다.
- 객체 자체
- 메소드에 매개 변수로 전달된 객체
- 메소드를 생성하거나 인스턴스를 만든 객체
- 객체에 속하는 구성 요소
위 가이드라인에 따르면 메소드를 호출하여 리턴 받은 객체의 메소드를 호출하는 방법은 좋지 않다.
대신 객체가 요청하도록 만들어야 한다.
간단하게 예를 들자면,
// 원칙을 따르지 않은 경우
public float getTemp() {
Thermometer thermometer = station.getThermomter();
return thermometer.getTemperature();
}
// 원칙을 따르는 경우
public float getTemp() {
return station.getTemperature();
}
그렇다면 다음 코드는 왜 최소 지식 원칙을 위반하는 것일까?
public class House {
WeatherStation station;
// 기타 메소드 및 생성자
public float getTemp() {
return station.getThermometer().getTemperature(); // 최소 지식 원칙 위반
}
}
다른 메소드 호출 결과로 리턴된 객체의 메소드를 한 번 더 호출하기 때문에 최소 지식 원칙 위반이다.
'Design Pattern' 카테고리의 다른 글
[Headfirst Design Pattern] 복합 패턴(Compound Patterns) (0) | 2024.06.13 |
---|---|
[Headfirst Design Pattern] 템플릿 메소드 패턴(Template Method Pattern) (0) | 2024.06.06 |
[Headfirst Design Pattern] 데코레이터 패턴(Decorator Pattern) (2) | 2024.04.24 |
[Headfirst Design Pattern] 옵저버 패턴(Observer Pattern) (0) | 2024.04.12 |
Adapter Pattern
어댑터 패턴(Adapter Pattern)은 특정 클래스 인터페이스를 클래스에서 요구하는 다른 인터페이스로 변환하여, 호환되지 않는 클래스를 사용할 수 있도록 한다.
Adapter?
어댑터란, 우리가 해외를 갈 때 110V로 변환하기 위해 챙기는 어댑터와 목적이 거의 동일하다.
어떠한 인터페이스를 클라이언트에서 요구하는 형태로 변환하는 역할을 한다.
예를 어떠한 업체의 라이브러리를 사용하는데, 이 라이브러리에서 지원하는 인터페이스와 기존의 인터페이스가 호환되지 않는다고 가정해보자. 더군다나 기존의 코드를 수정할 수 없는 상황이라면?

이 두 가지를 적응 시켜주는 것이 어댑터의 역할이다.
어댑터 사용 방법
public class MallardDuck implements Duck {
public void quack() {
System.out.println("Quack");
}
public void fly() {
System.out.println("I'm flying");
}
}
public class WildTurkey implements Turkey {
public void gobble() {
System.out.println("Gobble gobble");
}
public void fly() {
System.out.println("I'm flying a short distance");
}
}
MallardDuck 객체와 WildTurkey라는 객체가 있다.
둘은 각각 Duck과 Turkey라는 다른 인터페이스를 구현하고 있다.
만약 Duck 객체가 모자라서 Turkey라는 객체를 사용해야 할 상황이 오면 어떻게 해야 할까?
각각 다른 인터페이스를 구현하기 때문에 지금 상태 그대로 사용할 수가 없다.
이러한 경우에 사용하는 것이 Adapter이다.
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();
}
}
}
울음 소리는 turkey 객체의 소리를 그대로 가져와서 사용한다.
칠면조는 하늘을 날지 못하기 때문에 짧은 거리를 5번 반복해서 날아간다.
*여기서 TurkeyAdapter와 Turkey는 Composite 관계로 연관되어 있다.
이제 테스트를 해보자.
public class DuckTestDrive {
public static void main(String[] args) {
Duck duck = new MallardDuck();
Turkey turkey = new WildTurkey();
Duck turkeyAdapter = new TurkeyAdapter(turkey);
System.out.println("칠면조가 말하길");
turkey.gobble(); // 골골
turkey.fly(); // 짧은 거리를 날고 있어요!
System.out.println("\n오리가 말하길);
testDuck(duck); // 꽥, 날고 있어요!
System.out.println("\n 칠면조 어댑터가 말하길");
testDuck(turkeyAdapter); // 골골, 짧은 거리를 날고 있어요! (5번 반복)
}
static void testDuck(Duck duck) {
duck.quack();
duck.fly();
}
}
Adapter를 이용하여 칠면조를 오리로 바꾸어줬기 때문에 testDuck() 메서드는 오리와 칠면조를 구분하지 못한다.
*클라이언트는 중간에 어댑터가 있다는 사실을 알 수 없다. 타깃 인터페이스만 볼 수 있음.

클래스 어댑터
어댑터 패턴은 객체 어댑터와 클래스 어댑터 두 가지로 나뉜다.
여태 살펴본 내용은 객체 어댑터에 해당하는 내용이다. 클래스 어댑터 패턴을 사용하려면 다중 상속이 필요한데, 자바에서는 다중 상속을 지원하지 않기 때문에 다른 언어를 활용해야 한다.

객체 어댑터는 Composite으로 요청을 전달하는 반면, 클래스 어댑터는 Target과 Adaptee를 모두 서브클래스로 만들어서 사용한다.
Facade Pattern
퍼사드 패턴(Facade Pattern)은 서브 시스템의 인터페이스를 통합 인터페이스로 묶어준다.
또한, 고수준 인터페이스도 정의하므로 서브시스템을 더 편리하게 사용할 수 있다.
어댑터 패턴은 원하는 인터페이스의 형태로 변환하기 위해 사용했다면, 퍼사드 패턴은 하나의 인터페이스를 단순하게 바꾸기 위한 패턴이다.

위와 같이 다양한 작업을 하는 프로그램은 동작 하나하나를 이렇게 나눠놓으면 한 눈에 봐도 복잡해 보인다.
위 다이어 그램은 홈시어터 프로그램인데 만약 TV를 off 할 경우에는 모든 작업을 역순으로 다시 실행해야 할 것이다.

이걸 하나의 퍼사드로 간단하게 만들어주는 것이 퍼사드 패턴이다.
디자인 패턴이라고 칭하기에는 과하고 하나의 방법론으로 보는 것이 좋을 것 같다.

최소 지식 원칙(Principle of Least Knowledge)
객체 사이의 상호작용은 될 수 있으면 아주 가까운 '친구' 사이에서만 허용해야 한다.
시스템을 디자인 할 때 객체와 상호작용을 하는 방식과 클래스의 개수에 주의를 기울여야 한다는 뜻이다.
무분별한 객체 친구를 만들지 않기 위한 4가지 가이드라인이 있다.
- 객체 자체
- 메소드에 매개 변수로 전달된 객체
- 메소드를 생성하거나 인스턴스를 만든 객체
- 객체에 속하는 구성 요소
위 가이드라인에 따르면 메소드를 호출하여 리턴 받은 객체의 메소드를 호출하는 방법은 좋지 않다.
대신 객체가 요청하도록 만들어야 한다.
간단하게 예를 들자면,
// 원칙을 따르지 않은 경우
public float getTemp() {
Thermometer thermometer = station.getThermomter();
return thermometer.getTemperature();
}
// 원칙을 따르는 경우
public float getTemp() {
return station.getTemperature();
}
그렇다면 다음 코드는 왜 최소 지식 원칙을 위반하는 것일까?
public class House {
WeatherStation station;
// 기타 메소드 및 생성자
public float getTemp() {
return station.getThermometer().getTemperature(); // 최소 지식 원칙 위반
}
}
다른 메소드 호출 결과로 리턴된 객체의 메소드를 한 번 더 호출하기 때문에 최소 지식 원칙 위반이다.
'Design Pattern' 카테고리의 다른 글
[Headfirst Design Pattern] 복합 패턴(Compound Patterns) (0) | 2024.06.13 |
---|---|
[Headfirst Design Pattern] 템플릿 메소드 패턴(Template Method Pattern) (0) | 2024.06.06 |
[Headfirst Design Pattern] 데코레이터 패턴(Decorator Pattern) (2) | 2024.04.24 |
[Headfirst Design Pattern] 옵저버 패턴(Observer Pattern) (0) | 2024.04.12 |