싱글턴화된 매니저 클래스의 상속

프로그래밍 일반에 관한 포럼입니다.

Moderator: 류광

Locked
심 형근
Posts: 526
Joined: 2002-08-19 23:30

싱글턴화된 매니저 클래스의 상속

Post by 심 형근 »

Global 하고 Unique 한 객체를 만들기 위해서 싱글턴이라는것을 사용합니다.
싱글턴을 사용할줄 모른다면 전역변수 하나 선언하고 주석이나 문서에

"이 클래스는 반드시 전역변수 g_GlobalUniqueManager 을 통해서 접근하세요"
라고 적어놓으면 될것 같습니다.

문제는 전역변수이든, 싱글턴이든 한번 Global Unique 하게 되면
상속을 통한 클래스 재활용이 힘들다는 것이 문제입니다.

특정 관리자 클래스를 싱글턴으로 사용하는경우가 흔한데,
이럴경우 더이상 상속이 불가능한, 사실상 상속하느니 차라리 처음부터 재작성하는것이
나을법한 코드가 됩니다.

문제는 처음부터 재작성할경우 관리자와 연계된 수많은 코드를 다시 다 작성해야하는
난감한 사태가 발생하게 됩니다.

싱글턴으로 선언된 클래스는 상속이외의 방법으로 재활용 해야하는 것일까요?
아니면 상속이 필요없을만큼 더이상 상속이 필요 없을만큼의 단계에서
싱글턴을 사용해야 할까요? 어떻게 하는게 나을까요?
airshark
Posts: 199
Joined: 2006-07-16 22:49

안녕하세요~!

Post by airshark »

역시 심형근님의 질문은 뭔가 다른..^^

전제 조건은 이거네요.

싱글톤 패턴을 사용하여 유일성을 보장할 필요가 있다.
기능적 측면에서 어떠한 방법을 사용하여 다양화할 필요가 있다.

제 생각에는 이런 경우라면, 상속보다는 템플릿을 활용하는 것이 어떨까합니다. 어차피 싱글톤 패턴도 템플릿으로 구현할 수 있고, 기능적 측면은 유닛화를 통해 잘 쪼개 놓으면, 어떠한 것을 조합하느냐에 따라서 다양한 기능을 낼 수 있을 것 같네요.
안녕하세요 삽질 전문 Airshark입니다.
myevan
Posts: 1314
Joined: 2003-03-04 10:21
Contact:

Post by myevan »

Manager 라는 싱글턴 클래스가 있을때

Code: Select all

class Manager
{
public:
	static Manager& GetManager();

	void Func1(int a);

	virtual void Print();
	
protected:
	Manager();

protected:
	int m_a;
};

void Test1()
{
	Manager::GetManager().Func1(1);
	Manager::GetManager().Print();
}
Test1() 의 코드를 변경하지 않고 Manager 기능과 속성은 모두 유지한채
확장 기능이 포함된 SuperManager 를 만드시고 싶으신걸로 생각됩니다.

아래같은 방법은 어떠실까 합니다.

1. 싱글턴 변수를 함수내 static 변수에서 멤버 static 포인터형 변수로 변경

Code: Select all

class Manager
{
public:
	static Manager& GetManager();

	void Func1(int a);

	virtual void Print();
	
protected:
	Manager();

protected:
	int m_a;

//////////////////////////////////////////
private:
	static Manager* ms_singleton;
//////////////////////////////////////////
};

void Test1()
{
	Manager::GetManager().Func1(1);
	Manager::GetManager().Print();
}
2. 생성자에서 멤버 static 변수에 자신 등록

Code: Select all

Manager* Manager::ms_singleton = NULL;

Manager& Manager::GetManager()
{
	assert(ms_singleton != NULL);
	return *ms_singleton;
}
3. SuperManager 클래스를 Manager 에 상속 받아 제작

Code: Select all

class SuperManager : public Manager
{
public:
	static SuperManager& GetSuperManager();

	void Init();

	void Func2(int b);
	virtual void Print();
	
protected:
	SuperManager();

private:
	int m_b;
};

void Test2()
{
	SuperManager::GetSuperManager().Func2(2);
	SuperManager::GetSuperManager().Print();
}
4. SuperManager 싱글턴 변수는 함수 static 변수 사용

