ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [디자인패턴] 어뎁터 패턴 (Adapter Pattern)
    IT, 프로그래밍/Design Patterns 2019. 1. 5. 17:59

    어뎁터 패턴 (Adapter Pattern) : 한 클래스의 인터페이스를 클라이언트에서 사용하고자 하는 다른 인터페이스로 변환한다. 이 패턴을 사용하면 다른 인터페이스와의 호환성 문제를 해결할 수 있다.

    여기서 말하는 어뎁터(Adapter)를 설명하자면..

    보통 직구를 해서 전자제품을 구매하거나, 해외여행을 갈 때 한국과는 다른 규격의 전기 플러그를 사용할 때가 있습니다. 그럴때 보통 '돼지코'라고 불리는 플러그 변환 어뎁터를 씁니다.




    이 어뎁터가 특정 규격의 플러그를 다른 플러그와 호환이 가능하게 하듯이, 객체 지향 프로그래밍에서도 비슷하게 사용할 수 있습니다.






    A시스템사는 몇년 전 부터 식권 발매 시스템을 개발하여 운영하고 있습니다.

    이 시스템이 안정적으로 운영되면서 최근 같은 사업을 하던 G그룹이 인수를 제안하였고, 시스템을 통합하여 운영할 계획을 세웠습니다.

    G사는 A사의 기존의 시스템의 기능을 그대로 제공하면서, 몇 가지 기능을 좀 더 추가하려고 합니다.

    이럴때는 어떻게 해야할까요?


    A사와 G사의 식권 발권 시스템은 이런 기능들을 가지고 있습니다.


     A사

    G사 

     식권 선택

     식권 선택 

     식권 출력

     식권 출력 

     구매 (오프라인)

     오프라인 구매

     

     온라인 구매 

     

     메뉴 들고오기 


    A사와 G사는 식권 선택과 출력의 기능은 동일하나, A사는 오프라인만 판매를하고 G사는 오프라인과 온라인을 나누어서 구매할 수 있도록 나누어져 있습니다. 또한 메뉴정보를 가져오는 기능도 가지고 있습니다.


    A사의 식권 시스템을 인터페이스로 구현합니다.

    1
    2
    3
    4
    5
    6
    7
    public interface TicketA {
     
        public void choice(int token);
        public void print();
        public void buy();
    }
     
    cs

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    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("식권을 구매합니다..");
            
        }
     
        
        
    }
    cs





    G사의 식권 시스템을 인터페이스로 구현합니다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public interface TicketG {
     
        public void choice(int token);
        public void print();
        public void buyOnOffline();
        public void buyOnOnline();
        public String getMenu();
     
        
    }
     
    cs


    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
    32
    33
    34
    35
    36
    37
    38
    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에서 가져왔습니다.";
        }
     
        
        
        
        
    }
    cs

    이 시스템들이 동작하는걸 확인 해 봅니다.


    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
     
    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());
            
     
            
            
        }
        
    }
    cs



    보다시피 A사와 G사의 시스템 모두 잘 작동하는것을 볼 수 있습니다.






    문제는 여기서 부터 시작됩니다.


    1
    2
     TicketG ticketG = new TicketSystemA();
     
    cs


    기존 G사의 시스템안에서 A사의 시스템이 정상적으로 돌아가야 하기 때문에, 이것을 해결하려면 A사의 인터페이스를 G사에 맞게 다시 정의해 주어야 합니다. 이것은 소스 전체의 중복을 야기합니다.


    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
    32
    33
    34
    35
    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("지원되지 않는 기능");
            
        }
     
    }
    cs


    G사의 인터페이스로 정의한 A사의 시스템입니다. 보다시피 choice, print, buyOffline 메소드를 정의하기 위해 기존의 코드를 중복하여 사용하였습니다. (아무 의미없이 copy and paste..)


    만약 이 시스템이 예제로 든 시스템이 아니라 거대한 시스템이라면.. 엄청난 비효율을 불러올것이 분명합니다.


    G사와 A사의 개발팀은 어뎁터 패턴을 사용하여 이 문제를 현명하게 해결하였습니다.

    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
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    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("지원되지 않는 기능");
     
        }
        
        
    }
    cs


    위의 어뎁터를 보면, 생성자로 A사의 TicketSystemA 클래스를 받고, G사의 인터페이스를 구현하여 그대로 사용할 부분에 A사의 메소드를 호출합니다. 그리고 지원하지 않는 기능은 예외를 정의하여 처리합니다.


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    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사의 다른 시스템에서 제공되는 기능입니다.");
            }
            
     
            
        }
        
    }
     
    cs




    이제 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)


Designed by Tistory.