어댑터는 '제공되고 있는 것'과 '필요한 것' 사이를 연결해주는 역할을 한다.
예제)
Banner 클래스를 사용해서 Print 인터페이스를 충족시키는 클래스를 만든다.
PrintBanner 클래스: 어댑터 역할
제공되어 있는 Banner 클래스를 상속해서, 필요로 하는 Print 인터페이스를 구현한다.
package adapter;
public class Banner {
private String string;
public Banner(String string) {
this.string = string;
}
public void showWithParen() {
System.out.println("(" + string + ")");
}
public void showWithAster() {
System.out.println("*" + string + "*");
}
}
package adapter;
public interface Print {
public void printWeak();
public void printStrong();
}
package adapter;
public class PrintBanner extends Banner implements Print {
public PrintBanner(String string) {
super(string);
}
@Override
public void printWeak() {
showWithParen();
}
@Override
public void printStrong() {
showWithAster();
}
}
package adapter;
public class Main {
public static void main(String[] args) {
Print p = new PrintBanner("Hello");
p.printStrong();
p.printWeak();
}
}
실행결과:
*Hello*
(Hello)
메인메소드에서는 Print 인터페이스만을 사용한다는 점을 주목해보자.
Banner클래스의 showWithParen나 showWithAster는 완전히 숨겨져 있다.
교류100볼트를 직류12볼트에 직접 연결할 수 없어 AC 어댑터를 사용하는 것처럼,
프로그래밍상에서 직접 Banner클래스를 사용할 수 없는 경우 위와 같은 패턴을 이용하면 좋을 듯하다.
위의 예제에서 Print가 인터페이스가 아닌 클래스인 경우도 생각해보자.
그러면 PrintBanner는 다중상속을 할 수 없기때문에 PrintBanner의 필드로 Banner를 두는 것도 좋은 방법이다.
package adapter;
public abstract class Print {
public abstract void printWeak();
public abstract void printStrong();
}
package adapter;
public class PrintBanner extends Print {
private Banner banner;
public PrintBanner(String string) {
this.banner = new Banner(string);
}
@Override
public void printWeak() {
banner.showWithParen();
}
@Override
public void printStrong() {
banner.showWithAster();
}
}
어댑터 패턴 정리
target(대상)의 역할
지금 필요한 메소드를 결정한다. 노트북을 작동시키기 위한 직류12볼트에 해당한다.
예제에서는 Print 인터페이스나 Print 클래스가 이 역할을 한다.
client(의뢰자)의 역할
target 역할의 메소드를 사용해서 일을 한다. 직류 12볼트로 움직이는 노트북에 해당한다.
예제에서는 Main 클래스가 이 역할을 한다.
Adaptee(개조되는 쪽)의 역할
Adapt-er(개조하는 쪽)가 아니고 Adapt-ee(개조되는 쪽)이다. Adaptee는 이미 준비되어 있는 메소드를 가지고 있는 역할이다. 교류100볼트의 AC전원에 해당하며,
예제에서는 Banner 클래스가 이 역하을 한다. 이 Adaptee역의 메소드가 target역할의 메소드와 일치하면(즉, 가정에 제공되고 있는 것이 처음부터 직류 12볼트라면) 다음 Adapter의 역할은 필요가 없다.
Adapter의 역할
Adaptee 역할의 메소드를 사용해서 어떻게든 target 역할을 만족시키기 위한 것이 Adapter 패턴의 목적이며, Adapter 역할의 임무이다. 교류100볼트를 직류12볼트로 교환하는 Adapter에 해당한다.
예제에서는 PrintBanner클래스가 Adapter역할을한다.
왜? 사용하나
Adapter 패턴은 기존의 클래스를 개조해서 필요한 클래스를 만든다.
이 패턴으로 필요한 메소드를 발빠르게 만들 수 있다.
만약 버그가 발생해도 기존의 클래스(Adaptee의 역할)에는 버그가 없으므로 Adapter 역할의 클래스를 중점적으로 조사하면된다.
비록 소스가 없더라도
이미 만들어진 클래스를 새로운 인터페이스(API)에 맞게 개조시킬 때는 당연히 Adapter 패턴을 사용해야 한다. 그러나 실제 우리가 새로운 인터페이스(API)에 맞게 개조시킬 때는 기존 클래스의 소스를 바꾸어서 '수정'하려고 생각한다. '이것을 조금 바꾸면 분명 작업은 끝이다'라고 생각하기 쉽다. 그러나 그렇게하면 동작 테스트가 이미 끝난 기존의 클래스를 수정한 후에 다시 한번 테스트해야한다. Adapter 패턴은 기존의 클래스를 전혀 수정하지 않고 목적한 인터페이스(API)에 맞추려는 것이다. 또한 Adapter 패턴에서는 기존 클래스의 소스 프로그램이 반듸 필요한 것은 안디ㅏ. 기존 클래스의 사양만 알면 새로운 클래스르 만들 수 있다.
버전 업과 호환성
신버전만 유지보수 하고 싶다면 신버전을 Adaptee, 구버전을 Target역할로 한다.
긜고 신버전의 클래스를 사용해서 구버전의 메소드를 구현하는 Adapter역할의 클래스를 만든다.
관련패턴
Bridge 패턴
Adapter 패턴은 인터페이스(API)가 서로 다른 클래스들을 연결하는 패턴이라면 Bridge 패턴은 기능의 계층과 구현의 계층을 연결시키는 패턴이다.
Decorator 패턴
Adapter 패턴은 인터페이스(API)의 차이를 조정하기 위한 패턴이라면 Decorator 패턴은 인터페이스(API)를 수정하지 않고 기능을 추가하는 패턴이다.
'자바' 카테고리의 다른 글
jpa 엔티티 매핑 정리 (0) | 2024.03.24 |
---|---|
[자바] Stream의 match함수 예제(anyMatch, noneMatch, allMatch) (0) | 2024.01.10 |
dto 복사에 관하여 (0) | 2022.12.08 |
[스레드] 12. 스레드풀(3) (0) | 2022.05.07 |
[스레드] 11. 스레드풀(2) (0) | 2022.05.05 |