전체 페이지뷰

2017년 3월 6일 월요일

Facade Pattern

홈씨어터 구축을 예제로 들어 공부해 봅니다. 오랜 시간을 들여 영화를 보는데 필요한 모든 장비들을 세팅하는데 성공했습니다. 설치한 것들은 다음과 같습니다.



이제 영화를 보려면 어떤 과정을 거쳐야 할까요?

팝콘 기계를 켜고, 튀기기 시작하면서 전등을 어둡게 하고, 스크린 내리고, 프로젝터 켜고, DVD신호를 입력되게 하고, 프로젝터를 와이드스크린 모드로 전환하고, 앰프를 켜고, 앰프입력을 DVD로 전환하고, 앰프를 서라운드 음향으로 설정하고, DVD플레이어 켜고, DVD 재생을 누릅니다.

이건 보기 위한 것에 불과하고, 영화가 끝나면 또 어떻게 해야할까요? 시스템이 업그레이드 되면 과정을 전부 다시 익혀야 합니까?

너무 복잡합니다.

이럴때 사용하는 것이 퍼사드(Façade) 패턴입니다.

위의 홈씨어터 시스템을 통합관리할 하나의 퍼사드를 만들어봅니다.
간단한 메소드들로 이루어진 퍼사드 클래스에서 각 서브시스템의 메소드를 호출하여 필요한 작업을 수행하도록 만듭니다. WatchMovie() 메소드로 위에 길게 설명한 복잡한 과정을 전부 수행하게 하면 되겠지요. 그러나, 역시 각가의 서브시스템 레벨에서도 접근이 가능하므로 그 클래스들도 여전히 사용가능합니다. 퍼사드는 그저 단순화된 사용 인터페이스를 제공해줄 뿐입니다.

어댑터 패턴과 퍼사드 패턴은 모두 여러 개의 클래스를 감싸서 사용할수 있게 만들어준다는 공통점이 있지만 둘의 목적이 다릅니다. 어댑터 패턴은 인터페이스를 변경하여 클라이언트에서 필요로 하는 인터페이스로 적응시키기 위한 것이고, 퍼사드 패턴은 서브시스템에 대한 간략화된 인터페이스를 제공하기 위한 것입니다.

홈씨어터 퍼사드 구현

우선 퍼사드에서 서브시스템의 모든 구성요소에 접근 가능하도록 만듭니다(각각의 장치들 클래스는 가장 아래에 전부 공개하겠습니다).
// 홈씨어터 퍼사드 클래스
public class HomeTheaterFacade
{
    public Amplifier Amp { get; set; }
    public Tuner Tuner { get; set; }
    public DvdPlayer Dvd { get; set; }
    public CdPlayer Cd { get; set; }
    public Projector Proj { get; set; }
    public TheaterLights Lights { get; set; }
    public Screen Scr { get; set; }
    public PopcornPopper Popper { get; set; }
    // 메소드 들어갈 자리
            
}
cs

책에서는 필드로 만들었지만 그냥 프로퍼티로 바꾸었습니다.

이 안에 영화를 보는 WatchMovie() 메소드와 끄는 EndMovie()메소드를 작성합니다.
public void WatchMovie(string movie)
{
    Console.WriteLine("get ready to watch a movie...");
    Popper.On();
    Popper.Pop();
    Lights.Dim(10);
    Scr.Down();
    Proj.On();
    Proj.WideScreenMode();
    Amp.On();
    Amp.SetDvd(Dvd);
    Amp.SetSurroundSound();
    Amp.SetVolume(5);
    Dvd.On();
    Dvd.Play(movie);
}
public void EndMovie()
{
    Console.WriteLine("Shutting movie theater down...");
    Popper.Off();
    Lights.On();
    Scr.Up();
    Proj.Off();
    Amp.Off();
    Dvd.Stop();
    Dvd.Eject();
    Dvd.Off();
}
cs

일일히 해야했던 작업들을 하나의 메소드 안에 순차별로 밀어넣어줍니다. 그러나 실상 모든 작업은 서브시스템의 구성요소들이 하고 있는 셈입니다.

