객체지향 패러다임의 언어와 OOP를 처음 접하면 가장 이해할 수 없는 것 중 하나가
"왜 번거롭게 getter/setter를 쓰는지?"인 사람이 많을 겁니다.
왜 쓰는 걸까요? 걍 멤버변수에 접근하면 되잖음? C에서도 그랬고...
몇몇 다른 이유들도 있겠지만(콜스택 추적...)
가장 큰 이유는
"getter/setter가 인터페이스와 구현을 분리시킨다"
인거 같습니다.
무슨 소리일까요?
예를 들어봅시다.
저는 최근에 SDL을 이용해서 GUI프로그램을 만들고 있습니다
(SDL은 Simple Media Layer라고 C로 되있는 게임 제작용 라이브러리 입니다. 추천함.)
여기서 화면에 어떤 png파일을 그리려면(렌더링),
렌더링을 담당하는 렌더러와
Png파일의 크기와 그 좌표를 저장할 사각형이 필요합니다.
사각형은 x,y좌표와 w,h가로세로 길이가 있으면 되겠죠.
렌더러가 렌더링을 하게 하려면 Png파일과
x,y,w,h 정보가 렌더러에게 전달되어야합니다.
그러면 Png를 요렇게 만드는 겁니다.
class Png
{
public:
int x,y,w,h;
PngFile png;
Png(int ax, int ay, int aw, int ah)
{
x = ax;
y = ay;
w = aw;
h = ah
}
...
};
(실제 SDL API는 이렇지 않습니다 개구라입니다 걍 의미만 전달되면 됩니다.)
(C++스럽지 않은 초기화는 C++을 접하지 않은 사람들을 위해서입니다)
그리고 렌더러에게 저 변수들을 전달해 봅시다.
int main(){
...
Png pic(10,10, 200,200);
renderer.render(pic.png, pic.x, pic.y, pic.w, pic.h);
...
}
잘 돌아갑니다. 아무 문제없잖아!
getX() getY()같이 줄줄줄 써대지 않고 단 한글자로 표현하여
아주 깔끔하며 정갈하다는 생각에 가슴 한편이 훈훈해집니다.
후로그래머는 이후로도 이런 방식으로 5000줄 정도의 코드를 작성하게 됩니다.
그런데 어느날 SDL API참조문서를 읽던 프로그래머는 엄청난 사실을 깨닫습니다.

