어떤 클래스를 만들면 이런 저런 함수를 통해 제공하는 기능이 많을 것이다.

하기 내용은 클래스 설계자보다는 클래스를 사용하여 프로그래밍을 하는 사용자 입장에서 클래스를 사용하는데 있어

편하게 확장시킬 수 있는 방법에 대한 내용이라고 생각하면 조금 더 쉽게 생각할 수 있다.

 

1
2
3
4
5
6
7
8
9
class WebBrowser
{
public:
    ...
    void clearCache();
    void clearHistory();
    void removeCookies();
    ...
};
cs

 

위와 같이 동작하는 함수들을 한 번에 하고 싶을 때도 있기 때문에, 세 함수를 모아서 호출하는 함수도 준비해 둘 수 있다.

 

<멤버 함수 ver>

1
2
3
4
5
6
7
8
9
10
11
class WebBrowser
{
public:
    ...
    void clearCache();
    void clearHistory();
    void removeCookies();
 
    void clearEverything();    //상기 3개 함수 호출.
    ...
};
cs

 

<비멤버 함수 ver>

1
2
3
4
5
6
void clearBrowser(WebBrowser& wb)
{
    wb.clearCache();
    wb.clearHistory();
    wb.removeCookies();
}
cs

 

멤버 버전인 clearEverything과 비멤버 버전인 clearBrowser 중 어떤 쪽이 더 좋은지 살펴보자.
객체 지향 법칙에 관련된 이야기를 찾아보면 데이터와 그 데이터를 기반으로 동작하는 함수는 한 데 묶여 있어야하며,
멤버 함수가 더 낫다고들 한다. 그러나 이는 잘 못된 지식이다. 분명히 객체 지향 법칙은 할 수 있는 만큼 데이터를 캡슐화하라고

주장하지만 멤버 버전인 clearEverything은 비멤버 버전인 clearBrowser보다 캡슐화 정도가 오히려 형편없다.

또한 비멤버 함수를 사용하면 WebBrowser 관련 기능을 구성하는데 있어서 패키징 유연성이 높아지는 장점도 있고 컴파일

의존도도 낮추어 WebBrowser의 확장성을 높일 수 있다.

 

우선 캡슐화하는 것이 늘어나면 그만큼 밖에서 볼 수 있는 것들이 줄어들기 때문에, 내부적으로 변경될 때 필요한 유연성이 커진다.

즉, 이미 있는 코드를 바꾸더라도 제한된 사용자들밖에 영향을 주지 않는 융통성을 확보할 수 있다는 것이다.

정리하면... 앞에서도 말 했듯이 멤버 변수에 접근할 수 있는 함수의 개수가 적을수록 캡슐화가 잘 되었다고 할 수 있다.

그러나 멤버 함수가 늘어나면 늘어날 수록 멤버 변수에 접근할 수 있는 함수의 개수도 늘어나게 되는 것이므로 똑같은 기능을

제공하는데 멤버 함수를 쓰는 것보다는 비멤버 비프랜드 함수를 사용하는 것이 더 바람직하다고 볼 수 있다.

비멤버 비프른드 함수는 어떤 클래스의 private 멤버 부분을 접근할 수 있는 함수의 개수를 늘리지 않기 때문이다.

 

좋은 방법

 

C++에서는 Webrowser가 가진 private 멤버의 캡슐화에 영향을 주지 않으면서 자연스런 방법으로 구사할 수 있다.

clearBrowser를 비멤버 함수로 두되, WebBrowserstuff와 같은 네임스페이스 안에 두는 것이다.

 

1
2
3
4
5
6
7
8
namespace WebBrowserStuff
{
    class WebBrowser { ... };
 
    void clearBrowser(WebBrowser& wb);
 
    ...
}
cs

 

이 방법은 매우 진보적인 방법이라고 볼 수 있다. 네임스페이스는 클래스와 달리 여러 개의 소스 파일에 나뉘어 흩어질 수 있기 때문이다.

