이론/디자인패턴

디자인 패턴 - 구조

tsyang 2021. 12. 3. 14:51

개요

구조 패턴에는 다음의 패턴이 포함된다.

  • Adapter
  • Proxy
  • Facade
  • Decorator
  • Bridge
  • Flyweight
  • Composite

 

이 글에서는 위 패턴들을 '간단하게' 다룬다.

 


어답터 패턴


다른 인터페이스에 adapter 클래스를 만들어서 사용하고자 하는 인터페이스와 호환되도록 하는 것이다.

 

 

위 다이어그램처럼 외부에서 가져온 ForeignCat은 cry()메서드가 없지만 어댑터를 통해 기존 인터페이스와 호환되게 동작하도록 만들어줄 수 있다.

 


 

프록시 패턴


클래스에 접근할 때 프록시 클래스를 통해 접근하는 것. 프록시 클래스에서는 통계나 로그 캐싱등을 해줄 수 있다.

 

class Animal
{
public:
	virtual void speak() = 0;
};

class Cat final : public Animal
{
public:
	void speak() override
	{
		//짖는다.
	}
};

class CatProxy final : public Animal
{
public:
	CatProxy(Cat* cat)
	{
		cat_ = cat;
	}

	void speak() override
	{
		Log("Cat Speaks");
		cat_->speak();
	}
private :
	Cat* cat_;
};

프록시 클래스는 대상 클래스와 인터페이스가 같아야 한다. 따라서 프록시 클래스와 대상 클래스는 상속 관계에 있는 경우가 많다.

 


퍼사드 패턴


여러 클래스나 라이브러리를 조합해서 단순한 형태의 인터페이스를 제공해 주는 것.

 

예를 들어, 로켓을 발사한다고 하면 굉장히 많은 클래스들이 필요할 것이다. 1단계 로켓, 2단계 로켓부터 각종 시스템과 안전장치와 관련된 함수들을 적절히 호출해줘야 할 탠데 클라이언트 입장해서는 그냥 launch() 메서드 하나로 호출하고 싶을 때 Rocket 클래스를 만들어 launch()메서드에 일련의 과정들을 수행해주면 된다. 

 

어댑터 패턴과 비슷할 수 있는데, 퍼사드 패턴은 인터페이스나 다형성을 고려하지 않고 단순한 인터페이스를 제공하고자 할 때 쓸 수 있다.

 


데코레이터 패턴


패턴 이름 그대로 어떤 클래스에 기능을 덧대어주고 싶을 때 사용할 수 있다. 만약 기능 확장이 필요할 때 서브클래스를 만드는 대신 쓸 수 있다.

 

예를 들어, draw()함수를 호출하여 UI를 그릴 수 있는 SimpleWindow 클래스가 존재한다. 그런데 여기에 reCAPTCHA를 추가해주고 싶다면?

 

class SimpleWindow : public Window
{
public :
	void draw() override
	{
		cout << "draw Window" << endl;
		//draw window;
	}
};

class Decorator : public Window
{
public :
	Decorator(Window * window)
	{
		window_ = window;
	}

	void draw() override
	{
		window_->draw();
	}

private:
	Window * window_;
};

class ReCHAPCHADecorator : public Decorator
{
public :
	ReCHAPCHADecorator(Window * window) : Decorator(window) {}
	void draw() override
	{
		Decorator::draw();
		cout << "I'm not a robot" << endl;
	}
};

int main()
{
	SimpleWindow * someWindow = new SimpleWindow();
	auto newWindow = new ReCHAPCHADecorator(someWindow);
	newWindow->draw();
	
	//delete windows..
	return 0;
}

 

 

 


브릿지 패턴


브리지 패턴은 abstraction과 implementation을 분리하여 각각 독립적으로 만들어주는 패턴이다. 이때 클래스 자체를 abstraction으로 클래스가 할 수 있는 일을 implementor가 한다고 생각하면 편한다. 

 

https://en.wikipedia.org/wiki/Bridge_pattern

 

위 UML 다이어그램을 보면 알겠지만 Abstraction에서 Implementor를 필드로 가진다. 

 

사용 예) 도형클래스(Shape)를 Abstraction, 그리기 클래스(Drawer)를 Implementor라 놓고 구현하면 이 둘의 SRP를 잘 지켜주면서 분리가 가능함.


플라이웨이트 패턴


경량패턴 이라고도 한다. 경량패턴을 사용하면 공유객체를 통해 객체의 경량화를 이룰 수 있다. 

 

나무가 100개 있는 숲을 그려야 한다고 하자. 나무 클래스는 다음과 같을 것이다.

 

class Tree
{
private:
	Texture * texture_;
	Mesh * mesh_;
	int pos_x_, pos_y_, pos_z_;
	float rotation_;
};

 

이 때 Tree 객체가 100개일 때, 텍스쳐나 메시 객체도 100개라면 메모리를 꽤 잡아먹을 것이다. 더욱이 GPU메모리로 데이터를 옮기는 과정 역시 오래 걸릴 것이다. 이때 하나의 텍스처나 메시를 100개의 Tree객체가 공유한다면 메모리 절약과 성능에서 이점을 취할 수 있다.


컴포짓 패턴


컴포짓 패턴은 한 오브젝트의 그룹과 그 그룹의 단일 인스턴스를 같은 타입으로 취급하는 것이다. 

 

https://en.wikipedia.org/wiki/Composite_pattern

 


class Component
{
public:
	virtual void operation() = 0;
};

class Leaf : Component
{
public:
	void operation() override
	{
		//do something
	}
};

class Composite : Component
{
public:
	void operation() override
	{
		for (auto child : childs_)
			child->operation();
	}
private :
	std::vector<Component*> childs_;
};

 

이런 패턴은 파일 디렉토리와 같은 개념에 잘 어울린다. 폴더(Composite)과 파일(Leaf)를 같은 컴포넌트로 취급함으로써 파일 디렉토리의 트리 구조를 쉽게 순회할 수 있다.

'이론 > 디자인패턴' 카테고리의 다른 글

서비스 중개자  (0) 2022.01.16
이벤트 큐 패턴  (1) 2022.01.03
컴포넌트 패턴  (1) 2021.12.25
디자인 패턴 - 행동패턴  (0) 2021.12.12
디자인 패턴 - 생성 패턴  (0) 2021.11.27