get/set 메소드와 public 멤버변수

xylosper의 이미지

C++만 쓰면서, 언제부터인가 확실치 않지만, 멤버변수는 전부 private로 선언하고 외부에서 접근하거나 수정할 필요가 있는 것에는 get 혹은 set메소드를 만드는 방식으로 코딩하게 되었습니다.

C++을 공부하고, MFC를 보면서 객체지향은 원래 그런가보다 라고 생각하게 되었는데요...

그런데 문득, 설정 사항을 저장하고 불러오기 위한 클래스를 만들면서, 10개가 넘는 멤버 변수를 위해 일일이 get/set을 만들다가, 이게 진짜 필요한건가 하는 생각이 들었습니다.

물론 외부에서 주는 데이터를 체크하거나 변조한다음에 내부 데이터를 설정할 필요가 있는 경우라면 set메소드가 유용할것 같지만, 예를 들어..

class Settings {
	...
	int time() const {return t;}
	void setTime(int time) {t = time;}
	const std::string& name() const {return n;}
	void setName(const std::string& name) {n=name;}
	...
private:
	...
	int t;
	std::string n;
	...
};

이런 식으로 Settings 10개쯤되는 멤버변수에 대해 일일이 특별히 하는 일없이 값을 반환하거나 대입만 get/set 메소드를 만드는 식으로 코딩을 했습니다.
그런데 하다보니 결국엔 Settings::t나 Settings::n같은 건 결국 get/set 메소드때문에 외부에서 보기에는 public이나 다름 없는 데, 걍 public 으로 선언해버리면 되지 않을까 하는 생각이 들더군요.
이런 경우라도 private로 선언해서 get/set 메소드로 이용하게 만들어야할 이유가 있을까요?

chadr의 이미지

사실 캡슐화 목적도 있지만 저의 경우에는 디버깅 용도로 그렇게 합니다.

만약에 어떤 멤버변수가 삑사리 나서 추적이 필요할 경우 public으로 이곳저곳에서 set/get을 해대면 브레이크 포인트 걸기가 참으로 난감해집니다.

그럴때 그냥 이미 set/get함수를 만들어놓고 그냥 그 함수 두개에만 브레이크 포인트를 걸어놓으면 마음이 편해집니다.:)
이렇게 해놓으면 콜스택도 볼수 있으므로 어디서 이 변수를 set/get하는지 추적도 편하고 브레이크 포인트 노가다도 안해도되고 여러모로 좋습니다.

성능이요? 매우 크리티컬한 상황이 아니라면 이런곳에서 걸리는 부하보다는 다른데서 걸리는 부하를 줄이는게 훨씬 더 높은 성능을 기대 할수 있을 것입니다.
-------------------------------------------------------------------------------
It's better to appear stupid and ask question than to be silent and remain stupid.

-------------------------------------------------------------------------------
It's better to appear stupid and ask question than to be silent and remain stupid.

xylosper의 이미지

디버깅시엔 확실히 편하겠네요.
public이나 다름없는 멤버변수마다 일일이 get/set 만들어주다보니 이게 뭐하는 짓인가 해서 질문하게되었습니다.
귀찮을 땐 IDE의 변수 추가 기능을 이용해서라도 get/set을 해두는게 좋겠네요..
답변감사합니다.

kasi의 이미지

저도 코딩을 그렇게 많이 해보지는 않았지만

한 클래스에 그렇게 많은 get set 메소드가 필요할까요.

클래스 설계를 조금 다듬으면 get set 메소드를 많이

줄일 수 있을꺼 같습니다.

xylosper의 이미지

contents1, contents2 ..같은 식으로 뭔가 연관있는 변수라면 배열로 저장해서 get/set 메소드를 인덱스로 접근하게 해서 하나로 통합할수있겠지만, 예를 들어 어떤 위젯에 대해서...(클래스이름은 그냥 의미만 생각해서 적은 것입니다.)

int x, int y, int width, int height, Color textColor, Color bgColor, String title, String contents, Font font, double opacity ...

이런식으로 변수들이 있다고 하면 이경우는 변수만큼 get/set을 만들수밖에 없지 않나요...?

bootmeta의 이미지

