본문 바로가기

개발/디자인패턴

객체 생성 관련 디자인패턴

장세찬, 『GoF 디자인 패턴! 이렇게 활용한다. : C++로 배우는 패턴의 이해와 활용』, 한빛미디어(2004)

책의 내용을 배끼기보단 제가 이해한 내용을 위주로 작성해서 틀린 부분이 있을 수 있습니다!

 


1. Abstract Factory 패턴

책의 시작부터 좀 어려운 내용이 나와서 힘들었던 부분입니다ㅠㅠ
저는 다형성(Polymorphism)을 활용한 디자인 패턴으로 이해했는데요, 추상 클래스를 상속받는 클래스들을 생성하는 역할의 객체 혹은 함수를 두는 개념입니다.
제가 쓰고도 못알아듣겠으니 그림이 필요하겠네요!

참고 문헌 p84. Abstract Factory 패턴의 일반적인 클래스 구조

AbstractProductA, AbstractProductB가 있고, 사용자가 접근할 수 있지만 객체 생성은 AbstractFactory가 합니다.
이렇게 사용했을 때, client는 객체 생성 코드만 중복으로 사용하고 다른 기능 productA, productB 모두 동일하게 사용할 수 있다는 점입니다. 이 말 또한 저도 못알아들을 말이니 이번엔 코드가 필요할 것 같네요.

class A {
	virtual void func1() = 0;
	virtual void func2() = 0;
};
class A1 : public A {
	//func1, func2 구현
};
class A2 : public A {
	//func1, func2 구현
};

class CreateFactory {
	virtual static bool createProduct() = 0;
};
class CreateFactoryA1 {
	static bool createProduct();
};
class CreateFactoryA2 {
	static bool createProduct();
};
int main( int argc, const char **argv) {
	A val;
    
	if ( argv[1] == "1" ) {
		val = CreateFactoryA1::createProduct();
	} else if ( argv[1] == "2" ) {
		val = CreateFactoryA2::createProduct();
	}
    
	val.func1();
	val.func2();
    
	return 0;
}

main함수에서는 입력받은 인자값에 따라 val 객체를 생성하고 있죠. 생성한 뒤에는 A1이던 A2이던 상관없이 func1과 func2를 사용할 수 있습니다.
그래서 저는 Abstract Factory 패턴을 다형성을 이용해 코드 중복을 줄이는(단순히 중복을 줄이는게 엄청난 효과를 볼 수 있습니다.) 패턴이라고 말하고 싶네요!

2. Builder 패턴

 "객체 생성"이라는 말만 들으면 객체는 한번에 생성되는 것 처럼 들리지만, 사실 객체는 부분부분 조금씩 만들어질 때가 있죠. 이런 경우에 객체에서 사용해야 할 부분만 먼저 생성하고, 다른 부분은 나중에 사용할 때 생성하는 패턴을 Builder 패턴이라고 합니다.

class A {
public:
	virtual void funcA() = 0;
	virtual void funcB() = 0;
	virtual void funcC() = 0;
};

class A_a : public A {
	// funcA, funcB, funcC 구현
};

class A_b : public B {
	// funcA, funcB, funcC 구현
}

책에서는 위 코드처럼 상속 코드를 예로 들었습니다.
A_a클래스의 기능과 A_b 클래스의 기능을 각각 생성해 A라는 클래스의 객체를 부분부분 나눠서 생성하는 것이죠.
사실 상속받은 코드로 설명해서 이해하기 조금 힘들었는데, 검색해보니 그냥 setter함수도 Builder 패턴 구현 방식의 일종이라고 하더라고요.

class A {
public:
	void setI(int i) {
		m_i = i;
	}
	void setJ(int j) { 
		m_j = j;
	}
    
private:
	int m_i;
	int m_j;
}

이런 경우도 객체의 멤버변수인 i와 j를 따로 설정하게 되니 객체를 부분부분 설정하는 Builder 패턴에 속하는거죠!
즉, 생성자로 한번에 객체를 만드는 경우가 아니라면 모두 Builder 패턴에 해당됩니다.

3. Factory Method 패턴