이제 테스트를 작성해 보겠습니다.
static void Main(string[] args)
{
    Amplifier amp = new Amplifier("Top-O-Line Amplifier");
    Tuner tuner = new Tuner("Top-O-Line AM/FM Tuner", amp);
    DvdPlayer dvd = new DvdPlayer("Top-O-Line DVD Player", amp);
    CdPlayer cd = new CdPlayer("Top-O-Line CD Player", amp);
    Projector projector = new Projector("Top-O-Line Projector", dvd);
    TheaterLights lights = new TheaterLights("Theater Ceiling Lights");
    Screen screen = new Screen("Theater Screen");
    PopcornPopper popper = new PopcornPopper("Popcorn Popper");
    HomeTheaterFacade homeTheater = new HomeTheaterFacade()
    { Amp = amp, Tuner = tuner, Dvd = dvd, Cd = cd,
        Proj = projector, Scr = screen, Lights = lights, Popper = popper };
    homeTheater.WatchMovie("Raiders of the Lost Ark");
    homeTheater.EndMovie();
}
cs


결과)
get ready to watch a movie...
Popcorn Popper on
Popcorn Popper popping popcorn!
Theater Ceiling Lights dimming to 10%
Theater Screen going down
Top-O-Line Projector on
Top-O-Line Projector in widescreen mode (16x9 aspect ratio)
Top-O-Line Amplifier on
Top-O-Line Amplifier setting DVD player to FacadePattern.Program+DvdPlayer
Top-O-Line Amplifier surround sound on (5 speakers, 1 subwoofer)
Top-O-Line Amplifier setting volume to 5
Top-O-Line DVD Player on
Top-O-Line DVD Player playing "Raiders of the Lost Ark"
Shutting movie theater down...
Popcorn Popper off
Theater Ceiling Lights on
Theater Screen going up
Top-O-Line Projector off
Top-O-Line Amplifier off
Top-O-Line DVD Player stopped "Raiders of the Lost Ark"
Top-O-Line DVD Player eject
Top-O-Line DVD Player off
계속하려면 아무 키나 누르십시오 . . .


여태 공부해온 패턴과 비교할 때, 퍼사드 페턴은 상당히 개념적으로 단순합니다.

  • 디자인 원칙: 정말 친한 친구하고만 얘기하라(최소 지식 원칙).


서브시스템들에 수많은 클래스들이 있지만 결국 클라이언트는 Facade만을 친구로 가집니다. 서로 얽혀 돌아가는 클래스들을 많이 다루면 다룰수록 시스템은 복잡해지고 문제가 생길 가능성이 높습니다.

직접 다루지 않으면서 다른 객체에 영향력을 행사하려면 메소드에서 다음 네 종류의 객체만 호출해야 합니다.


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


그렇다면 어떤 메소드를 호출하여 리턴 받은 객체의 메소드를 호출하는 것은 바람직하지 않습니다. 직접 다루게 될 객체가 늘어나고 의존성이 증가되기 때문입니다.


사용한 전체 코드를 소개하면서 Facade패턴을 마칩니다.



using System;
 
namespace FacadePattern
{
    class Program
    {
        // 서브시스템 클래스들
        // 앰프 클래스
        public class Amplifier
        {
            string description;
            Tuner tuner;
            DvdPlayer dvd;
            CdPlayer cd;
 
            public Amplifier(string description)
            {
                this.description = description;
            }
 
            public void On()
            {
                Console.WriteLine(description + " on");
            }
 
            public void Off()
            {
                Console.WriteLine(description + " off");
            }
 
            public void SetStereoSound()
            {
                Console.WriteLine(description + " stereo mode on");
            }
 
            public void SetSurroundSound()
            {
                Console.WriteLine(description + " surround sound on (5 speakers, 1 subwoofer)");
            }
 
            public void SetVolume(int level)
            {
                Console.WriteLine(description + " setting volume to " + level);
            }
 
            public void SetTuner(Tuner tuner)
            {
                Console.WriteLine(description + " setting tuner to " + dvd);
                this.tuner = tuner;
            }
 
