728x90

어떤 객체에 대한 접근을 제어하기 위한 용도로 대리인이나 대변인에 해당하는 개체를 제공하는 패턴을 프록시 패턴이라고 한다.


사용하게 되는 곳은 기존 인터페이스의 형태를 변경하지 않고 원형 클래스의 행동을 변경할 때 유용하게 사용된다. 특히 원형 클래스가 서드 파티 라이브러리인 경우 직접적인 변경이 어렵기 때문에 프록시 패턴이 더욱 유용하다.


프록시 패턴의 UML다이어그램은 다음과 같이 그려질 것이다.


아마도 기본적인 Proxy Pattern을 적용하면 이런 형태가 될 것이다.


다른 방법으로 Proxy Pattern을 적용하려면 프록시와 원형 API가 함께 사용하는 추상 인터페이스를 통해 이런 접근 방식을 늘리는 것인데, 이 경우 비록 원형 API를 변경할 상황이 발생한다 해도 두 API를 계속해서 동기화 시킬 수 있다.




이 패턴을 사용했을 때의 장점은 다음과 같다.

 - 원형 객체의 지연된 인스턴스 구현 : 이 경우 원형 객체는 실제로 메서드 호출이 발생하기 전까지 인스턴스화되지 않는다. 

원형 객체의 인스턴스가 많은 메모리를 차지하는 경우라면 지연된 인스턴스 생성이 절대적으로 필요하다.


 - 원경 객체의 접근 제어 구현 : 예를 들어, 올바른 권한을 가진 사용자만이 원형 객체의 특정 메서드를 호출할 수 있는 기능을 구현하기  위해 원형 객체와 프록시 사이에 권한 계층을 두고 접근을 제어한다.


 - 디버그 또는 드라이런 모드 지원 : 프록시 내에서 원형 객체를 호출하는 모든 메서드의 실행 코드를 디버깅하거나, 예를 들어 객체의 상태를 더 이상 저장하지 않도록 행동을 변경하는 것처럼 프록시 내에서 원형 객체의 특정 메서드에 어떤 값을 전달하지 않는 드라이 런 테스트를 진행할 수 있다.


 - 스레드에 안전한 원형 객체 지원 : 뮤텍스 락을 추가시켜 메서드가 스레드에 안전하도록 만들 수 있다.


 - 자원 공유 지원 : 같은 원형 객체를 사용하는 여러 개의 프록시 객체를 사용할 수 있다. 여러 개의 객체가 동일한 데이터를 공유하여 메모리 공간을 최소화 시킨다.


 - 원형 객체의 향후 변경으로부터 보호 : 현재 사용 중인 라이브러리가 향 후 변경될 것을 예상하고 프록시를 생성해서 현재 라이브러리의 동작을 그대로 옮겨 패턴을 구현한다. 그리고 예상대로 라이브러리가 변경이 되면 프록시 객체를 통해 기존의 인터페이스는 그대로 유지하고 새로운 라이브러리의 메서드를 사용하기 위한 구현 부분을 변경하며, 이때는 자연스럽게 어댑터 패턴을 사용한다.

728x90
728x90

어댑터라는 것은 우리 주변에서도 흔히 볼 수 있다. 대표적으로 110V 전용 가전 제품에 220V 어댑터를 끼워 사용하는 것이다.


어댑터를 보면 어댑터 패턴이 무엇인지 대충 감이 올 수도 있겠다.



이 패턴을 사용할만 한 경우를 만들어보면,



"포토뷰어 엔진을 판매하는 회사 A가 있다. 이 A 회사의 엔진이 우리 회사의 포토뷰어 엔진보다 성능이 좋다는 결과를 통보 받았다. 그래서 나는 우리 회사의 포토 뷰어 엔진을 교체 하기 위해 A라는 회사에 엔진을 요청했고 엔진 명세서를 받아봤다."



