프로그래밍 관련/프로그래밍 관련팁

클래스의 캡슐화 accessor, Set() / Get() 메서드를 적절히!!

AlrepondTech 2020. 9. 15. 12:16
반응형

 

 

 

 

 

=================================

=================================

=================================

 

 

 

 

 

 


Set과 Get를 난무하지 말고 적절하게 써야겠다 ^^;

안녕하세요... 가입하고 첫 포스팅이네요 -0-;;

저 같은 경우는 관심분야가 상당히 다양한데, 그 중에서 OOP 원리에 대한 관심도 좀 높습니다.

디자인 패턴이나 리팩토링 같은 주제를 제대로 이해하기 사용하기 위해선 그 밑에 깔려있는 OOP 원리를 정립해 놔야 한다고 생각해서 말이죠... ^^;;

 

그래서, 잠시 OOP 에 대한 얘기를 적어볼까 합니다.. 무거운 주제는 아니니 뉴스 읽듯이 쉭~ 읽으시면 될 것 같구요.. 이미 OOP 에 대해 잘 아시는 분들은 패스하시면 됩니다 ^^;;

 

먼저 첫 이야기는 accessor 에 대한 내용입니다.

 

원본 : http://mobilism.tistory.com/

(블로그 글을 복사한거라 보기가 깔끔하지가 않네요;;; ㅠㅠ 깔끔하게 보실분은 블로그에서 직접 보시면 됩니다.)

 

=================================================================================================================

 

"개발이야기" 는 주로 개인적인 생각들을 담고 있습니다. 여러분들의 생각과 많이 다를 수도 있으니 부족하더라도 질책보다는 의견을 주시면 감사하겠습니다 ^^ 


C++ 이나 C#, Java 등의 정적 OOP 프로그래밍에서 클래스를 만들다보면 대부분의 사람들이 인스턴스 변수를 하나 만들고 이를 액세스 하는 함수들 즉, setter, getter 메서드(이하 accessor)를 거의 무조건 만들더군요.. 그리고, C++ 에선 거의 대부분의 사람들이 멤버 변수의 이름에 m_h , m_str 등의 전형적인 헝가리안 표기법을 사용합니다. (요즘은 정말 고수분들도 계시고 커뮤니티도 활성화 되어서 그런지 그나마 좀 나아졌다지만, 저처럼 평범하게 살아가는 사람들이 훠~~~~얼씬 더 많기에 아직도 현실은 이상과는 거리가 많더군요... ㅠ.ㅠ)

오늘은 이것들이 미치는 영향에 대해서 이야기 해 보려 합니다... 물론, 제목에서 보듯이 좋은 얘기는 아니겠죠 ^^ㅋ;;;;

객체에 대해 생각해 보다 보면 이러한 것들이 얼마나 안 좋은 것인지 알 수가 있습니다. 물론, 프로그래머들이 아직까지도 저렇게 프로그래밍을 하는 데는 많은 OOP 관련 서적들의 책임도 분명 있을 것 입니다. 아직도 많은 OOP 관련 서적들에서 setter, getter 를 무분별하게 사용하고 있거든요.

OOP의 원리 중 가장 기본이 되는 것은 바로 캡슐화(encapsulation)입니다. 여기서부터 다른 모든 것들이 파생되었다고 해도 과언이 아니죠. 그리고, 모든 OOP서적에서도 캡슐화를 중요하게 다룹니다. 잠시 간단한 코드를 생각해 보죠. Person 이란 클래스는 사람을 표현하는 객체입니다. 물론, 이 클래스는 많은 일들을 하겠지만 지금은 간단히 이름만을 가지고 있다고 생각해보죠. 두 Person 객체의 이름을 비교하는 코드를 생각해 보도록 합시다... 너무 쉬우시다고요?? ^^

제 주변에 있는 많은 분들은 아래의 코드와 비슷하게 떠올렸습니다.

[ Code 1 ]

 

class CPerson
{
public :
   CPerson() : m_pName(NULL) {}
   char * SetName(const char * pName ) { assert(pName); m_pName = pName ; }
   char * GetName() { reutrn m_pName; }
private :
   char * m_pName;
};

int main()
{
    CPerson superman, batman;
    if ( strcmp( superman.GetName(), batman.GetName() ) == 0 )
    {
        // ...
    }
}

 

네.. 물론, string 객체를 사용하신 분들도 계실테고, 생성자에서 파라미터로 이름을 넣어주신 분들도 계실 겁니다.. 하지만, 지금 중요한 것은 그게 아니니 넘어가도록 하죠.