            public void SetDvd(DvdPlayer dvd)
            {
                Console.WriteLine(description + " setting DVD player to " + dvd);
                this.dvd = dvd;
            }
 
            public void SetCd(CdPlayer cd)
            {
                Console.WriteLine(description + " setting CD player to " + cd);
                this.cd = cd;
            }
 
            public string toString()
            {
                return description;
            }
        }
 
        // 씨디플레이어 클래스
        public class CdPlayer
        {
            string description;
            int currentTrack;
            Amplifier amplifier;
            string title;
 
            public CdPlayer(string description, Amplifier amplifier)
            {
                this.description = description;
                this.amplifier = amplifier;
            }
 
            public void On()
            {
                Console.WriteLine(description + " on");
            }
 
            public void Off()
            {
                Console.WriteLine(description + " off");
            }
 
            public void Eject()
            {
                title = null;
                Console.WriteLine(description + " eject");
            }
 
            public void Play(string title)
            {
                this.title = title;
                currentTrack = 0;
                Console.WriteLine(description + " playing \"" + title + "\"");
            }
 
            public void Play(int track)
            {
                if (title == null)
                {
                    Console.WriteLine(description + " can't play track " + currentTrack +
                            ", no cd inserted");
                }
                else
                {
                    currentTrack = track;
                    Console.WriteLine(description + " playing track " + currentTrack);
                }
            }
 
            public void Stop()
            {
                currentTrack = 0;
                Console.WriteLine(description + " stopped");
            }
 
            public void Pause()
            {
                Console.WriteLine(description + " paused \"" + title + "\"");
            }
 
            public string toString()
            {
                return description;
            }
        }
 
 
        // DVD플레이어 클래스
        public class DvdPlayer
        {
            string description;
            int currentTrack;
            Amplifier amplifier;
            string movie;
 
            public DvdPlayer(string description, Amplifier amplifier)
            {
                this.description = description;
                this.amplifier = amplifier;
            }
 
            public void On()
            {
                Console.WriteLine(description + " on");
            }
 
            public void Off()
            {
                Console.WriteLine(description + " off");
            }
 
            public void Eject()
            {
                movie = null;
                Console.WriteLine(description + " eject");
            }
 
            public void Play(string movie)
            {
                this.movie = movie;
                currentTrack = 0;
                Console.WriteLine(description + " playing \"" + movie + "\"");
            }
 
            public void Play(int track)
            {
                if (movie == null)
                {
                    Console.WriteLine(description + " can't play track " + track + " no dvd inserted");
                }
                else
                {
                    currentTrack = track;
                    Console.WriteLine(description + " playing track " + currentTrack + " of \"" + movie + "\"");
                }
            }
 
            public void Stop()
            {
                currentTrack = 0;
                Console.WriteLine(description + " stopped \"" + movie + "\"");
            }
 
            public void Pause()
            {
                Console.WriteLine(description + " paused \"" + movie + "\"");
            }
 
            public void SetTwoChannelAudio()
            {
                Console.WriteLine(description + " set two channel audio");
            }
 
            public void SetSurroundAudio()
            {
                Console.WriteLine(description + " set surround audio");
            }
 
            public string toString()
            {
                return description;
            }
        }
 
        // 팝콘기계 클래스
        public class PopcornPopper
        {
            string description;
 
            public PopcornPopper(string description)
            {
                this.description = description;
            }
 
            public void On()
            {
                Console.WriteLine(description + " on");
            }
 
            public void Off()
            {
                Console.WriteLine(description + " off");
            }
 
            public void Pop()
            {
                Console.WriteLine(description + " popping popcorn!");
            }
 
            public string toString()
            {
                return description;
            }
        }
 
        // 프로젝터 클래스
        public class Projector
        {
            string description;
            DvdPlayer dvdPlayer;
 
            public Projector(string description, DvdPlayer dvdPlayer)
            {
                this.description = description;
                this.dvdPlayer = dvdPlayer;
            }
 
            public void On()
            {
                Console.WriteLine(description + " on");
            }
 
            public void Off()
            {
                Console.WriteLine(description + " off");
            }
 
            public void WideScreenMode()
            {
                Console.WriteLine(description + " in widescreen mode (16x9 aspect ratio)");
            }
 