clearBrowser 같은 함수들은 편의상 준비한 함수들이며 멤버도 프랜드도 아니기에, WebBrowser 사용자 수준에서 아무리 애를 써도

얻어낼 수 없는 기능은 이들도 제공할 수 없다. 예를 들어, clearBrowser가 없다면 사용자는 그냥 clearCache, clearHistory, removeCookies

를 호출하면 그만이다.

 

WebBrowser처럼 응용도가 높은 클래스는 이런 종류의 편의 함수가 꽤 많이 생길 수 있다. 즐겨찾기에 관련된 함수라든지, 인쇄에 관련된

함수도 있을 것이고, 쿠키 관리용 함수 등등이 있을 것이다.

이것들을 나누어 놓는 쉽고 깔금한 방법은, 즐겨찾기 관련 편의 함수를 하나의 헤더 파일에 몰아서 선언하고, 쿠키 관련 편의 함수는 다른

헤더 파일에 몰아서 선언하고, 인쇄 관련 편의 함수는 또 다른 헤더 파일에 몰아서 선언하는 것이다. <하기 참조>

 

<WebBrowser.h>

1
2
3
4
5
6
namespace WebBrowserStuff
{
    class WebBrowser { ... };
 
    ...
}
cs

 

<WebBrowserClear.h>

1
2
3
4
namespace WebBrowserStuff
{
    void clearBrowser(WebBrowser& wb); //clear 관련 편의 함수
}
cs

 

<WebBrowserBookmark.h>

1
2
3
4
namespace WebBrowserStuff
{
    //즐겨찾기 관련 관련 편의 함수들이 여기에 들어간다.
}
cs

 

<WebBrowserCookies.h>

1
2
3
4
namespace WebBrowserStuff
{
    //쿠키 관련 관련 편의 함수들이 여기에 들어간다.
}
cs

 

표준 C++ 라이브러리가 이러한 구조로 구성되어 있다. std 네임스페이스에 속한 모든 것들이 <C++ standardLibrary> 헤더 같은

것에 모조리 들어가 있지 않고, 몇 개의 기능과 관련된 함수들이 수십 개의 헤더(<vector>, <algorithm>, <memory> 등등..)에

흩어져 선언되어 있다.

 

이렇게 사용하면 vector 기능만 필요한 사용자는 굳이 <memory>를 #include 할 필요가 없으며 사용자가 필요한 기능에 대해서만

#include를 이용하여 사용하면 된다. 결국 사용자가 실제로 사용하는 구성요소에 대해서만 컴파일 의존성을 고려할 수 있게 된다.

그러나 멤버 함수로 사용하게 되면 이런 식으로 기능을 쪼개는 것이 불가능하므로 하나의 클래스는 그 전체가 통으로 정의되어야만

한다.

 

또한 여러 개의 헤더 파일에 나누어 놓으면 편의 함수 집합의 확장도 쉽게 할 수 있다.

만약 사용하던 중에 사용자가 추가로 다운로드 이미지에 관련된 편의 함수가 필요하다고 생각된다면 사용자가 직접 헤더 파일을 하나

추가해서 WebBrowserStuff 네임스페이스를 만들고 그 안에 관련 함수들을 집어넣기만 하면 되기 때문이다.

이런 부분 역시 클래스 멤버 함수였다면 사용자가 클래스 정의 자체를 수정할 수 없기 때문에 불가능하였을 것이다.

만약 해당 클래스를 상속받아서 사용자가 사용한다고 해도 파생클래스는 기본 클래스 안에 캡슐화된 멤버에 대한 접근권한이 없기

때문에, 이런 식의 확장은 좋은 방법이 아니다.

 

1줄 요약

 

 - 멤버 함수보다는 비멤버 비프렌드 함수를 자주 쓰도록 하자. 캡슐화 정도가 높아지고, 패키징 유연성도 커지며, 기능적인

   확장도 늘어난다.

+ Recent posts