즉 '나'는 성능의 향상을 위해 포토 뷰어 엔진을 기존의 엔진에서 A 업체가 제공하는 엔진으로 바꿔야만 하는 상황이다.



이제 명세표를 보면 A엔진과 기존 엔진의 구조를 살표보자.


/* 내가 쓰는 엔진 명세 (MyLib) */ class MyLib { public: void printPhoto(String fileName)          { ... } void printPhotoList(String[] listName)    { ... } void deletePhoto(String fileName)        { ... } void addPhoto(String fileName)           { ... } }



/* A 엔진 명세 (ALib) */ class ALib { public:

void printPhoto(String fileName)          { ... } void deletePhoto(String fileName)        { ... } void addPhoto(String fileName)          { ... } }




엔진이 완전히 같다면 좋을텐데 미묘하게 다른 구석이 있다. 차이가 있는 것은 제공 받은 엔진에서 printPhotoList() 가 없다는 것이다. 어쨋든 엔진 교체를 위해 코드 분석에 들어가보자. 아래 코드는 기존 엔진을 사용한 코드이다. 기존 엔진의 이름은 MyLib 이다.


/* 기존 코드 */ int main() { MyLib lib = new MyLib(); String photoList = { "abc.jpg" , "def.jpg" }; lib.printPhotoList(photoList); lib.printPhoto("abc.jpg"); lib.deletePhoto("abc.jpg"); lib.addPhoto("aaa.jpg"); }




자, 이제 함수명이 동일하니 한번 엔진을 바꿔보자.


/* 바뀐 코드 Ver.1 */ int main() { ALib lib = new ALib(); // change String photoList = { "abc.jpg" , "def.jpg" }; for(int i = 0 ; i < photoList.length; i++)    // for문 추가됨

{ lib.printPhoto(photoList[i]); } lib.printPhoto("abc.jpg"); lib.deletePhoto("abc.jpg"); lib.addPhoto("aaa.jpg"); }




이제 프로그램은 잘 돌아갈거라고 생각이 되지만, 기본 로직에 변경이 생겼다. 바로 전에 없었던 for()이 들어가게 된 것이다. 물론 이 코드를 더 이상의 유지 보수를 하지 않을 예정이라면 이대로 놔둬도 상관없겠지만, 프로그램을 만들면 무조건 유지 보수를 해야하기 때문에 이렇게 두는 것은 좋지 않은 방법이다.




따라서 기본 로직을 그대로 이용할 수 있게끔 어댑터 패턴을 사용해보자.


class IPhotoEngine { public:

virtual void printPhoto(String fileName) = 0;     virtual void printPhotoList(String[] listName) = 0;

virtual void deletePhoto(String fileName) = 0; virtual void addPhoto(String fileName) = 0; }




인터페이스 클래스를 만들었다. 이제 어댑터 클래스를 만들어보자.



class ALibAdapter : public IPhotoEngine { private: ALib lib; public: ALibAdapter( ALib lib ) { this.lib = lib; } void printPhoto(String fileName) { lib.printPhoto( fileName ); } void printPhotoList(String[] listName) { for(int i = 0 ; i < listName.length; i++) { lib.printPhoto( listName[i] ); } } void deletePhoto(String fileName) { lib.deletePhoto( fileName ); } void addPhoto(String fileName) { lib.addPhoto( fileName ); } }





이제 이 클래스를 사용해 보자.



/* 바뀐 코드 Ver.2 */
int main()
{
	ALib alib = new ALib();

	PhotoEngine lib = new ALibAdapter( alib );
	String photoList = { "abc.jpg" , "def.jpg" };
	lib.printPhotoList(photoList);
	lib.printPhoto("abc.jpg");
	lib.deletePhoto("abc.jpg");
	lib.addPhoto("aaa.jpg");
}



물론 ALib가 아닌 다른 엔진을 사용할 때에도 마찬가지로 IPhotoEngine 인터페이스를 포함한 어댑터 클래스를 만들기만 하면 언제든 로직을 재사용할 수 있다.