자동화 툴이나 디버깅 목적으로 생성된 get/set이 아니라면 굳이 모든 변수 마다 필요하지 않을 것 같습니다.
각 변수마다 설정한다는 것은 단지 함수를 통해 접근한다는 것만 차이가 있지 public한 멤버 변수들로 설정하는 것과 차이가 없습니다. 역으로 캡슐화를 무력화 시키는 것이겠죠.

익명 사용자의 이미지

변수명이 바뀌는 경우는 차이가 생깁니다. get/set으로 디커플링 된 경우는 한 클래스에서 해결되지만, public 멤버 변수로 가져다 쓰는 것이 내 소스가 아닌 경우는 버전 호환이 안되는 문제가 생기겠죠.

xylosper의 이미지

보통 어떤 변수를 위한 get/set함수라면, 변수이름이랑 함수이름은 비슷하지 않나요?

변수 이름이 바뀌었다면 뭔가 변수가 의미상 변화가 있기 때문에 바뀌었을텐데, 그렇다면 그것을 반환하는 함수도 역할이 바뀐 셈이므로, 임시로 지은 변수가 아닌 한은 변수 이름이 바뀌면 보통은 함수이름도 바뀌지 않을까요..?

나그네나그네의 이미지

저같은 경우는 주로 Java만 쓰기에 C++에선 어떨지 모르겠습니다만 -ㅅ-ㅋ

Settings 클래스를 상속하는 클래스에서 field의 set, get 시에 특별한 다른 일도 해야 할 경우도 생기지 않나요?

저 같은 경우는 get, set을 반드시 서줍니다.
----------------
agidari.wordpress.com

xylosper의 이미지

앞으로 특별한 다른일을 해야할 경우가 생길수도 있으니 get/set이 필요하다는 말씀인것 같습니다.
음...현재로선 딱히 바뀔거 같단 생각도 안드는데...앞일은 아무도 모르니 뭐라하기 힘드네요.
저도 지금까진 디버깅이나 테스트를 위해서 임시로 추가한 변수가 아닌한은 무조건 private에 get/set을 만들었었는데, 사실상 지금까지 만든 get/set의 90%이상이 단순 대입이나 반환뿐이다보니 필요성에 의문이 생겼네요.

hongminhee의 이미지

현재는 단순히 멤버를 주고받는 용도로만 쓰이지만, 앞으로 요구사항이 달라져 멤버를 주고받을 때 뭔가를 더해야할 수도 있겠죠. Python이나 C#이라면 이런 경우에 일단 public 멤버를 선언하여 사용하다가, 뭔가를 더해야할 경우에 property(C#이라면 getter/setter)로 만들어서 인터페이스를 유지할 수 있습니다만, C++, Java는 그런 기능이 없으므로 처음부터 직접 get/set 멤버 함수를 써야합니다.

xylosper의 이미지

말씀듣고 C#의 프로퍼티에 대해 찾아보니 정말 신기하더군요.
C++도 하자면 프로퍼티를 위한 클래스를 만들어서 대입연산자를 오버로딩하는 식으로 하면 가능할 것 같긴 한데...
그런데 이것도 정보은닉한거라고 할 수 있나요..? 웬지 공부할땐 정보은닉을 위해 접근자가 필요하단 식으로 한거 같은데, 결국 프로퍼티를 이용한다고 해도 외부에서 유효한 값이라면 얼마든지 바꿀수 있는 셈이니 정보은닉에는 별 도움이 안되는듯 한데요...

hongminhee의 이미지

일단 정보 은닉을 왜 하는지에 대해서 고민해볼 필요가 있겠죠. 그리고 get/set 메서드를 둔다고 해도 결국 외부에서 그것을 통해 객체의 상태를 건드릴 수 있지 않습니까? 애초에 그러기 위한 get/set 메서드고요. C#의 프로퍼티는 object.getA()object.a로, object.setA(value)object.a = value로 표현할 뿐, get/set 메서드와 하는 일은 동등합니다. 저 같은 경우 object.a, object.a = value와 같은 구문이 언어적으로 제공되는데도 그것을 사용하지 못한다는 점에서 get/set 메서드가 예쁘게 보이지 않더군요. 물론 Java, C++의 경우에는 C#과 같은 프로퍼티 기능이 없으므로 get/set 메서드를 만들어서 써야겠지만요. (말씀하신 것처럼 C++라면 대입 연산자를 오버로딩하여 비슷하게 흉내낼 수 있겠지만 배보다 배꼽이 큰 것 같네요.)

