ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [디자인패턴] 퍼사드 패턴 (Facade Pattern)
    IT, 프로그래밍/Design Patterns 2019. 1. 7. 22:16

    퍼사드 패턴 (Facade Pattern) = 어떤 서브시스템의 일련의 인터페이스에 대한 통합된 인터페이스를 제공합니다. 퍼사드에서 고수준 인터페이스를 정의하기 때문에 서브시스템을 더 쉽게 사용할 수 있습니다.


    facade에 대한 이미지 검색결과


    * 퍼사드(Facade) 라는 것은 무엇일까요?

    퍼사드란, 프랑스어 Façade 에서 유래된 단어로 건물의 외관이라는 뜻을 가지고 있습니다. 건물의 외벽에서 보면 안의 구조는 보이지 않습니다. 


    퍼사드 패턴은 많은 서브시스템(내부 구조) 거대한 클래스(외벽)로 만들어 감싸서 편리한 인터페이스를 제공해 줍니다.




    퍼사드패턴으로 전자레인지 만들기


    microwave oven에 대한 이미지 검색결과


    우리는 전자레인지를 버튼만 클릭하면 동작 시킬 수 있습니다.

    동작시킬 타이머만 설정하고 실행 버튼만 클릭하면 곧 따뜻한 음식을 먹을 수 있죠.

    여기서 전자레인지가 동작하는 원리라던가 일련의 동작들을 이해할 필요는 전혀 없습니다.

    만약 우리가 전자레인지에 피자를 데우기 위해서 타이머의 전원을 넣고, 실행 신호를 보내고, 마이크로파 발생기를 직접 작동시켜야 한다면 정말 끔찍할겁니다. 그러면 전자레인지를 쓰기가 싫겠죠.

    그래서 제조사들은 단순한 버튼으로 동작시킬 수 있게 편리한 인터페이스를 제공합니다. 



    전자레인지의 구성을 살펴봅시다.


     쿨러 

     전자레인지의 열을 식혀준다.

     마그네트론

     마이크로파를 발생시킨다 

     턴테이블

     조리할 음식을 회전시킨다 

     타이머

     시간이 되면 전자레인지를 끈다. 





    부품의 전원을 켜고끄는 스위치 인터페이스

    1
    2
    3
    4
    5
    6
    7
    public interface Switch {
            
        public void on();
        public void off();
     
    }
     
    cs


    쿨러

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    public class Cooler implements Switch {
     
        @Override
        public void on() {
     
            System.out.println("쿨러 작동 시작..");
        }
     
        @Override
        public void off() {
            
            System.out.println("쿨러 작동 끝..");
            
        }
     
    }
    cs


    마그네트론

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    public class Magnetron implements Switch {
     
        @Override
        public void on() {
            System.out.println("마이크로파 발생기 켜짐.. 작동중");
            
        }
     
        @Override
        public void off() {
            System.out.println("마이크로파 발생기 꺼짐");
            
        }
     
        
    }
     
    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
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    public class TimeChecker implements Switch{
        
        public static long TIME_INTERVAL = 1000;
        private final int EXPIRED_TIME;
        private Timer timer;
        private TimerTask task;
        int count = 0;
        MicrowaveFacade microwave;
        
        
        
        public TimeChecker(int milsec, MicrowaveFacade microwave) {
            super();
            this.EXPIRED_TIME = milsec;
            this.count = EXPIRED_TIME/1000;
            timer = new Timer();
            this.microwave = microwave;
            task = new TimerTask() {
                
                
                @Override
                public void run() {
                    if(count > 0) {
                        System.out.println("Timer.. " + (count--+ " 초");
                        
                    }else {
                        System.out.println("조리가 완료되었습니다!");
                        
                        timer.cancel();
                        microwave.off();
                    
                    }
                    
                }
            };
            
            
        }
     
     
     
        @Override
        public void on() {
     
            
            timer.schedule(task, 0, TIME_INTERVAL);
            
            
        }
        
        @Override
        public void off() {
            
            timer.cancel();
        }
        
     
    }
    cs


    턴테이블

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    public class Turntable implements Switch{
     
        @Override
        public void on() {
            System.out.println("Turntable이 움직입니다");
            
        }
     
        @Override
        public void off() {
            System.out.println("Turntable이 멈췄습니다");
            
        }
     
        
        
    }
    cs






    만약 퍼사드패턴을 쓰지 않는다면, 우리는 직접 전원 스위치를 on 시켜야 합니다.

    먼저 쿨러를 작동시키고.. 마그네트론을 작동시키고.. 턴테이블을 돌린 다음에 타이머를 원하는 시간만큼 작동시킵니다.

    또 만약 정지시키려면 하나하나씩 모두 손수 스위치를 내려 줘야하죠.

    우리는 이것을 편리하게 바꿀 퍼사드 클래스를 하나 만들겁니다.


    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
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    public class MicrowaveFacade {
        
        Cooler cooler;
        Magnetron magnetron;
        TimeChecker timeCheck;
        Turntable turntable;
        Mode mode;
        Switch[] switches;
        String food;
        boolean isActive = false;
        
        public MicrowaveFacade(Cooler cooler, Magnetron magnetron, TimeChecker timeCheck, Turntable turntable, Mode mode) {
            super();
            this.cooler = cooler;
            this.magnetron = magnetron;
            this.timeCheck = timeCheck;
            this.turntable = turntable;
            this.mode = mode;
            switches = new Switch[]{cooler, magnetron, timeCheck,turntable};
        }
     
        public MicrowaveFacade(Mode mode) {
            super();
            cooler = new Cooler();
            magnetron = new Magnetron();
            timeCheck = new TimeChecker(mode.getValue(), this);
            turntable = new Turntable();
            this.mode = mode;
            switches = new Switch[]{cooler, magnetron, turntable, timeCheck};
     
            
            
        }
        
        
        public void on() {
            
        
            for(int i=0; i<switches.length++i) {
                switches[i].on();
            }
            isActive = true;
            
            
        }
        
        public void off() {
            
            for(int i=0; i<switches.length++i) {
                switches[i].off();
            }
            
            isActive = false;
     
            
        }
     
        public void getMode() {
            System.out.println("현재 모드는... " + mode.getName());
     
        }
     
        public void setMode(Mode mode) {
            this.mode = mode;
        }
        
     
     
    }
    cs


    사용자는 이제 on과 off버튼만 알면 전자레인지를 작동시킬 수 있습니다.

    또한, 어떤 부품을 다른 부품으로 교체하여 전자레인지를 작동 시킬수도 있죠.

    (예를 들면 기존보다 3배 강력한 마그네트론으로 교체한다던가..)


    이제 아주 간편해진 전자레인지를 작동시켜 봅시다.


    1
    2
    3
    4
    5
    6
    7
    8
    9
    public class User {
     
        public static void main(String[] args) {
            MicrowaveFacade microwave = new MicrowaveFacade(Mode.FAST);
            microwave.on();
            
        }
     
    }
    cs





    버튼 한번으로 간편하게 사용할 수 있습니다. 지금 우리가 전자레인지를 쓰는 것 처럼 말입니다.


    중간에 끄는 것도 한 번 해보겠습니다. 작동시킨 후 2초 후에 급한 일이 생겨 전자레인지를 off 시킵니다.


     

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    public class User {
     
        public static void main(String[] args) {
            MicrowaveFacade microwave = new MicrowaveFacade(Mode.FAST);
            microwave.on();
     
            try {
                Thread.sleep(2000);
                microwave.off();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
     
        }
     
    }
    cs



    버튼 하나로 전자레인지를 정지시켰습니다.








    클래스 다이어그램과 같이 (위의 다이어그램에서 Mode enum은 일부러 삭제시켰습니다.) 전자레인지를 사용하는 User 클래스에서는 전자레인지의 내부 부품들이 퍼사드 클래스에 감싸져 있지만 제공되는 인터페이스(on, off 버튼 등..)를 통해 편리하게 사용할 수 있습니다.






    ++ 퍼사드 클래스가 서브시스템 클래스들을 캡술화 하는 것은 아닙니다. 다만 기능을 편하게 사용할 수 있도록 인터페이스를 제공해 줄 뿐이죠. (접근지정자가 default 인 것에 주목하세요)


    ++ 최소 지식 원칙 (데메테르의 원칙, Law of Demeter) : 정말 관련있는 객체와만 관계를 맺어라!

    • 객체자체
    • 메소드에 매개변수로 전달된 객체
    • 그 메소드에서 생성하거나 인스턴스를 만든 객체
    • 그 객체에 속하는 구성요소

    이 종류의 메소드만을 호출하면 이 원칙을 지킬 수 있습니다.


    왜 지켜야 하냐구요?


    이런 상황이 올 수도 있다는 것이죠..

    의존성을 낮추어 관리를 용이하게 해 주어야 하는것이 핵심입니다.

    하지만 다른 메소드를 호출하기 위한 래퍼클래스를 만들어야 하는 등의 단점도 생길 수 있으니 상황에 따라 잘 선택하는 것이 좋은 일 이겠죠?



    이상 전자레인지 예시를 사용하여 퍼사드패턴이 어떤 것인지 알아보았습니다.

    질문이나 틀린 부분이 있다면 언제든지 댓글 남겨주세요~~!

Designed by Tistory.