이것이 바로 어댑터 패턴을 사용하는 목적이기도 하다.


만약 기존엔진을 다시 써야하는 상황이 올때를 대비해서 기존 엔진의 어댑터 클래스를 만들어보자.



class MyLibAdapter : public PhotoEngine { private: MyLib lib; public: MyLibAdapter( MyLib lib ) { this.lib = lib; } void printPhoto(String fileName) { lib.printPhoto( fileName ); } void printPhotoList(String[] listName) { lib.printPhotoList( listName ); } void deletePhoto(String fileName) { lib.deletePhoto( fileName ); } void addPhoto(String fileName) { lib.addPhoto( fileName ); } }



그리고 간단하게 기존 엔진을 사용하도록 바꾸면 된다.



/* 바뀐 코드 Ver.3 */
int main()
{
	MyLib myLib = new myLib();
	PhotoEngine lib = new MyLibAdapter( myLib );
	String photoList = { "abc.jpg" , "def.jpg" };
	lib.printPhotoList(photoList);
	lib.printPhoto("abc.jpg");
	lib.deletePhoto("abc.jpg");
	lib.addPhoto("aaa.jpg");
}




출처 : http://jdm.kr/blog/11




728x90
728x90

컴포지트 패턴(Composite Pattern)은 간단하게 말해서 단일 객체(Single Instance)든 객체들의 집합(Group of Instance) 이든 같은 방법으로 취급하는 것이다. 다시 말해, 개별적인 객체들과 집합간의 처리 방법의 차이가 없을 경우 사용하면 된다.


여기서 컴포지트의 의미는 일부 또는 그룹을 표현하는 객체들을 트리 구조(Tree Structures)로 구성한다는 뜻이다.




컴포지트 패턴을 활용은 다음과 같은 곳에서 사용할 수 있다.


파일 시스템을 간단하게 구현한다고 생각했을 때, 먼저 필요한 것은 파일이다. 그래서 파일 클래스를 만들었다.