            public void TvMode()
            {
                Console.WriteLine(description + " in tv mode (4x3 aspect ratio)");
            }
 
            public string toString()
            {
                return description;
            }
        }
 
        // 스크린 클래스
        public class Screen
        {
            string description;
 
            public Screen(string description)
            {
                this.description = description;
            }
 
            public void Up()
            {
                Console.WriteLine(description + " going up");
            }
 
            public void Down()
            {
                Console.WriteLine(description + " going down");
            }
 
            public string toString()
            {
                return description;
            }
        }
 
        // 조명 클래스
        public class TheaterLights
        {
            string description;
 
            public TheaterLights(string description)
            {
                this.description = description;
            }
 
            public void On()
            {
                Console.WriteLine(description + " on");
            }
 
            public void Off()
            {
                Console.WriteLine(description + " off");
            }
 
            public void Dim(int level)
            {
                Console.WriteLine(description + " dimming to " + level + "%");
            }
 
            public string toString()
            {
                return description;
            }
        }
 
        // 튜너 클래스
        public class Tuner
        {
            string description;
            Amplifier amplifier;
            double frequency;
 
            public Tuner(string description, Amplifier amplifier)
            {
                this.description = description;
            }
 
            public void On()
            {
                Console.WriteLine(description + " on");
            }
 
            public void Off()
            {
                Console.WriteLine(description + " off");
            }
 
            public void SetFrequency(double frequency)
            {
                Console.WriteLine(description + " setting frequency to " + frequency);
                this.frequency = frequency;
            }
 
            public void SetAm()
            {
                Console.WriteLine(description + " setting AM mode");
            }
 
            public void SetFm()
            {
                Console.WriteLine(description + " setting FM mode");
            }
 
            public string toString()
            {
                return description;
            }
        }
 
        // 홈씨어터 퍼사드 클래스
        public class HomeTheaterFacade
        {
            public Amplifier Amp { get; set; }
            public Tuner Tuner { get; set; }
            public DvdPlayer Dvd { get; set; }
            public CdPlayer Cd { get; set; }
            public Projector Proj { get; set; }
            public TheaterLights Lights { get; set; }
            public Screen Scr { get; set; }
            public PopcornPopper Popper { get; set; }
 
            public void WatchMovie(string movie)
            {
                Console.WriteLine("get ready to watch a movie...");
                Popper.On();
                Popper.Pop();
                Lights.Dim(10);
                Scr.Down();
                Proj.On();
                Proj.WideScreenMode();
                Amp.On();
                Amp.SetDvd(Dvd);
                Amp.SetSurroundSound();
                Amp.SetVolume(5);
                Dvd.On();
                Dvd.Play(movie);
            }
 
            public void EndMovie()
            {
                Console.WriteLine("Shutting movie theater down...");
                Popper.Off();
                Lights.On();
                Scr.Up();
                Proj.Off();
                Amp.Off();
                Dvd.Stop();
                Dvd.Eject();
                Dvd.Off();
            }
        }
        static void Main(string[] args)
        {
            Amplifier amp = new Amplifier("Top-O-Line Amplifier");
            Tuner tuner = new Tuner("Top-O-Line AM/FM Tuner", amp);
            DvdPlayer dvd = new DvdPlayer("Top-O-Line DVD Player", amp);
            CdPlayer cd = new CdPlayer("Top-O-Line CD Player", amp);
            Projector projector = new Projector("Top-O-Line Projector", dvd);
            TheaterLights lights = new TheaterLights("Theater Ceiling Lights");
            Screen screen = new Screen("Theater Screen");
            PopcornPopper popper = new PopcornPopper("Popcorn Popper");
 
            HomeTheaterFacade homeTheater = new HomeTheaterFacade()
            { Amp = amp, Tuner = tuner, Dvd = dvd, Cd = cd,
                Proj = projector, Scr = screen, Lights = lights, Popper = popper };
 
            homeTheater.WatchMovie("Raiders of the Lost Ark");
            homeTheater.EndMovie();
        }
    }
}
cs

댓글 없음:

댓글 쓰기