[모던 c++ 디자인 패턴] 10-12장

hyng·2023년 4월 24일
0

모던 c++ 디자인 패턴 을 읽고 인상 깊었던 내용을 정리합니다.

퍼사드

퍼사드 패턴은 하나 이상의 복잡한 서브시스템 앞에 단순한 인터페이스를 두기 위한 방법이다.

터미널은 버퍼, 뷰 포트 등 내부적으로 여러 구성요소를 통해 동작하지만 앞 단에 콘솔 인터페이스를 둠으로써 사용자는 터미널을 편리하게 이용할 수 있다.

struct Console
{
	vector<Viewport*> viewports;
	Size charSize, gridSize;
	...
};

플라이웨이트

플라이웨이트 패턴은 많은 수의 가벼운 임시 객체들을 스마트 참조로 사용 하는 것을 말하며 그러한 객체들을 플라이 웨이트라고 부른다.

11.1 사용자 이름

대규모 멀티 플레이가 지원되는 온라인 게임을 생각해보자. 플레이어 중에는 중복되는 이름이 많이 존재할 것이다.

대신 동일한 이름은 한번만 저장하고 메모리 공간을 공유하도록 하면 메모리 절감을 할 수 있을 것이다.

더 나아가서 “성”과 “이름”을 분리하는 방법도 있을 수 있다.

typedef uint32_t key;

struct User
{
	User(cosnt string& first_name, const string& last_name) : first_name{add(first_name}), last_name(add{last_name}) {}
	....
	protected:
		key first_name, last_name;
		static bimap<key, string> names;
		static key seed;
		static key add(const string& s) {
			auto it = names.right.find(s);
			if (it == names.right.end()) {
				// 새로운 이름이므로 추가
				names.insert({++seed, s});
				return seed;
			}
			return it->second;
		}
}

프록시

프록시 패턴 또한 앞서 보았던 데코레이터 패턴과 동일하게 어떤 객체의 기능을 수정/확장한다는 목적에서는 비슷하지만 기존 API의 사용방식을 정확하게 동일하게 혹은 비슷하게 유지하면서 내부 동작만 다르게 한다는 점에서 다르다.

(데코레이터 패턴에서는 Logger 객체를 생성하고 함수를 인자로 넘겨 처리 했었다.)

12.1 스마트 포인터

스마트 포인터는 일반적인 포인터를 사용할 때와 완전히 동일한 방식으로 사용할 수 있다. 즉, 보통의 포인터가 가진 인터페이스를 유지한다.

struct BackAccount 
{
	void deposit(int amount) { ... }
};

BankAccount *ba = new BankAccount;
ba->deposit(123);
auto ba2 = make_shared<BankAccount>();
ba2->deposit(123); // 일반 포인터와 동일하게 사용할 수 있다.

12.3 가상 프록시

nullptr나 초기화되지 않은 포인터를 역 참조하면 크래시가 발생한다.

어떤 경우에는 객체를 생성하되 불필요하게 일찍 자원이 할당되는 것을 원하지 않을 수도 있다. (lazy instantiation)

// 이미지에 대한 인터페이스
struct Image
{
	virtual void draw() = 0;
}

만약 성급한 동작 방식으로 구현한다면 Bitmap을 생성할 때 바로 파일을 로딩 할 것이다.

struct Bitmap : Image
{
	Bitmap(const string& filename) // 생성할 때 이미지 로딩
	{
		cout << "Loading image from" << filename << endl;
	}
	void draw() override // 실제 사용하는 시점
	{
		cout << "Drawing image  " << filenaem << endl;
	}
}

Bitmap img{ "pokemon.png" }; // pokemon.png 이미지 로딩

이제 객체를 생성할 때 말고 실제 사용하는 시점에 이미지를 로딩 하도록 바꿔보자.

그런데 만약 Bitmap 이 외부라이브러리이기 때문에 내부 코드를 변경할 수 없고 여러 이유로 상속을 이용할 수 없다고 해보자.

struct LazyBitmap : Image 
{
	LazyBitmap(const string& filename) : filename( filename ) {}
	~LazyBitmap() { delete bmp; }
	void draw() override // 실제 파일 로딩은 해당 함수가 호출되는 시점에 처리된다.
	{
		if (!bmp)
			bmp = new Bitmap(filename);
		bmp->draw();
	}	
	private:
		Bitmap *bmp {nullptr};
		string filename;
};
	
void draw_image(Image& img)
{
	cout << "About to draw the imgae" << endl;
	img.draw();
	cout << "Done drawing the imgae" << endl;
}

// 사용하는 코드
LazyBitmap img{ "pokemon.png" }
draw_image(image); 

wide characters
wchar_t는 아스키 코드로 표현할 수 없는 국제 문자를 다루기 위한 자료형
wchar_t형의 변수나 배열에 와이드 문자 리터럴을 할당할 때는 접두어 ‘L’을 사용해야 한다.
와이드 문자를 출력할 때는 wcout, wprintf 등의 함수를 사용해야 한다.
https://micropilot.tistory.com/3062

profile
공부하고 알게 된 내용을 기록하는 블로그

0개의 댓글