xylosper의 이미지

Quote:
get/set 메서드를 둔다고 해도 결국 외부에서 그것을 통해 객체의 상태를 건드릴 수 있지 않습니까? 애초에 그러기 위한 get/set 메서드고요.

저도 그렇게 생각합니다.
그런데 객체지향이란걸 접하면서, 마치 접근자는 정보은닉을 위해서 객체지향에 필수적인 요소인것처럼 소개 되있고, 스스로도 그냥 정보은닉을 위해서 필요한가보다 하고써오다가, 문득 내부 정보를 외부로 노출시키고, 또 외부로 부터 내부 정보를 변경시키는 접근자가 어째서 정보은닉을 위한 것인가 라고 생각하게 된 것이었습니다.
익명 사용자의 이미지

맴버변수의 자료형이 달라지는 경우도 생각해 볼 수 있을 겁니다.
하지만 객체라는건 그 내부가 어떻게 되어 있는지는 사용하는 입장에서 전혀 영향을 받지 않아야 합니다.

그런데 맴버변수를 바로 쓰는 구조라면 영향받는 코드가 어디에 있는지 찾기도 일이 많을듯염.

당연한 얘기지만 get/set으로 되어 있다면 그런식으로 객체의 내부구조가 변경되더라도
객체의 내부가 변경되더라도 기존의 get/set인터페이스를 그대로 유지할 수 있으므로
그걸 사용하는 다른 코드들에게 전혀 영향을 주지 않을 수 있겠죠.

또한 맴버함수는 오버로딩이라는 것도 가능하니 ...

netionics의 이미지

객체지향에 빠져든 프로그래머는 이상하리만큼 getter/setter에게 기대하는 바가 높은 것 같습니다.

"거의 모든" 멤버에 getter/setter를 두고 다른 멤버 함수가 없다면 데이터를 public으로 즉, 구조체로 만들 때와 비교해서 얻을 수 있는 장점이 거~의 없습니다.

변수랑 함수가 1:1대응인데 인터페이스가 유지될것이란 생각은 너무 낙관적이지요.
그렇지 않나요? 여친한테 딴 남자가 생겼는데도 자기랑 계속 사귈 가능성과 비슷하다고 봅니다.
그런 구조라면 멤버가 변경될 때 getter/setter도 바뀔 가능성이 굉장히 높습니다.

그리고 단순한 데이터 저장용 타입을 상속받는 경우가 얼마나 있을지 의문입니다.
거기다가 오버라이딩까지 할 가능성은 좀...
그럴 가능성이 없다에 1000원 걸겠습니다.

(혹시 오해하실까봐 강조하지만 그냥 public으로 공개하는게 더 좋다는 이야기가 아닙니다
둘다 거기서 거기. 다 안좋다는 말이지요)

아예 다른 구조로 짜는게 좋습니다..만 (javaworld에서 why getter and setter methods are evil이라는 기사에서 말하는 접근방식을 사용하는게 좋아보입니다)

다 뜯어 고치기엔 내 인생이 너무 짧다고 생각하신다면 같이 일하는 동료한테 (특히 이 클래스를 주로 사용할 동료들) 물어서 결정하는게 인간관계 유지에 그나마 도움이 될거라 생각되네요.

:)

익명 사용자의 이미지

멤버가 변경되어 새로운 getter/setter가 생기더라도
기존의 getter/setter 함수 내부에서 형변환(예전 형태에서 새로운 형태)을 하면 되기 때문에
기존의 인터페이스를 유지할 수는 있습니다. ^^

익명 사용자의 이미지

근데 오버라이딩이라는 얘기는 어디서 나온 얘기인지 의문이군요.
설마 오버로딩과 혼동한 것은 아닌지?

class foo {
    int a;
public:
    void setA(int n);
    void setA(Integer& n);
    ...
};
M.W.Park의 이미지

상속받은 클래스가 오버라이딩한다는 뜻인거 같은데요?

-----
오늘 나의 취미는 끝없는, 끝없는 인내다. 1973 法頂