SDL_Rect라는 구조체를 SDL에서 제공하는겁니다.
이 구조체는 x,y,w,h를 가집니다.
그리고 렌더러의 또다른 렌더링 함수가 있었습니다.
renderRect(Png picture, SDL_Rect);
그 함수는 SDL_Rect를 넣어야 작동하는 함수이고
종전의 렌더링함수보다 성능이 좋으면 다양한 효과를 png비트맵에 적용할 수 있는
그냥 짱좋은 놈이었습니다. 이전의 render함수보다 모든 면에서 나았지요.
어떠한 이유때문에 프로그래머는 새로 알아낸 렌더링함수 renderRect를 써야했습니다.
아니면 최소한 이제부터는 renderRect를 써야했습니다.
후로그래머는 고민에 빠집니다. 쉬바 어떡하지
이미 써놓은 코드가 너무 많습니다! 이걸 다 고칠수는 없습니다.
그래서 요렇게 멤버를 하나 더 추가합니다.
class Png {
public:
int x,y,w,h;
PngFile png;
SDL_Rect rect;
....
};
int main(){
...
Png pic(10,10, 200,200);
...
renderer.renderRect(pic.png, pic.rect);
...
}
그러나 테스트 결과 맛이 가버렸습니다.
이전에 작성한 코드의 어떤부분에서 w가로 크기를 반으로 줄였는데
pic.x = pic.x / 2;
렌더링이 된 결과는 w크기 그대로 나와버린 것입니다.
그도 그렇듯이 rect와 x,y,w,h는 아무런 관계도 없기 때문이죠.
그러면 어떻게 해야되나... 그렇다면 아예 멤버변수 rect를 없애는건 어떨까요?
렌더링하기 전에 일회용 rect를 만들어서 주는거지요
class Png
{
public:
int x,y,w,h;
PngFile png;
Png(int ax, int ay, int aw, int ah)
{
x = ax;
y = ay;
w = aw;
h = ah
}
...
};
int main(){
...
Png pic(10,10, 200,200);
...
SDL_Rect rect(pic.x, pic.y, pic.w, pic.h);
renderer.renderRect(pic.png, pic.rect);
...
}
하지만 이건 좀 멍청한 거 같습니다. 매번 SDL_Rect를 생성해야되고
각각의 Png마다 렌더링용 Rect를 하나씩 가지고 있어야되는데
아무리 생각해도 SDL_Rect는 Png의 멤버변수가 되어야할 것입니다.
그래서 결국 후로그래머는 멤버rect에 대한 접근함수를 만듭니다(엥? 그거완전...)
그리고 멤버 rect는 private으로 걸어줍니다.
class Png {
public:
int x,y,w,h;
PngFile png;
Png(int ax, int ay, int aw, int ah)
{
x = ax;
y = ay;
w = aw;
h = ah
}
SDL_Rect getRect()
{
rect.x = x;
rect.y = y;
rect.w = w;
rect.h = h;
return rect;
}
...
private:
SDL_Rect rect;
};
int main(){
...
Png pic
renderer.renderRect(Png.png, pic.getRect());
...
}
어딘가 어설프긴 하지만 해결은 된 거 같군요!
하지만 여전히 문제는 있습니다. 쓸데없이 데이터를 겹쳐서 저장하는군요..
하지만 저는 이제 뭘 어떻게 해야할지 모르겠읍니다.
그런데 말입니다... 처음부터 getter/setter를 썼으면 어땠을까요?
처음에는 후로그래머는 SDL_Rect의 존재를 몰랐으니 다음처럼 작성했을겁니다.
class Png {
private:
int x,y,w,h;
PngFile png;
public:
Png(int ax, int ay, int aw, int ah)
{
x = ax;
y = ay;
w = aw;
h = ah
}
//getter
int getX(){ return x; }
int getY(){ return y; }
int getW(){ return w; }
int getH(){ return h; }
PngFile getPng(){ return png; }
//setter
void setX(int ax){ x = ax; }
void setY(int ay){ y = ay; }
void setW(int aw){ w = aw; }
void setH(int ah){ h = ah; }
...
};
int main(){
...
Png pic(10,10, 200,200);
renderer.render(pic.getPng(), pic.getX(), pic.getY(), pic.getW(), pic.getH());
...
}
앗! 그런데 SDL_Rect를 알게 되었다면?
이렇게 하면 되겠죠!
class Png {
private:
SDL_Rect rect;
PngFile png;
public:
Png(int ax, int ay, int aw, int ah)
{
rect.x = ax;
rect.y = ay;
rect.w = aw;
rect.h = ah
}
//getter
int getX(){ return rect.x; }
int getY(){ return rect.y; }
int getW(){ return rect.w; }
int getH(){ return rect.h; }
PngFile getPng(){ return png; }
SDL_Rect getRect(){ return rect; }
//setter
void setX(int ax){ rect.x = ax; }
void setY(int ay){ rect.y = ay; }
void setW(int aw){ rect.w = aw; }
void setH(int ah){ rect.h = ah; }
...
};
int main(){
...
Png pic(10,10, 200,200);
renderer.render(pic.getPng(), pic.getX(), pic.getY(), pic.getW(), pic.getH()); //전에 적은걸 걍 냅둬도 됩니다! kia!
...
...
renderer.renderRect(Png.png, pic.getRect); //물론 새로운 함수를 도입해도 되지요!
}
크으.... OOP뽕에 취한다...
겹치는 데이터도 없고
이전에 작성한 걸 고칠 필요도 없습니다.
아주 좋소!
이렇게 getter/setter를 쓰면 요구사항 변화에 쉽고 빠르게 대응할 수 있습니다.
구현을 private으로, 인터페이스를 getter/setter함수로 분리했기에 가능한 일이죠. 껄껄~
OOP를 배우기 어려운 이유는
자꾸 뭔가 변경이 많이 되는 걸 프로그래밍해봐야 그 강력함을 이해하는데
학부 수준에서는 그런 큰 걸 접하는 일이 드물고
유지보수 따위 필요없는 작은 과제 프로그램만 짜고 저멀리 치워버리기 때문일겁니다.
그러니까 답은 프로젝트입니다.
너만의 프로젝트를 해 보아요!
방학 때 쓴 글임
이 글에선 프로그래머의 실수 떄문에 급작스런 요구사항 변화가 일어나지만...
SDL_Rect같은 게 없다가 생길지도 몰라...!
그건 미리 알 수도 없으니 노답이지!
댓글 영역
획득법
① NFT 발행
작성한 게시물을 NFT로 발행하면 일주일 동안 사용할 수 있습니다. (최초 1회)
② NFT 구매
다른 이용자의 NFT를 구매하면 한 달 동안 사용할 수 있습니다. (구매 시마다 갱신)
사용법
디시콘에서지갑연결시 바로 사용 가능합니다.