위의 코드의 문제가 무엇인지 아시겠나요? 아신다면 저 보다 더 잘 아는 분이실 테니 더 이상 이 글을 읽을 필요는 없으실 것 같네요.. ^^;; 위의 코드는 그냥 OOP 문법을 사용한 프로그램 입니다. OOP의 관점에서 볼 때, 위의 코드는 빵점짜리 입니다. 왜일까요? 바로 위에서 언급했었던 캡슐화를 깨어버렸기 때문입니다. 캡슐화를 깨어버린 곳은 바로 if 문이고, 그렇게 하도록 유도한 부분은 GetName() 입니다. 그리고, 많은 분들이 별다른 생각없이 이런식으로 사용하곤 합니다.

다시 한 번, 캡슐화에 대해 생각해 보도록 하죠. 캡슐화는 자신의 내부 사항을 감춤으로써 외부의 영향에서 벗어나도록 하는 것 입니다. 즉, 정당한 이유가 아니면 객체의 내부에 어떠한 것들이 존재하는지 알려주면 안 된다는 것이죠. 정당한 이유없이 함부로 accessor 사용하는 것은 자신의 멤버를 그냥 public 으로 사용하는 것과 별로 다를 바가 없습니다. 이러한 현상은 주로 get() 메서드에서 상당히 많이 발생합니다. set() 메서드는 그나마 파라미터 체크를 한다는 명분이라도 있을테니 말이죠.

좀 더 이해를 쉽게 하기 위해서, [ Code 1 ]의 main() 부분을 실제 대화로 하면 어떻게 될지 한 번 생각해 볼까요? ^^

 

main : 저기 거기 왼쪽에 계신 분.. 이름이 어떻게 되시나요?
superman : 수퍼맨 입니다. 그런데, 왜 그러시죠?
main : 제가 좀 쓸 일이 있어서요.. 그럼 오른쪽에 계신분은 이름이 어떻게 되시죠?
batman : 배트맨 입니다만.. 무슨 일입니까??
main : 제가 좀 쓸 이 있다니까요.. 왜 이렇게 궁금해 하세요... 제가 알아서 사용하도록 하겠습니다.


수퍼맨과 배트맨의 입장에 있었다면 맘이 좀 상했을 대화 내용이네요.. 지금 물어본게 이름이었기에 망정이지 주민등록번호 였다면 어딘가 몰래 도용되지 않을까 굉장히 걱정을 많이 했었을 수도 있겠네요 -0-;; 실제 대화로 풀어봐도 웬지 자연스럽지 못 한것 같습니다. 평상시 우리는 아마 더 예의바르게 대화 할 것 입니다. 아래와 같이 말이죠.

 

main : (수퍼맨을 보면)잠시 실례합니다만, 혹시 두 분의 이름이 같으신가요?
superman : 글쎄요... 그건 저도 잘 모르겠는데요. (배트맨을 보며) 실레지만 혹시 이름이 수퍼맨 이신가요?
batman :  아닌데요. 왜 그러시죠?
main : 아.. 제가 잠시 참고할 일이 있어서 물어본 것 입니다... 아무튼 두 분의 이름은 틀리시군요.


자, 이번의 대화는 어떤가요? main은 두 사람의 이름은 모르지만 두 사람이 이름이 같은지를 비교하는 목표는 분명히 만족시켰습니다. 수퍼맨과 배트맨도 main 에게 자신의 이름을 노출시킬 필요가 없었으니 정보 노출에 대한 걱정을 한시름 덜었을 것입니다. (물론, 배트맨은 수퍼맨의 이름을 알테지만 말이죠 ^^)

캡슐화를 제대로 지켰다는 느낌이 들지 않으십니까? 이 대화를 코드로 옮긴 것이 [ Code 2 ] 입니다.

[ Code 2 ]

class Person
{
public :
   Person(char * given_name) : _name(given_name) { }
   bool IsEqualName(Person & other) { return (strcmp(_name, other._name) == 0); }
private :
   char * _name;
};

int main()
{
    Person superman, batman;
    if ( batman.IsEqualName(superman) )
    {
        // ...
    }
}

 

어떠신가요? 우리는 캡슐화를 깨지 않았고, 덕분에 무엇을 하려는지 그 의도를 보다 명확하게 적을 수 있었습니다. [ Code 1 ]의 if () 문은 이름이 같다는 것을 비교하려는 것 인지, 두 객체가 같다는 것을 비교하려는 것인지 의도가 명확하지 않습니다. 물론, 수정에도 민감한 건 말할 필요도 없겠죠.  (이름들도 제 스타일로 약간 바꿔봤는데 이 부분은 그냥 참고만 하시기 바랍니다.)

지금처럼 아주 간단한 예에서도 캡슐화는 그 위력을 나타냅니다. 프로그램이 복잡해지고 객체간의 연관 관계가 발전할수록 이런 사소한 부분들이 모여서 나중엔 엄청난 위력을 발휘하게 되는 것이죠 ^^..