-----
오늘 의 취미는 끝없는, 끝없는 인내다. 1973 法頂

익명 사용자의 이미지

재미있는 농담이군요.
오버라이딩은 당연히 상속관계에서만 나오는 얘기지요. ^^

1000원을 걸겠다기에 갑자기 그 얘기가 왜 나왔나 궁금해서요.
내기는 혼자하는게 아니고 어디서 무슨 얘기를 들었으니까 하는 얘기 아닌가요? ^^

xylosper의 이미지

"why getter and setter methods are evil이라는 기사에서 말하는 접근방식"이란게 읽어봐도 뭔지 감이 잘 안오더군요...
CRC카드를 이용해서 의견을 수렴하는 등 하고 적혀있는데, 결국엔 클래스 설계를 잘하면 된다는 이야기 같은데요...
뭔가 뜬구름 잡는 이야기만 적힌 듯해서 이해가 안됩니다.
구체적인 상황으로, AWidget에 적용하기위한 속성들(크기, 폰트, 색깔 등등)을 대화상자를 통해서 사용자로부터 입력 받고 저장한 후, AWidget을 열때 저장된 속성들을 불러오는 경우, 속성을 저장하고 불러오기 위한 AConfig를 만들어서 대화상자에서 AConfig의 값들을 지정하고, 나중에 AWidget에선 AConfig의 값을 불러오는 식으로 짰는데요...
결국 이렇게 짜면 AConfig의 값을 지정하고 불러오는 과정이 들어가니까 결국엔 접근자를 쓰거나 퍼블릭 멤버변수로 만들거나 해야할 것 같은데요...어떤 구조로 짜면 접근자가 없어질까요..?

bootmeta의 이미지

가라 코드 입니다.

class AConfig
{ 
public:
   save ( int width, int height, string font_name, int font_size, int red, int green, int blue);  
   load ( ... );
private:
 
  rectangle size;
  xfont     font;
  rgb_color color; 
}

굳이 각 요소별 데이타를 따로 처리할 필요가 없다면 interface는 load(), save()만 있으면 됩니다.
멤버 변수들의 data type 구현이 gui 환경에 따라 바뀌더라도(예를 들면 ms windows에서 x window로 포팅하는 경우) 인터페이스에는 영향을 미치지 않습니다.

많은 경우, 강박적으로 각 멤버 변수들에 대해 getter, setter를 구현하는 경우를 보게 됩니다.
즉 실제 쓰이지도 않을 것에 대해 미리 너무 많은 걱정을 하는 경우입니다.
물론 실제 쓰이는 경우를 대비하는 것은 좋습니다만 기우인 경우(실제 쓰이지 않는 사장되는 코드들)를 너무 많이 봅니다.

특히 잘 알려진 범용 라이브러리들과 달리 현재 개발 중인 프로그램의 사용 층이 한정되어 있다면 인터페이스는 간결하고 필수적인 것들만 유지하는 것이 좋습니다.

xylosper의 이미지

답변 감사합니다.
그런데 말씀하신거처럼 save를 통해서 set을 없앤다고 해도 겉보기에 set이 없을뿐 사실상 있는 거나 다름 없는 것 아닌가요...?
 save ( int width, int height, string font_name, int font_size, int red, int green, int blue);
이건
setSize(int width, int height); setFont(string font_name, int font_size); setColor(int red, int green, int blue);
라고 쓸걸 단지 하나로 합친것에 불과한 것처럼 보이는데요...근본적으로 어떤 부분이 다른 지 알려주시면 감사하겠습니다.
그리고 load(...) 라고 쓰신건, C++로 적어보면
load(int *width, int *height, string *font_name, int *font_size, int *red, int *green, int *blue);
라는 의미로 적으신건가요...?

bootmeta의 이미지

만일 AConfig 클래스를 파일 등에 설정 값을 저장하는 목적(serialize)으로 쓴다면 각 멤버에 접근하는 함수를 굳이 만들 필요가 없습니다만 다용도 목적으로 쓴다면 setter/getter 구현이 필요할 수도 있겠죠.
load() 부분은 생각하신대로 제가 나열하기 귀찮아서.. ^^;