class File { private: String name; // ... }



그리고 파일들을 가질 수 있는 디렉토리 클래스가 필요할 것이다. 


class Directory { private: String name; List<File> children; // ... public: void add(File file) { // ... } }




디렉토리 클래스는 자신의 이름과 파일들을 가질 수 있다. add() 를 이용해 파일을 추가할 수도 있을 것이다.

근데 이 구조에서 디렉토리 안에 디렉토리가 있는 것을 어떻게 표현해야 할까?




/** INode 클래스는 기본적인 파일 및 디렉토리의 근간이라고 가정합니다. 모든 파일과 디렉토리는 이름을 가지고 있을테니 이름을 반환할 getName() 메소드를 가집니다. */ class INode { public: virtual String getName() = 0; } /** File 클래스는 INode 인터페이스를 구현하면 끝입니다.

자신은 자식 요소를 가질 필요가 없기 때문이죠.

*/ class File : public INode { private: String name; // ... public: virtual String getName()    { return name; } } /** Directory 클래스는 INode 인터페이스를 구현하는 것 외에도

자식 요소를 담아둘 List가 필요합니다. */ class Directory : public INode { private:     String name;     std::list<INode> children; // ... public: virtual String getName()    { return name; } public void add(Node node) { children.add(node); } }



이제 사용을 한다면 다음과 같이 사용하게 될 것이다.



Directory dir = new Directory();
dir.add(new File()); // 디렉토리에 파일 하나를 삽입!
dir.add(new Directory()); // 디렉토리에 디렉토리를 삽입!
Directory secondDir = new Directory();
secondDir.add(dir); // 기존 루트 디렉토리를 새로 만든 디렉토리에 삽입!



간단하지만 트리 구조를 표현할 수 있는 파일 시스템을 만들었다. 이것이 바로 컴포지트 패턴을 이용한 것이다.



실제로 컴포지트 패턴의 UML은 다음과 같다.



Component : 모든 표현할 요소들의 추상적인 인터페이스를 말한다. 위의 예제 코드에서 INode 인터페이스가 이 역할을 

                  담당하고 있었다.


Leaf : Leaf 요소는 Component로 지정된 인터페이스를 구현한 것이다. 위의 예제 코드에서 File 클래스가 이 역할을 담당

        하고 있었다.


Composite : Composite 요소는 Component 요소를 자식으로 가진다. 따라서 Component 요소를 관리하기 위한 메소드들

                을 추가적으로 구현해줘야한다. 위의 예제 코드에서는 Directory 클래스가 이 역할을 담당하고 있었다.




출처 : http://jdm.kr/blog/228

728x90
728x90
C++에서 싱글톤 구현하기
; http://agbird.egloos.com/<wbr />4730538


일단, 위의 글과 함께 댓글들을 읽어보면서 한가지 놀란 점이 있습니다. 의외로 "Thread-Safe"하냐는 것에 대해서는 아무도 관심을 갖고 있지 않았기 때문입니다.


알려진 바에 의하면, 초기화 순서가 불분명하다는 단점에도 불구하고 Singleton 과 같이 구현하는 경우에는 Thread-safe합니다. 하지만, 겉으로 좋아보이는 DynamicSingleton 의 경우에는 Thread-safe하지 않다는 다소 치명적인 단점이 있습니다.


이 문제를 해결하는 것이 생각보다 만만치 않은데 아래의 문서에서 아주 자세히 설명해 주고 있습니다.


C++ and the Perils of Double-Checked Locking
; http://www.aristeia.com/<wbr />Papers/DDJ_Jul_Aug_2004_<wbr />revised.pdf


DynamicSingleton 을 위의 문서대로 thread-safe하게 다시 구현해 볼까요? ^^


우선, 잠금을 통해서 Thread-safe하게 바꿉니다.


class DynamicSingleton {
  private:
  ... [생략] ...
  public:
    static DynamicSingleton* getInstance() 
    {
      Lock lock;
      if (inst == 0) inst = new DynamicSingleton();
      return inst;
    }
};


하지만, 매번 getInstance 때마다 쓰레드 경합이 발생할 여지를 두는 것은 좋지 않기 때문에 다음과 같이 DCLP(Double Chceked Locking Pattern)을 이용해서 개선할 수 있습니다.


static DynamicSingleton* getInstance() 
{
  if (inst == 0)
  {
    Lock lock;
    if (inst == 0) 
    {
      inst = new DynamicSingleton();
    }
    return inst;
  }
}


그래도 여전히 헛점이 있습니다. 명령어 재배열과 같은 최적화 기법에 의해 inst = new DynamicSingleton 이라는 구문에 오류를 발생할 수 있는 여지가 존재하게 됩니다. 즉, 생성자가 불리지 않았음에도 불구하고 inst 변수에 값이 할당되어져 있는 상황이 발생하게 되고 연이은 쓰레드의 호출에서 생성자가 호출되지 않는 상태의 그 inst 변수가 사용되어져 버리는 것입니다. 그래서 다음과 같이 바뀌어야 합니다.


class DynamicSingleton
{
private:
    static volatile DynamicSingleton* volatile inst;

    static volatile DynamicSingleton* volatile getInstance() 
    {
      if (inst == 0)
      {
        Lock lock;
        if (inst == 0) 
        {
          volatile DynamicSingleton* volatile temp = new volatile DynamicSingleton;
          inst = temp;
        }
        return inst;
      }
    }
}


위와 같이 volatile 로 하는 경우 말고도 다중 프로세서에서의 메모리 캐쉬 문제를 해결하기 위해 Memory Barrier를 직접 사용하는 방법을 택하는 것도 가능합니다. 그래서... 다음과 같이 구현해도 됩니다.


#include <intrin.h>
#pragma intrinsic(_ReadWriteBarrier)

class DynamicSingleton
{
private:
    static DynamicSingleton* inst;

    static DynamicSingleton* getInstance() 
    {
      DynamicSingleton* volatile temp = inst;
      _ReadWriteBarrier();
      if (temp == 0)
      {
        Lock lock;
        temp = inst;
        if (temp == 0) 
        {
          temp = new DynamicSingleton;
          _ReadWriteBarrier();
          inst = temp;
        }
        return inst;
      }
    }
}


PhoenixSingleton 은, 바로 위의 코드와 같이 Thread-safe 문제를 어느 정도 해결한 상태에서 살을 붙여야 할테니... "C/C++ 에서 thread-safe 한 Singleton 개체"를 사용하는 것이 그다지 녹록치만은 않습니다. (사실 이것은 C/C++ 만의 문제는 아닙니다.)


위의 PDF 문서에 의하면, (학문적으로 파고들기에는 제가 실력이 모자라서 이해하기 힘들지만) C++의 abstract machine 자체가 단일 쓰레드이기 때문에 위와 같은 소스 코드의 보정에도 불구하고 thread-unsafe한 기계어를 생산하는 C++ 컴파일러도 있다고 합니다. VC++이 그런 경우에 포함되는 것인지는 확인할 길이 없고. (이런 거 보면... 저도 어쩔 수 없는 "응용 개발자"에 속해 있지요. ^^ 그래서 가끔 이런 것들을 학문적으로 파헤치는 분들이 부러울 때가 있습니다.)


그나 저나, abstract machine 이 다중 쓰레드인 언어는 그럼 또 뭐가 있는 건가요?




참고로, "C++에서 싱글톤 구현하기"에서 소개된 코드 중에 "명시적인 해제 작업을 피하기 위해서는 static 지역 객체를 사용하면 됩니다" 라면서 소개한 코드가 있는데요. 그것 역시 Thread-safe 하지 않다는 문제가 있습니다. 이에 대해서는 다음의 글에서 설명되어져 있습니다.


C++ scoped static initialization is not thread-safe, on purpose!
; http://blogs.msdn.com/<wbr />oldnewthing/archive/2004/03/<wbr />08/85901.aspx


재미있게도, scoped-static 초기화가 C++ 표준에 의해서 구현되었기 때문에 thread-safe하지 않은 것이 일단은 당연하다고 되어 있는데 댓글들을 보면 C++ 표준이 아닌 마이크로소프트 임의 구현이라는 비난이 있습니다.

표준이든 아니든, VC++은 일단 그렇게 구현되어 있기 때문에 주의하는 것이 좋겠습니다. ^^
(그런데, 위의 글이 2004년도에 씌여진 것이라서 VC++ 10 에서는 어떻게 바뀌었을지... 모를 일이군요)



끝으로. 한가지 더!

만약 저한테 Thread-safe한 C/C++ 코드를 만들라고 하면.
차라리 주 Thread-safe 개체와 그에 종속된 Thread-safe 한 개체를 만들도록 기본 방침을 정할 것 같습니다. 무슨 소리냐면. 전역적인 static 초기화가 Thread-safe함에도 불구하고 그 순서를 알 수 없다는 이유로 때로 사용이 기피되는데 이것만이 그 이유라면 다음과 같은 식으로 해결할 수 있다는 것입니다.

// ThreadSafeSingleton 개체는 전역 static 으로 등록하고.
class ThreadSafeSingleton
{
   ThreadSafeSingleton()
   {
       // 순서 대로!
       SingletonA()->Initialize();
       SingletonB()->Initialize();
       SingletonC()->Initialize();
   }
}


출처 : http://www.sysnet.pe.kr/

728x90
728x90

팩토리 패턴은 기본적으로 2개로 분류(Factory Method, Abstract Factory) 된다.


팩토리 메소드(Factory Method)는 객체를 생성하되 직접 객체의 생성자를 호출해서 객체를 생성하는 것이 아니라 대행 함수를 통해 간접적으로 객체를 생성하는 방식이다.


쉽게 말하면, 스타크래프트에서 배럭(클래스)에서 유닛(추상클래스)를 통해 마린, 파이어뱃, 메딕 등을 생성하게 된다.


소스코드


unit.h




barracks.h




main.cpp



위와 같이 배럭(클래스)에서는 상황에 따라 마린 또는 메딕등을 생성하지만 무엇이 생성되었는지는 알지 못해도 생성된 유닛을 사용하는데는 문제가 없다. 이렇듯 Factory에서 생성된 객체를 사용자는 종류에 신경쓰지 않고 생성된 객체를 똑같이 사용할 수 있다.




추상 팩토리(Abstract Factory)는 클래스의 객체를 전담 생성하는 클래스를 두지만, 새로운 제품군 생성을 추가하는 것이 쉽도록 클래스 상속을 도입하고 구체적인 제품군별 Factory 클래스 상위에 Abstract Base Class를 정의한 형태의 설계구조를 말한다.


쉽게 말하면 Factory Method의 확장 버전으로 Unit 뿐만이 아니라 Barracks도 Factory Method화 하는 것이다.




아래의 소스 코드에서 ISword는 Unit, ISwordFactory는 Factory의 추상화라고 생각하고 소스 코드를 보면 될 것 같다.


소스 코드


Sword.h




Factory.h




main.cpp




추상 팩토리 패턴에서 팩토리 클래스들의 각 멤버 함수들은 앞에서 언급한 팩토리 메서드(Factory Method) 패턴 형태로 구현되어 있으며 추상팩토리 패턴은 기본적으로 팩토리 메서드 패턴을 활용해서 구현한다.


단점은 새로운 객체가 추가 될때마다 새로운 팩토리 클래스를 정의하고 구현해야 하는 불편함을 초래하는데 이 부분을 해결하려면 객체를 종류별로 팩토리 클래스에 등록해두고 객체 생성 요청시 복제해주는 방식(프로토타입 패턴)을 사용한다.


또한 팩토리 객체를 하나만 두고 싶을 때는 싱글턴 패턴을 사용하면 된다.






출처 : http://blog.naver.com/sorkelf/40169734025http://egloos.zum.com/ani2life/v/2887675

728x90
728x90


옵저버 패턴(Observer Pattern)은 기본적으로 Tool을 만들 때 많이 사용되는 패턴이다.


쉽게 말하면 툴에서 어떤 이벤트를 날렸을 때, 엔진 내부에서는 그 이벤트에 대해 여러가지 동작을 해야 할 것이다.


그 이벤트에 대해 여러가지 동작을 하려면 같은 변수가 여러 개가 들어가게 되거나, 소스 코드에 쓸데 없는 코드가 많이 들어가게 되어 많이 복잡하게 될 것이다.


이러한 문제를 고치기 위해 옵저버 패턴이 만들어졌다.




즉, 옵저버 패턴이란 한 객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체들 모두에게 연락이 가고 자동으로 내용이 갱신되는 패턴 1 : N 의 의존성으로 정의한다.






소스 코드 :




위의 클래스는 Observer Base Class이다.


이 클래스는 기본적으로 시청자의 이름과, 방송을 받는 가상함수만 있다.




위 클래스는 Observer Base Class를 상속 받아서 실제로 사용되는 Observer Class 이다.




위 클래스는 시청자를 관리하는 클래스이다.




위의 코드는 Main() 코드





마지막으로 결과 !!



테스트 예제로 뭘 해볼까 하다가 아래 블로그에서 방송 예제를 보고 작업을 해보았습니다.


출처 : http://copynull.tistory.com/140


ObserverPattern.cpp


728x90
728x90

Private 멤버를 헤더에서 숨기기위한 기법으로 Pimpl Idiom이 있다.

클래스 내부 구조체에 private 멤버들을 넣는 것인데, 헤더에는 구조체를 선언만하고 소스에서 정의를 하는 방법이다.

간단한 코드를 보면 다음과 같다.




구조체의 포인터 이름을 따서 Pimpl Idiom이라고 하는 기법인데, 이런식으로 하면 헤더에서는 private 멤버들을 전혀 알 수 없고, private 멤버들을 수정할 때 헤더는 건드리지 않으므로 이 헤더를 사용하는 다른 파일들까지 다시 컴파일을 하지 않아도 되는 장점이 있다. 


물론 몇가지 단점도 있다. private 멤버들을 사용할 때는 구조체의 포인터를 이용해야 하기 때문에 약간의 오버헤드가 있고, private 멤버들에서 public 멤버를 엑세스하기 위해서는 구조체에 원래 클래스로의 포인터가 필요한 문제점도 있다.


참고 : Exceptional C++ 책

728x90
728x90

우선 파사드의 의미를 사전에서 살펴보면 다음과 같다.

1. <건축> (건물의) 정면, 전면, 파사드

2. <<비유적>> (사물의) 표면, 외관, 겉보기, 허울 (또는 facade)


위의 사전적 의미에서도 알 수 있듯이 파사드 패턴이라 함은 인터페이스를 단훈화시키기 위해서 인터페이스를 변경하는데, 하나 이상의 클래스의 복잡한 인터페이스를 깔끔하면서도, 말쑥한 파사드(겉모양, 외관등)으로 덮어주는 것이다.


파사드 패턴을 다시 한번 정의하자면, 어떤 서브 시스템의 일련의 인터페이스에 대한 통합된 인터페이스를 제공함으로써 서브시스템에서 더 쉽게 사용할 수 있도록 하는 패턴이다.

그림 - 파사드 패턴


홈씨어터를 예로 들어서 설명하자면 다음과 같은 몇가지 작업을 실행해야 된다.

1. 팝콘 기계를 켠다.

2. 팝콘을 튀기기 시작한다.

3. 전등을 어둡게 조절한다.

4. 스크린을 내린다.

5. 프로젝터를 켠다.

6. 프로젝터로 DVD 신호가 입력되도록 한다.

7 프로젝터를 와이드 스크린 모드로 전환한다.

8. 앰프를 켠다.

9. 앰프의 입력을 DVD로 전환한다.

10. 앰프를 서라운드 음향 모드로 전환한다.

11. 앰프 볼륨을 중간으로 설정한다.

12. DVD 플레이를 켠다.

13. DVD 재생한다.


위와 같은 작업을 하기 위해서 사용자가 코드를 한 줄씩 모두 작업을하게 될 것이다.

만약, 이렇게 된다면 문제가 몇가지 존재하게 된다.

1. 홈씨어터의 사용법이 너무 복잡해진다.

2. 영화를 꺼야할 때는 어떻게 처리해야 할지 고민하게 된다.

3. 시스템이 업그레이드 되면 작동 방법을 배워야 한다.


이럴 때 파사드 패턴을 사용해서 복잡한 일을 간단하게 사용할 수 있다.


그림 - CD Player 구현 코드


그림 - Tuner 구현 코드


몇가지 클래스가 더 만들어져야 하지만 글이 너무 길어지기 때문에 생략한 클래스는 (Amplifier, Projecter, TheaterLight, Screen, PopcornPopper) 등이 있을 것이다.


이제 파사드 패턴을 구현한 클래스를 보도록 하자.


그림 - 파사드 패턴 클래스


위와 같이 파사드 패턴은 단순화된 인터페이스를 통해서 서브 시스템을 더 쉽게 사용할 수 있도록 하기 위한 용도로 사용된다.

덕분에 클라이언트에서는 단순한 메소드 하나만을 호출함으로 일하기도 편리하고 수원해질 수 있는 것이다.


728x90

+ Recent posts