프로그래밍 일반/디자인 패턴 22

책임 사슬(Chain of Responsibility Pattern)

포인터 사슬 크리처가 이름, 공격력, 방어력을 가지고 있습니다. 게임 플레이 도중 공격력과 방어력이 어떤한 이벤트에 의해 변경이 됩니다. 이를 위해 CreatureModifier를 호출합니다. 이벤트들이 여러번 발생하여 CreatureModifier가 여러번 호출 될 수 있도록 변경 작업을 크리처 별로 쌓아 순서대로 변경 될 수 있도록 합니다. 즉, 연결된 고리의 어느 한 객체가 실제 상황에 적합하다고 판단되면 자신에게 정의된 서비스를 제공합니다. 게임에 크리처들이 있고 이름, 공격력, 방어력 속성을 가지고 있습니다. struct Creature { string name; int attack, defense; Creature(const string& name, const int attack, const ..

프락시 패턴(Proxy Pattern)

프록시 패턴도 데커레이터 패턴처럼 어떤 객체의 기능을 수정/확장한다는 목적이 비슷합니다. 기존 API의 사용 방식을 정확히 동일하게 유지하면서 그 내부 동작만 다르게 한다는 점에서 다릅니다. 같은 API에 대해서 서로 다른 종류의 서로 다른 목적의 완전히 다른 프로시들이 여러 개발자에 의해서 만들어질 수 있습니다. 스마트 포인터 가장 단순하면서도 직접적인 프록시 패턴의 예는 스마트 포인터입니다. 스마트 포인터는 일반적인 포인트를 사용할 때와 완전히 동일한 방식으로 사용할 수 있습니다. 즉, 보통의 포인터가 가진 인터페이스를 유지합니다. 일반 포인터의 인터페이스를 그대로 유지하면서 다른 목적의 기능이 구현되었습니다. 속성 프록시 C++에서 어떤 필드에 필별히 지정된 접근자/변경자를 부여하고 싶다면 속성 프..

플라이웨이트 패턴(Flyweight Pattern)

데이터를 공유하여 메모리를 절약할 수 있게 하는 패턴입니다. 한번 생성된 객체는 또 다시 생성하지 않고 Pool로 관리하여 사용합니다. 플라이웨이트는 원래 권투 용어로 몸무게가 낮은 선수들의 매치를 의미합니다. 플라이웨이트 패턴은 많은 수의 가벼운 임시 객체들을 "스마트 참조"로 사용하는 것을 말하며 그러한 객체들을 플라이웨이트라고 부릅니다. 대단히 많은 수의 매우 비슷한 객체들이 사용되어야 할 때 메모리 사용량을 절감하는 방법으로 자주 사용됩니다. 사용자 이름 수 많은 유저의 이름을 저장하는 겨우를 생각해봅시다. 유저의 이름이 많이 겹친다면 이를 일일이 모두 메모리에 저장하면 메모리를 많이 사용할 것입니다. 이름 키값을 이용하여 중복된 이름을 함께 참조한다면 메모리 사용량을 줄일 수 있습니다. type..

퍼사드 패턴(Façade Pattern)

Façade는 프랑스어로 "출입구가 있는 건물의 앞부분을 의미합니다" 즉, 복잡한 부분은 뒤로 숨기고 간략화된 인터페이스를 제공하는 패턴입니다. 퍼사드 디자인 패턴은 하나 이상의 복잡한 서브 시스템 앞에 단순한 인터페이스를 두기 위한 방법입니다. 퍼사드는 편리하고 직관적인 API를 통해 이용할 수 있게합니다. class Engine { public: void Start() { ... } }; class Headlights { public: void TurnOn() { ... } }; // facade class Car { public: void Start() { headlights.TurnOn(); engine.Start(); } private: Engine engine; Headlights headli..

데커레이터 패턴(Decorator Pattern)

동료가 작성한 코드의 원본을 수정하지 않고 기능을 확장하는 방법은? 가장 쉽게 생각나는 것이 "상속"을 이용하는 것입니다. 데커레이터 패턴은 이미 존재하는 타입에 새로운 기능을 추가하면서도 원래 타입의 코드에 수정을 피할 수 있게 해줍니다. (OCP 준수) Shape을 상속 받아 ColoredShape과 TransparentShape을 추가하고 두 기능이 모두 필요하여 ColoredTransparentShape을 만들었습니다. 이렇게 상속의 방법으로 기능을 추가 한다면 Shape의 기능이 들어 나거나 도형이 추가된다면 클래스가 점점 수도 없이 늘어나야만 합니다. Square, Circle 등과 같은 도형추가 되거나 크기 조절 가능 기능을 추가되는 것을 상상해 봅시다. 평범한 상속으로는 효율적으로 원본을 ..

컴포지트 패턴(Composite Pattern)

컴포지트 패턴은 어떤 객체들의 집합에 대해 개별 인터페이스를 동일하게 가져갈 수 있게하는 것입니다. 배열에 기반한 속성 크리처의 속성을 배열로 구현하면 작성하기 쉽고 유지 보수 하기도 쉽다. 힘, 민첩, 지능을 가지는 크리쳐의 능력 들의 통계를 내는 구현입니다. struct Creature { int strength, agility, intelligence; int sum() const { return strength + agility + intelligence; } double average() const { return sum()/3.0; } int max() const { return max(max(strength, agility), intelligence); } } 크리처의 속성이 늘어 날때 마다..

브릿지 패턴(Bridge Pattern)

기능 클래스(껍데기)와 구현 클래스(실제 구현)를 서로 연결하는 패턴입니다. Bridge하면 무엇이 떠오르나요? 브릿지가 서로 떨어진 두 곳을 연결 하듯이 브릿지 패턴 또한 서로 떨어진 두 기능을 연결합니다. 이것이 핵심입니다. 즉, 기능과 구현을 서로 연결하는 것입니다. Pimpl(Pointer to Implementation) 관례 구현부를 포인터로 참조하는 관례를 뜻한다. 클래스의 구현부를 다른 클래스(PersonImpl)에 숨깁니다. PersonImpl 구현이 모두 .cpp 파일에 정의하는 것이 핵심입니다. Person.h struct Person { std::string name; class PersonImpl; // 전방 선언 PersonImpl *impl; // 구현은 모두 cpp 파일에서 ..

어댑터 패턴(Adapter Pattern)

인터페이스가 용도에 맞지 않을 때 용도에 맞도록 중간에서 변환한다. 6.2 어댑터 일반적인 어댑터의 예입니다. Line을 Point들로 변환하는 어댑터를 설명합니다. Point들을 그리는 기능만 있는데 Line을 그리려면 어떻게 해야 하나? void DrawPoints(CPaintDC& dc, std::vector::iterator start, std::vector::iterator end) 다음과 같이 라인을 점들로 변환하는 어댑터를 구현합니다. 이 구현은 수직, 수평인 라인들만을 다루고 나머지는 무시합니다. struct LineToPointAdapter { typedef vector Points; LineToPointAdapter(Line& line) { int left = min(line.start..

싱글턴 패턴(Singleton Pattern)

인스턴스의 포인터를 전역으로 접근하면서 오직 하나의 인스턴스만을 생성하는 것을 보장하는 패턴입니다. 역사상 가장 많이 미움을 받고 있는 디자인 패턴입니다. 패턴을 통틀어 구조적으로 가장 간단한 패턴입니다. 전역변수와 같은 역할을 하지만 호출 될때 객체를 만들 수 있어서 필요 할때만 만들 수 있습니다. 한 클래스의 인스턴스가 하나만 생기도록 하는 구현은 생각보다 까다롭다. 전역 객체는 초기화 순서가 정의 되어 있지 않습니다. 전역 객체가 다른 전역 객체를 참조 한다면 문제가 발생합니다. static Database database{}; 이 함수는 스레드 안전성이 C++11 이상 버전에서만 보증됩니다. Database& get_datatabase() { static Database database; retu..

프로토타입 패턴(Prototype Pattern)

이미 만들어진 객체(프로토타입, 원형)을 복사하여 새로운 객체를 생성하는 패턴입니다. 무에서 유를 창조하는 것은 드물다. 이미 존재하는 객체를 복제하여 사용하거나 일부를 수정하여 사용합니다. 이러한 것들이 프로토타입 패턴의 아이디어입니다. 프로토타입 패턴은 객체의 깊은 복제를 수행하되 매번 전체 초기화를 하는 대신 미리 부분적으로 만들어진 객체를 복제하여 약간의 수정만으로 이용할 수 있게합니다. 이 과정에서 원본 객체에 대한 걱정은 하지 않아도 되게 해줍니다. 객체 생성 복잡한 생성을 거쳐 잘 갖추어진 객체가 있다면 이 객체를 그대로 복제하는 것이 좋은 방법입니다. 프로토타입 패턴은 객체의 복제가 주요 기능이다. 복제를 하는 여러 방법들이 알아봅시다. 평범한 중복 처리 복제 대상 객체의 모든 항목이 값으..