ps) 멤버 변수가 몇개 되지 않을 때 getter/setter는 별 문제가 안될 수 있습니다.
그러나 멤버 변수가 20개 정도 있고(물론 클래스를 쪼개는 경우도 있을수 있습니다만) 각 변수에 대해 getter/setter를 전부 구현한다면 웬지 배보다 배꼽이 더 커보이지 않을까요?

쓸데없이 떠들었지만 결국 제가 하고 싶은 한마디는 "기계적으로 getter/setter를 쓰는 것은 비추천!"

xylosper의 이미지

에..우선 답변에 감사드립니다.
그런데 bootmeta님이랑 저랑 얘기하고 있는게 다른거 같습니다.
전 netionics님이 언급하신 'why getter and setter methods are evil에서 말하는 접근 방식'에 대한 이야기를 하고 있는데, why getter and setter methods are evil에서는 애시당초 접근자는 써선 안된다는 거처럼 나와있고, 접근자를 안쓰는 방법으로 CRC카드를 이용해서 클래스를 잘 설계하면 된다고만 적혀있어서, 개인적으로 뜬구름 잡는 이야기인듯 하여 구체적으로 접근자없이 구현하는 방법의 예를 보여달라고 한 것이었습니다.
bootmeta님께서는 기계적으로 변수마다 get/set을 만들 필요는 없단 말씀인 것 같은데, 그건 저도 당연하다고 생각하며, 외부에서 접근할 필요가 없는 변수에 get/set을 만드는 일은 없습니다. 그에 반해 why getter and setter methods are evil에서는 아예 접근자를 쓰지 말라고 주장하고 있기 때문에, 그럼 구체적으로 접근자를 안쓰고 이런 경우는 어떻게 구현하느냐 하는 이야기였습니다.

bootmeta의 이미지

죄송합니다.
문맥을 제대로 읽지 못하고 어설픈 삽질을 했네요.
말씀하신 글을 한번 읽어봐야 겠네요.
수고... :)

김일영의 이미지

그렇게 단지 값에 접근하기 위한 메소드를 일일이 만드는 것은
오로지 학습 편의를 위해서만 그렇게 예를 보여주는거지
그것이 개체지향적 구조인것처럼 생각하는 것은 문제가 있다고 하더군요.
(오래전이라... 링크를 찾아 걸기는 좀...)

저 역시 쭉 그렇게 생각해왔습니다.

단순히 변수의 값에 접근하는 것 외에
어떤 개체의 '속성'으로서, 값에 접근할 경우 동반되는 로직이 있을 경우에만
get/set 메소드를 구현하는 것이 바람직하다고 봅니다.

그런데 사실 잘 설계된 개체라면 멤버변수에 접근할 때 동반되는 로직이 꽤 있기 마련입니다.
해당 멤버변수 자체의 무결성 조건, 멤버변수 간의 상호 무결성 조건이 있을 테니까요.
그렇게 유기적으로 연관이 있는 속성들이니까 개체로 도출이 되었을 것이고요.
그냥 전혀 제약이 없는 속성들이 모여만 있다면
그 개체가 제대로 도출이 된것인지부터 생각을 해봐야 할 것 같습니다.

xylosper의 이미지

답변 감사합니다.
물론 int같은 원시자료형이라면 최소값 최대값을 체크하는 등의 조건 체크가 필요할수 있는데요...
하지만, 말씀하신대로 무결성 조건을 만족하도록 잘 설계된 클래스의 객체를 멤버로 가지고 클래스의 경우는 더이상 할게 없는 경우가 많지 않나요...?
예를 들어 색깔을 나타내는 클래스라면 이미 그 클래스는 대부분 잘못된 rgb값에 대해 기본값으로 초기화한다던가 하는 식으로 설계되있는 경우가 많습니다.
그리고 사실상 원시자료형은 bool이랑 int정도밖에 안쓰고 거의 그러한 클래스의 인스턴스를 쓰는 경우가 대부분이다보니, 딱히 체크할 필요가 없는 경우가 대부분이던데...이건 저만 그런걸까요...?

keedi의 이미지

C++ 및 다른언어의 OO 환경에서 set/get 메소드에 대해 궁금한점이 있습니다만...
Perl 객체쪽을 공부하다보면, 가끔 아티클에서 볼 수 있는 내용인데,