무분별하게 남용되는 accessor들은 바로 이런 캡슐화를 깨 버리는 주범이므로, 반드시 필요한 경우에만 accessor를 사용하는 습관을 기르는 것이 고수로 가는 지름길이라 생각됩니다.

몇 년전 회사를 다닐때 팀원들에게 이 부분에 대해서 얘기를 많이 했었는데, 솔직히 그 때는 사람들이 매우 헷갈려 했었습니다. 특히, 네이밍(naming)을 어떻게 할 것인지 감을 못 잡는 경우가 많더군요. 갑자기 왜 네이밍이냐구요? 메서드 명에서 set, get을 빼니 그 때부터 이름이 모호해져서 방황을 하게되는 경우가 있더군요 ^^;; 이름은 굉장히 중요합니다. 때문에, 반드시 심사숙고해서 지어야 합니다. 만약, 프레임웍에 있는 객체의 이름이 잘못 지어진 경우라면, 그 부작용은 정말 이루 말할 수가 없습니다.

다음번에는 "객체란 과연 무엇인가?!" 와 오늘 미처하지 못한 "왜 헝가리안 표기법을 버려야 하는가"에 대해 생각해 보도록 하겠습니다.. 특히, 헝가리안 표기법을 버림으로써 "이름"에 대해 미처 생각하지 못했던 부분들에 대해 많이 알아갈거라 생각되네요 ^^

[출처] OOP 기초 - 독이 되는 getter / setter (게임 개발자 네트워크 (jzsdn)) |작성자 모바일리즘

중요포인트

위의 코드는 그냥 OOP 문법을 사용한 프로그램 입니다. OOP의 관점에서 볼 때, 위의 코드는 빵점짜리 입니다. 왜일까요? 바로 위에서 언급했었던 캡슐화를 깨어버렸기 때문입니다. 캡슐화를 깨어버린 곳은 바로 if 문이고, 그렇게 하도록 유도한 부분은 GetName() 입니다. 그리고, 많은 분들이 별다른 생각없이 이런식으로 사용하곤 합니다.

다시 한 번, 캡슐화에 대해 생각해 보도록 하죠. 캡슐화는 자신의 내부 사항을 감춤으로써 외부의 영향에서 벗어나도록 하는 것 입니다. 즉, 정당한 이유가 아니면 객체의 내부에 어떠한 것들이 존재하는지 알려주면 안 된다는 것이죠. 정당한 이유없이 함부로 accessor 사용하는 것은 자신의 멤버를 그냥 public 으로 사용하는 것과 별로 다를 바가 없습니다. 이러한 현상은 주로 get() 메서드에서 상당히 많이 발생합니다. set() 메서드는 그나마 파라미터 체크를 한다는 명분이라도 있을테니 말이죠.

 

반문 리플들

  •  
  •  
      멈비
    이건 좀 논쟁의 여지가 있는 글 같네요.

    단순히 이름을 제공 위해서 클래스에서 getName 메소드를 제공한 경우는 상황이 다릅니다.

    또한 사용자 입장에서 getName 을 보고 내부 내용을 추측하는 것 조차 바보 같은 상황입니다.

    분명 name 은 가지고 있겠지만 그게 string 인지 char 배열인지 어떻게 저장되는지는 알 수 없으니 제대로된 캡슐화가 맞습니다.

    개인적으로 저는 필요한 get/set 은 필수라고 생각하고 OOP 에도 위배되지 않는다는 생각이 드네요.
  •   라이오스
    이거 실용주의 디자인패턴에서 나왔던 내용같은데요.
    웹쪽에서는 VO( Value Object)라고 해서, Getter/Setter만 지정된 객체를 만들지 않나요?
  •   멈비
    아 그리고 입력 후 생각난건데 위의 예제는 오히려 OOP 설계에 위배되고 있다는 생각이 드네요.

    OOP 설계 원칙 중 단일 책임 원칙 ( SRP ) 에 위배된다는 생각입니다.

    Person 클래스가 처리해야하는 책임이 사람에 대한 정보 처리라면 사람이 같은 사람인지 비교하는 것은 타당하지만 사람의 이름이 같은지 비교하는 것은 필요 이상의 책임이라고 생각됩니다.

    사람의 이름은 문자열로 되어있으면 문자열 클래스가 처리해야하는 책임이거나 Name 클래스로 되어있으면 Name 클래스가 처리해야할 책임이 아닐까 하는 의견입니다.
  • [출처] OOP 기초 - 독이 되는 getter / setter (게임 개발자 네트워크 (jzsdn)) |작성자 모바일리즘

 

 

 

=================================

=================================

=================================

 

 

 

 

 

 

반응형