Code: Select all

SuperManager& SuperManager::GetSuperManager()
{
	static SuperManager s_mgr;
	return s_mgr;
}
5. 프로그램 시작시 SuperManager 생성

Code: Select all

int main()
{
	SuperManager::GetSuperManager().Init();
	...
}
이런 방법을 사용하면 Manager 를 사용한 Test1 은 전혀 수정하지 않고 SuperManager 클래스를 사용할 수 있습니다.

새로운 기능을 사용하려면
- Manager::GetManager() 인터페이스 대신
- SuperManager::GetSuperManager() 인터페이스를
사용하는 것이죠 ~(-_-)~
빗자루네 http://www.myevan.net >_<b
BurnHead3D
Posts: 50
Joined: 2007-01-18 16:24

!

Post by BurnHead3D »

유일한 객체로만 제한할때의 경우나 N개 제한이더라도

어떤 사례인지 좀 알려주세요,

static 멤버변수로 만들어서 static하게 상속받는 방법이 있고,

상속받은 클래스::createInstance() 식으로 해서 포인터를 멤버에 담는것도 생각해볼수 있겠네요

new singleton; 이런 유령 생성도 있고...

그런쪽보다는 저같은 경우에는 자료구조에 기능을 넣다보니,

인터페이스에의한 호출에서 성격이 조금 다른 객체를 관리하는 경우에

줄코딩으로 이어지는 경우가 많더군요.

때로는 접근포인터나 void포인트를 인자값으로 받는 함수같은 위험한 코드나 만능코드를 첨부할 때도 있습니다.

싱글톤 관리자-인스턴스생성과 인스턴스 관리자를 분리해서

인스턴스 관리자를 상속받아서 쓰는 방법이 좋지 않을 까 생각합니다.
kedu
Posts: 39
Joined: 2008-01-15 11:45

Post by kedu »

싱글턴으로 지정된 클래스를 상속받아 사용한다면 해당 클래스의 상속된 클래스만을 사용해야 할 것 같습니다.

예를 들어 렌더러를 싱글턴으로 만들고 이것을 DX렌더러, OpenGL렌더러 이렇게 만든 다음, 실제 코드에서 사용할때에는 DX와 OpenGL 중 하나만 택일해서 런타임시 사용하게 될 듯 합니다. 물론 구현은 각각으로 하고요.

만약 A관리자와 B관리자가 하나의 싱글턴을 상속받는다면 이것 역시 런타임에서 사용할때에는 A와 B중 택일해야 활 것 같습니다.

그렇지 않고 A관리자, B관리자를 동시에 사용해야 한다면, A관리자 B관리자는 각각의 싱글턴을 상속받아야 할거 같고요.

뭐 개인적으로 소스 구조를 깔끔하게 만드는 거에는 자신도 없고 관심도 적은 편이긴 합니다만, 약간의 원칙 정도만 세워놓고 있습니다.
mastercho
Posts: 587
Joined: 2004-05-09 20:37

Post by mastercho »

A 라는 관리자가 있고 A를 상속 받은 B 관리자를 만들고 싶은거라면

애초에 A 관리자의 구현을 따로 때어 Q 라는 구현 클래스를 만드는게
좋지 않을까합니다

따라서 A 관리자는 Q라는 클래스는 포함관계로 (혹은 private 상속)
가지고 B또한 Q라는 클래스를 포함 관계로 가지면 되지 않을까요?

단지 Q의 기능을 확장하는거라면 Q를 상속받아 W를 만든뒤에
B에서 W를 포함하면 될테고요
아니라면 그냥 Q를 포함 하면 될테고


구현과 인터페이스 클래스를 분리하면
상당히 자유로와 질거 같다는 느낌이 듭니다
심 형근
Posts: 526
Joined: 2002-08-19 23:30

클래스 의존관계

Post by 심 형근 »

코딩자체가 클래스의 상속관계만을 생각한다면, 문제가 어떻게든 해결될수 있지만,
싱글턴 매니저 클래스를 쓰는 이유를 생각하면 사태는 생각보다 어려워집니다.

A,B,C,D,E,F 클래스가 있는데, 서로 상호조회를 해야한다면???

전역변수 및 싱글턴을 쓰지 않는다면

