일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- jquery
- 상속예제
- 스프링
- 업캐스팅
- 자바
- 다형성
- 단축키
- 형변환
- 전체
- Validations
- 다운캐스팅
- 페이징
- 로또
- angular2
- 전자정부
- 전체텍스트
- IBatis procedure
- 자바 야구게임
- while
- 25가지 효율적인 sql작성법
- Login with OAuth Authentication
- 이클립스
- full text indexing
- 추상클래스
- 야구게임
- 가변인자
- Full text
- 상속
- Random
- Today
- Total
nalaolla
[디자인패턴] 어뎁터 패턴 (Adapter Pattern) 본문
어뎁터 패턴 (Adapter Pattern) : 한 클래스의 인터페이스를 클라이언트에서 사용하고자 하는 다른 인터페이스로 변환한다. 이 패턴을 사용하면 다른 인터페이스와의 호환성 문제를 해결할 수 있다.
여기서 말하는 어뎁터(Adapter)를 설명하자면..
보통 직구를 해서 전자제품을 구매하거나, 해외여행을 갈 때 한국과는 다른 규격의 전기 플러그를 사용할 때가 있습니다. 그럴때 보통 '돼지코'라고 불리는 플러그 변환 어뎁터를 씁니다.
이 어뎁터가 특정 규격의 플러그를 다른 플러그와 호환이 가능하게 하듯이, 객체 지향 프로그래밍에서도 비슷하게 사용할 수 있습니다.
A시스템사는 몇년 전 부터 식권 발매 시스템을 개발하여 운영하고 있습니다.
이 시스템이 안정적으로 운영되면서 최근 같은 사업을 하던 G그룹이 인수를 제안하였고, 시스템을 통합하여 운영할 계획을 세웠습니다.
G사는 A사의 기존의 시스템의 기능을 그대로 제공하면서, 몇 가지 기능을 좀 더 추가하려고 합니다.
이럴때는 어떻게 해야할까요?
A사와 G사의 식권 발권 시스템은 이런 기능들을 가지고 있습니다.
A사 |
G사 |
식권 선택 |
식권 선택 |
식권 출력 |
식권 출력 |
구매 (오프라인) |
오프라인 구매 |
|
온라인 구매 |
|
메뉴 들고오기 |
A사와 G사는 식권 선택과 출력의 기능은 동일하나, A사는 오프라인만 판매를하고 G사는 오프라인과 온라인을 나누어서 구매할 수 있도록 나누어져 있습니다. 또한 메뉴정보를 가져오는 기능도 가지고 있습니다.
A사의 식권 시스템을 인터페이스로 구현합니다.
public interface TicketA {
public void choice(int token); public void print(); public void buy(); }
|
public class TicketSystemA implements TicketA {
@Override public void choice(int token) { System.out.println("선택된 식권 타입은... " + token + " 입니다");
}
@Override public void print() { System.out.println("식권을 출력합니다.."); }
@Override public void buy() { System.out.println("식권을 구매합니다..");
}
} |
G사의 식권 시스템을 인터페이스로 구현합니다.
public interface TicketG {
public void choice(int token); public void print(); public void buyOnOffline(); public void buyOnOnline(); public String getMenu();
}
|
public class TicketSystemG implements TicketG{
@Override public void choice(int token) { System.out.println("선택된 식권 타입은... " + token + " 입니다");
}
@Override public void print() { System.out.println("식권을 출력합니다..");
}
@Override public void buyOnOffline() { System.out.println("오프라인으로 구매합니다..");
}
@Override public void buyOnOnline() { System.out.println("온라인으로 구매합니다..");
}
@Override public String getMenu() { return "메뉴정보를 DB에서 가져왔습니다."; }
} |
이 시스템들이 동작하는걸 확인 해 봅니다.
public class TicketMachine {
public static void main(String[] args) {
TicketA ticketA = new TicketSystemA(); ticketA.choice(1); ticketA.buy(); ticketA.print();
System.out.println("-----------------");
TicketG ticketG = new TicketSystemG(); ticketG.choice(1); ticketG.buyOnOffline(); ticketG.buyOnOnline(); ticketG.print(); System.out.println(ticketG.getMenu());
}
} |
보다시피 A사와 G사의 시스템 모두 잘 작동하는것을 볼 수 있습니다.
문제는 여기서 부터 시작됩니다.
TicketG ticketG = new TicketSystemA();
|
기존 G사의 시스템안에서 A사의 시스템이 정상적으로 돌아가야 하기 때문에, 이것을 해결하려면 A사의 인터페이스를 G사에 맞게 다시 정의해 주어야 합니다. 이것은 소스 전체의 중복을 야기합니다.
public class NewTicketSystem implements TicketG{
@Override public void choice(int token) { System.out.println("선택된 식권 타입은... " + token + " 입니다");
}
@Override public void print() { System.out.println("식권을 출력합니다..");
}
@Override public void buyOnOffline() { System.out.println("식권을 구매합니다..");
}
@Override public void buyOnOnline() {
throw new UnsupportedOperationException("지원되지 않는 기능");
}
@Override public String getMenu() {
throw new UnsupportedOperationException("지원되지 않는 기능");
}
} |
G사의 인터페이스로 정의한 A사의 시스템입니다. 보다시피 choice, print, buyOffline 메소드를 정의하기 위해 기존의 코드를 중복하여 사용하였습니다. (아무 의미없이 copy and paste..)
만약 이 시스템이 예제로 든 시스템이 아니라 거대한 시스템이라면.. 엄청난 비효율을 불러올것이 분명합니다.
G사와 A사의 개발팀은 어뎁터 패턴을 사용하여 이 문제를 현명하게 해결하였습니다.
public class TicketAdapter implements TicketG{
private TicketA ticket;
public TicketAdapter(TicketA ticket) { super(); this.ticket = ticket; }
@Override public void choice(int token) { ticket.choice(token);
}
@Override public void print() { ticket.print();
}
@Override public void buyOnOffline() { ticket.buy();
}
@Override public void buyOnOnline() { throw new UnsupportedOperationException("지원되지 않는 기능");
}
@Override public String getMenu() { throw new UnsupportedOperationException("지원되지 않는 기능");
}
} |
위의 어뎁터를 보면, 생성자로 A사의 TicketSystemA 클래스를 받고, G사의 인터페이스를 구현하여 그대로 사용할 부분에 A사의 메소드를 호출합니다. 그리고 지원하지 않는 기능은 예외를 정의하여 처리합니다.
public class TicketMachine {
public static void main(String[] args) {
TicketG ticketG = new TicketAdapter(new TicketSystemA());
ticketG.choice(1); ticketG.buyOnOffline(); ticketG.print(); try {
System.out.println(ticketG.getMenu());
}catch(UnsupportedOperationException e) { System.out.println("이 서비스는 G사의 다른 시스템에서 제공되는 기능입니다."); }
}
}
|
이제 G사의 인터페이스로 A사의 시스템 클래스를 성공적으로 사용할 수 있게 되었습니다.
G사의 식권 발매기에서도 A사의 시스템이 아주 잘 돌아가는 것이죠..
만약 어뎁터패턴이 없었다면 G사와 A사의 개발자들은 최소한 며칠은 밤을 새 가면서 기존의 코드를 모두 옮겨야 했겠죠..
++ 그렇다면 A사의 System 클래스와 G사의 System 클래스를 모두 상속받아 만들 수도 있지 않을까요? >> 물론 가능합니다! 하지만 자바 같은 객체지향 언어에서는 다중 상속을 지원하지 않죠..
++ 자바에서는 Vector, Stack 등 옛날 버전에서 사용하던 Enumeration을 새로운 Iterator으로 바꾸면서 어뎁터패턴을 사용하였습니다.
아직 어뎁터패턴이 이해가 안되시나요? 그럼 이렇게 생각해 보세요.
어뎁터 패턴은 B를 A처럼 포장하여 A로 사용할 수 있게 하는 패턴이다.
(자료 출처 : http://m.todayhumor.co.kr/view.php?table=animal&no=55892)
'JAVA > 999. 디자인패턴' 카테고리의 다른 글
[디자인패턴] 반복자 패턴 (Iterator Pattern) (0) | 2020.04.09 |
---|---|
[디자인패턴] 퍼사드 패턴 (Facade Pattern) (0) | 2020.04.09 |
[디자인 패턴] 커맨드 패턴 (Command Pattern) (0) | 2020.04.09 |
[디자인패턴]싱글턴 패턴(Singleton Pattern) (0) | 2020.04.09 |
[디자인패턴] 추상 팩토리 패턴 (Abstract-Factory Pattern) (0) | 2020.04.09 |