private 항목에 대해서 단순한 set/get 메소드를 만드는 경우,
에디터의 자동완성이나 copy n paste 를 통한 직접 타이핑보다,
set/get 처리를 해줄 수 있는 루틴을 만들어서 set/get 메소드를 자동 자동생성해서
사용하는 것이 유지보수 측면에서 더 낫다... 라는 이야기가 있습니다.

그래서 Perl의 OO 프레임워크들은 대부분 클래스 정의시 몇가지 표식을 해주면,
해당 멤버들의 set/get 메소드를 내부적으로 자동 생성을하고,
(IDE가 생성하는 코드가 아니므로 프로그래머는 set/get 코드를 볼 수 없습니다. :)
특별하게 동작해야할 set/get의 경우 오버라이드해서 사용하곤 합니다.

궁금한 것이 C++이나 다른 언어에서는 이런식으로 set/get 코드를 내부적으로(외부적이 아니라)
자동생성하는 것이 OO 모듈이나 라이브러리 또는 프레임워크는 없는지요?
또는 IDE가 너무 잘 되어 있기 때문에 이런 류의 프레임워크는 필요가 없는지요.

만약 이런 기능을 지원하기 때문에, 일일이 set/get 메소드를 생성하는 수고가 0에 가깝다면,
일단, 이것이 유용할까에 대한 고민 또는 set/get의 남용에 대한 고민, 구조에 대한 고민은
set/get 메소드를 만드는 번거로움과 별개로 생각해볼 수 있을텐데요.

---------------------------
Smashing Watermelons~!!
Whatever Nevermind~!!

Keedi Kim

----
use perl;

Keedi Kim

xylosper의 이미지

자바는 모르겠고요.. C/C++은 컴파일을 하기 전에 전처리기를 돌리기 때문에, 매크로로 자동으로 생성되게 할 수 있습니다.
그런데 이건 표준사항이 아니기 때문에, 컴파일러 의존적이라, Qt를 이용해서 윈도우랑 리눅스용 양쪽모두 만들고 있는 저로썬 쓸수가 없네요. 대신에 Qt에서는 컴파일러에 의존하지 않게 자체적으로 자체적으로 매크로를 지원하는데, 이경우는 특정 클래스(QObject)를 상속받은 클래스에서만 유효하기 때문에, 이거 때문에 상속받는 것도 뭐하고 해서 안쓰고 있는 상황입니다.

익명 사용자의 이미지

외부에서 접근할 필요조차 없는 내부를 get/set으로 몽땅 드러내는건 당연히 좋지 않지만
(외부에서 필요한지 안필요한지 몰라서 일단 몽땅 만든다는 거면 설계부터 문제가 있는듯)
그렇다고 내부를 몽땅 public으로 만드는건 더더욱 나쁘다고 생각됩니다.

그 객체의 멤버가 변경이라도 되면
전 소스를 뒤져가며 그 멤버에 접근하는 코드는 다 수정되어야 합니다.

자신의 내부가 변경 되었다고 자신을 사용하는 소스를 모두 검사해서 수정해 달라고 하는 객체를 두고
객체지향이라고 하기 곤란할 듯합니다.

netionics의 이미지

시간이 지나고 제가 쓴 글을 보니 제가 다 얼굴이 붉어지는군요.
제가 애매 모호한 답변한 것은 사실 저도 정확히 알지 못하면서 답 글을 달았기 때문일 겁니다.
알지도 못하는 것을 아는 척 말한 점 xylosper님께 사과를 드립니다.
글 투도 좀 격양된 것을 보니 뿔 딱지 난 상태에서 글로 토악질을 한 것 같습니다.
다른 모든 여러분께도 사과를 드립니다.

너무 늦은 건 아닌지 모르겠지만 제가 생각하는 방법을 말씀 드리려 합니다.

예를 들어 xylosper님이 어떤 게임을 만들고 있다고 하겠습니다.
게임에서 그래픽 옵션과 사운드 옵션들을 설정할 수 있고
다시 게임을 시작하면 설정된 값을 가져와서 써야 할 겁니다.