Factory Method와 Abstract Factory 패턴이 상당히 유사해서 이해하기가 좀 힘들었어요.
그러나 끝내 이해한 Factory Method는 Abstract Factory 패턴과 확실히 다른 점이 있었습니다.

Abstract Factory 패턴은 클래스를 생성하는 creater 클래스를 생성해 사용하는 패턴입니다.
하지만 createrA, createrB, createrC 세 클래스의 createProduct() 함수가 Product의 종류를 제외하고 모두 동일할 경우, 중복 코드가 생기는 문제가 생기죠.
그래서 creater 클래스를 하나로 합치고 createProduct() 함수도 하나만 사용하는 것이 Factory Method 패턴입니다.

class A {
	virtual void func1() = 0;
	virtual void func2() = 0;
};
class A1 : public A {
	//func1, func2 구현
};
class A2 : public A {
	//func1, func2 구현
};

class CreateFactory {
	static bool createProduct(int type) {
		A val;
		// 선처리
		switch(type) {
		case 1:
			val = new A1();
		case 2:
			val = new A2();
		}
		// 후처리
	}
};

int main( int argc, const char **argv) {
	createFactory::createProduct(argv[1]);    
	return 0;
}

선,후처리가 모두 동일하다면 creater를 굳이 나눌 필요는 없겠죠?
이렇게 한 함수에서 동일한 선처리, 후처리를 해주고 객체 생성만 구분해주는 Factory Method 패턴입니다!

4. Prototype 패턴

Prototype 패턴은 좀 더 간단한 패턴이예요. 위 패턴들이 객체를 "생성"하는 패턴들이라면, 이번엔 미리 생성한 객체를 "복사"해 사용하는 패턴입니다.
여기서 중요한 것은, 복사할 객체에 "포인터 멤버변수"가 있다면 복사 생성자를 만들어야 한다는 점입니다.

class A {
	A(){
		i = new int();
		*i = 1;
	}
	A(const &A val) {
		i = new int();
		*i = *val.i;
	}
	virtual ~A();
public:
	int *i;
}

복사생성자 ( A(const &A val) )가 없으면 객체 복사 시, 변수 i에는 1로 초기화 한 값은 제외하고포인터 값만 들어간다고 해요.

복사생성자 없이 수행한 객체 복사를 얕은 복사(shallow copy),
복사생성자를 통해 멤버변수를 모두 복사하는 것을 깊은 복사(deep copy)라고 합니다!

5. Singleton 패턴

아무데서나 아무렇게나 객체를 생성하면 안되는 경우가 있죠. 
이런 경우엔 객체 생성자 자체를 숨겨두는 방법이 있습니다.
생성자를 private으로 두고, public으로 사용하는 함수에서 객체 생성에 제한을 걸어두는 Singleton 패턴입니다.

class A {
public:
	bool createA() {
		/* 조건 확인 */
		// 조건에 맞지 않는 경우
		return false;
        
		/* 객체 생성 */
		return true;
    }

private:
	A();
	virtual ~A();
};

이렇게 되면 다른 코드에서 실수로라도 A객체를 만들려고 해도 만들 수 없겠죠.

** 사용 사례 **

1) 정책 객체
    - 정책 객체는 프로그램 동작 결과에 치명적인 영향을 주기 때문에 객체 생성을 Singleton으로 관리하는 것이 좋다
    - 다른 public 함수는 조회, 검사 등 객체를 수정하지 않는 범위에서 수행

2) 객체 수 제한이 있는 경우
    - 객체를 제한된 수 안에서 만들어야 할 경우 ( ex> 게임 유닛 객체)
    - 다른 코드에서 객체를 생성해 제한된 수를 넘기지 않도록 Singleton 객체 생성 함수 내에서 현재 생성한 객체 수를 조절한다.

3) 객체 생성 시 fork, thread를 사용하는 경우
    - 객체 생성이 프로그램 퍼포먼스에 영향을 줄 수 있으니 객체 생성 시 제한, 혹은 확인 과정을 추가해 생성하도록 한다.

 

 

'개발 > 디자인패턴' 카테고리의 다른 글

구조 개선을 위한 디자인패턴(2)  (1) 2020.12.10
구조 개선을 위한 디자인패턴  (0) 2020.12.10