A 클래스는 B,C,D,E,F 클래스의 인접포인터를 가지고 있어야 하고.
B 클래스는 A,C,D,E,F 클래스의 인접 포인터를 가지고 있어야 하고.
C 클래스는 A,B,D,E,F 클래스의 인접포인터를 가지고 있어야 합니다.

문제는 A 부터 Z 까지의 클래스가 있는데, 서로 전역변수 및 싱글턴을 쓰지않고
상호조회를 한다면????

26*25 만큼의 상호조회 경우의 수가 나오는데, 이는 사실상 코딩을 포기하는 행동입니다.

결국 Global Unique 한 관리자 클래스를 통해서 접근하는것이 정석적인 해답인데,
문제는 서로다른 클래스에 서로 조회를 하기 때문에, 함부로 바꿀수가 없다는 점입니다.

A,B,C,D,E,F 클래스가 있는데, A-Manager 클래스가 변경되면,
B,C,D,E,F 클래스가 SOS~~ 를 날리게 되죠.

결국 코드의 커플링을 줄여서 상호의존도가 낮은 설계를 하면 될듯 싶지만,
좋은 설계를 통해서 문제를 해결하는것은 언제나 "BEST" 한 상황에서만 가능한 부분일듯 싶기도 하고요.

"NORMAL" 상황에서는 쉽게 추가할수 있는 코드가 어떻게 유지보수되는지가 중요한 문제입니다.
클래스간 의존도는 기획사항과도 연관이 있기 때문에, 프로그래머의 의지만으로는 쉽지가 않기도 합니다.
myevan
Posts: 1314
Joined: 2003-03-04 10:21
Contact:

Re: 클래스 의존관계

Post by myevan »

심 형근 wrote: 26*25 만큼의 상호조회 경우의 수
전임자가 정말 만들고 싶은데로 만들었다면 발생가능한 상황일수는 있습니다.

하지만 유지보수하는 입장에서 바라보면
어차피 키보드는 하나뿐이기 때문에 특정 시점 손봐야하는 클래스는 하나입니다.
그럼 연관되는 숫자 대폭 줄어듭니다.

체크사항1. A 클래스를 손볼때 영향 받는 클래스는 B, C, D, E, F 뿐입니다.

26*25 만큼의 상호참조가 나오는건 이성적인 사고로는 나오기 불가능합니다.
본능대로 작성했기 때문에 그런 막나가는 코드가 나오는것이죠.

-> 이성적인 사고로 접근하면 상호참조를 대폭적으로 줄일수 있습니다.

체크사항2. 클래스간에 사용하는 연관관계(호출메소드)를 살펴보면 호출되는 메소드는 몇개정도로 한정됩니다.
(엄청 많다면 그것은 코드위치가 잘못된것이니 옮겨야겠죠)

여기까지 진행되면 참조관계는 A-B, A-C, A-D, A-E, A-F 로 나눌수 있습니다.
1:1 단위로 해결하는 것이 리팩토링의 시작입니다.

B 에서는 A 단일체(singleton)를 직접 참조하는 대신 AInterface_ForB 라는 단일체(singleton)를 참조하도록 하고
A 클래스는 AInterface_ForB 를 상속받게 수정합니다.

Code: Select all

class A : public AInterface_ForB
{
...
}
코드가 잘돌아가는지 확인합니다.

본능적인 코드에 테스트 모듈이 있을리 만무하니
일단 여기까지 해결하고 마무리합니다.
중요한건 리팩토링을 하겠다는 의지와 정책입니다!


유지보수 작업을 반복하다보면 궁극적으로는 아래와 같은 모양이 됩니다.

Code: Select all

class A : public AInterface_ForB, public AInterface_ForC, public AInterface_ForD, public AInterface_ForE, public AInterface_ForF
{
....
}
이쯤되면 정말 A 매니저가 필요할까라는 생각이 들게 되죠; 적당한 기회봐서 A 를 제거하면 됩니다-_-

심 형근 wrote: 클래스간 의존도는 기획사항과도 연관이 있기 때문에, 프로그래머의 의지만으로는 쉽지가 않기도 합니다.
클래스간 의존도는 기획과 연관은 있지만
클래스간 의존복잡도는 기획하고는 별개 문제라고 생각됩니다.
빗자루네 http://www.myevan.net >_<b
Locked