class Setting {
public:
bool load(File& file);
bool getCharacterVisible();
void setCharacterViseble(bool visible);
bool getMusicOn();
void setMusicOn(bool on);
private:
... // 멤버 함수가 같은 이름의 멤버 변수가 쭉 있습니다.
};

void Grahpic::set(const Setting& s) {
m_charaterVisible = s.getCharacterViseble();
}

void Sound::set(const Setting& s) {
m_musicOn = s.getMusicOn();
}

class System {
void load {
File file;
m_setting.load(file);
m_graphic.set(m_setting);
m_sound.set(m_setting);
}
private:
Setting m_setting;
Grahpic m_graphic;
Sound m_sound;
};

설정을 불러올 때 이렇게 할 수 있고 저장할 때도 비슷한 방식으로 하면 되겠죠.
지극히 직관적이지만 몇 가지 문제가 있는데
첫 번째로 Setting에서 Graphic, Sound가 가지고 있는 설정이 전부 드러나므로 만약 그래픽 설정이 늘어난다면
Graphic만 바뀌는 게 아니라 Setting도 수정해야 합니다.

두 번째로 Setting에서 Graphic, Sound 설정을 전부 불러오고 저장하는 방법을 알아야 하기 때문에 복잡도가 집중됩니다.
그래픽 설정 저장 방식과 사운드 설정 저장 방식이 다르다거나
나중에 마우스나 키보드 설정도 저장해야 한다면 점점 더 복잡도가 집중될 수 밖에 없습니다.

또 비슷비슷한 getter/setter함수가 여기저기 생기기 때문에 재미도 없고 단순 타이핑 노동량도 늘어납니다.

문제의 근본 원인이 Graphic, Sound에 숨어 있어야 할 정보가 밖으로 드러나 있다는 것이기 때문에
이 문제를 해결하려면 아예 Setting클래스를 없애야 합니다.
Setting과 비슷한 기능을 하는 클래스가 있다면 어떤 방식을 쓰더라도 위에 있는 문제를 완전히 해결하지 못합니다.
다음처럼 할 수 있습니다.

class Grahpic {
bool load(File& f) {
f.read(m_characterVisible);
}
bool save(File& f) {
f.write(m_characterViseble);
}
};

class Sound {
bool load(File& f) {
f.read(m_musicOn);
}
void save(File& f) {
f.write(m_musicOn);
}
};

class System {
void load {
File file;
m_graphic.load(file);
m_sound.load(file);
}
};

비유하자면
첫 번째 방식은 System이 정보가 들어있는 상자를 가져오고 상자를 열어서 내용물을 하나하나 다 꺼낸 다음 일일이 Graphic, Sound에 배급하는 식이고
두 번째는 System은 상자만 가져오고 Graphic, Sound가 직접 내용물을 가져가도록 한 것이라 볼 수 있습니다. System은 상자 내용물을 알지 못합니다.

여기 까지는 괜찮지만 한가지 큰 문제가 남아있는데 게임 중간에 설정을 변경할 방법이 없다는 것이죠.
설정이 숨겨져 있기 때문에 외부에서 이를 바꿀 수가 없습니다.
화면에 체크박스를 띄워서 설정을 바꾸는 방식을 지원하려면
Graphic과 Sound에 getter/setter를 둬야 한다는 것인데 이럼 말짱 도루묵이니까요.
이런 경우에 이런 식으로 할 수 있습니다.

void Graphic::proc(...) {
// 필요한 컨트롤을 만들어 메시지에 맞는 처리
}

void Graphic::showSettingWindow(Window& win, const Rectangle& pos) {
win.createChild(proc, pos);
}

void Sound::proc(...) {
// 마찬가지로 컨트롤을 만들어 배치하고 메시지 처리를 합니다.
}

void Sound::showSettingWindow(Window& win, const Rectangle& pos) {
win.createChild(proc, pos)
}

void System::showSettingWindow() {
Rectangle gPos(...);
Rectangle sPos(...);
m_graphic.showSettingWindow(m_mainWin, gPos);
m_sound.showSettingWindow(m_mainWin, sPos);
}
이번에도 System이 직접 설정을 가져오고 메시지 처리를 하는 부분이 없으며
윈도우를 하나 던져주고 각자 맞는 프로시저를 사용해서 윈도우 입력/출력을 하도록 합니다.
다만 윈도우 배치는 System이 결정해 주는 게 맞는 것 같으므로 위치 정보도 같이 넘기도록 했습니다.

이런 식으로 짜면 원래 보다는 더 복잡한 구조가 되는 것은 맞습니다.
Graphic과 Sound가 원래는 몰랐던 File이나 Window를 알아야 하고 대리자 클래스를 만들어야 하는 경우도 있으니까요.
전체적으로 클래스간에 관계가 늘어납니다.
저 수준의 라이브러리들은 각 클래스들이 독립적으로 이용 가능하도록 짜야 되기 때문에 대개 이런 식으로 작성하지는 않습니다.
이 방식이 무조건 옳다고 할 수는 없으며 단지 변경에 의한 영향을 줄이는 한 방식으로 보는 게 맞는 것 같습니다.
변경 가능성도 거의 없고 차라리 getter/setter를 두는 게 이해도 쉽다면 득보다 실이 더 많다고 볼 수 있으니까요

하지만 그런 경우가 아닐 때 getter/setter를 쓰면 정보가 여러 군데 분산 되고
어디선가 그 정보가 한번에 처리되는 구조로 짜기 쉬워집니다.
해당 정보가 변경되면 수정되는 부분이 늘어날 테니까요.

혹 질문하신 의도와는 거리가 있다고 생각되거나 뭔가 문제가 있다고 생각하신다면 지적해주시길 바랍니다.

:)

xylosper의 이미지

그런 방법이 있군요. 확실히 그렇게 하면 setter없이 값을 설정하는게 가능하네요.
하지만 말씀하신대로 컴파일 의존성도 늘어나고 복잡해지는 군요...
참고하도록 하겠습니다. 감사합니다.

semmal의 이미지

OO에서 캡슐화를 하는 이유는 내부의 구현을 숨기기 위함입니다.

때문에 모든 변수에 대해서 getter/setter를 만드는 것은 구현을 그대로 까발리는 행위기 때문에 당연히 지양되어야 합니다. 설령 정의된다고 하더라도 private/protected으로만 써야하는 경우도 있습니다.

클래스의 메소드는 언제나 최소한으로 정의되어야 합니다. 필요없는 메소드는 만들 필요도 없고 노출될 필요도 없습니다.

혹시 나중에 다른 메소드가 필요하거나 노출이 필요하면 그때 상속이나 프로토타이핑을 써야하는 겁니다. 물론 언어적인 제약이나 효율성 때문에 다른 선택을 하는 경우도 있지만, OO의 철학으로 보자면 상속 또는 프로토타이핑이 답이 맞습니다.
------------------------------
How many legs does a dog have?

------------------------------
How many legs does a dog have?

xylosper의 이미지

감사합니다.
프로토타이핑이란 걸 처음보아서 객체지향에 대한 프로토타이핑을 찾아보면 프로토타입기반의 언어에 대한 소개가 나오는데, 여기서 말하는 프로토타입과 같은 것인가요...? 죄송하지만 프로토타이핑이 무엇인지 간단하게나마 설명해주실수 있을까요?

semmal의 이미지

상속을 통해서 확장을 시도하는 것이 아니라 클론을 만들어서 비슷한 일을 할 수 있습니다. 영화 스타워즈에서 장고 펫의 DNA를 복사해서 클론병사를 만든 것 처럼, 일단 복사를 한 다음에 필요한 부분을 변경함으로써 원하는 기능을 구현할 수 있습니다. 즉, 클래스간의 관계로 설명하는 것이 아니라 인스턴스간의 관계로 설명되죠.

프로토타이핑이라고 했지만 정확한 용어는 아니고 클로닝이나 그냥 복사라고 보통 부릅니다. 자바스크립트, Io, Self, Lua 등의 언어에서 사용합니다. 몇몇 다른 언어에서도 다른 방법을 통해 쓸 수 있도록 하는 경우도 있구요.
------------------------------
How many legs does a dog have?

------------------------------
How many legs does a dog have?

xylosper의 이미지

답변감사합니다.
말그대로 프로토타입을 만들고 그걸 확장하는 것이군요.
개인적으로 Lua에 대해 관심이 있었는데 한번 시간날때 공부해봐야겠습니다.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.