2016년 3월 9일 수요일

[c++] 정보은닉(Information Hiding) & 캡슐화(Encapsulation)

프로그램을을 사용하는 사용자가 알아야 하는 것은 프로그램 사용법이지 프로그램의 내부 동작이나 상세 구조가 아니다. 사용자가 굳이 알 필요가 없는 불필요한 정보는 숨김으로써 사용자는 최소한의 정보만으로 프로그램을 쉽게 사용할 수 있어야 한다. 
  C++에서는 클래스의 정보 은폐 기능을 지원하기 위해 private, public, protected 등의 접근제어 키워드를 통해 선언된 클래스 외부에서 직접적인 접근을 허용하지 않는것을 정보은닉이라 할 수 있다. 
  하지만 간접적 접근 경로를 제공해줘야 한다. 숨길 멤버와 공개할 멤버의 블록을 구성하도록 해서, 공개된 멤버는 외부에서 자유롭게 읽을 수 있지만 숨겨진 멤버를 참조하려고 시도하면 컴파일 과정에서 접근할 수 없다는 에러로 처리를 하면 된다.  (아래는 정보은닉의 예)
  1. #include<iostream>  
  2. using namespace std;  
  3.   
  4. class Point  
  5. {  
  6.     int x;   // x좌표의 범위 : 0 ~ 100  
  7.     int y;   // y좌표의 점위 : 0 ~ 100  
  8. public:  
  9.     int GetX(){ return x; }  
  10.     int GetY(){ return y; }  
  11.   
  12.     void SetX(int _x){ x=_x; }  
  13.     void SetY(int _y){ y=_y; }  
  14. };  
  15.   
  16. int main()  
  17. {  
  18.     int x, y;  
  19.     cout<<"좌표입력 : ";  
  20.     cin>>x>>y;  
  21.   
  22.     Point p;  
  23.     p.SetX(x);  
  24.     p.SetY(y);  
  25.   
  26.     cout<<"입력 된 데이터를 이용해서 그림을 그림"<<endl;  
  27.     return 0;  
  28. }  

캡슐화된 코드를 제공할때 함부로 건드려서는 안되는 코드들을 보호하기 위해, 프로그램의 안정적 구현을 위해서는 정보은닉이 필요하다.


캡슐화: 클래스를 정의하는데 있어서 관련있는 데이터 및 함수를 하나로 묶는것


캡슐화를 함으로써 정보 은닉도 함께 가져오는 효과를 가져오기도 한다. 아래의 캡슐화가 된 예제와 캡슐화가 안된 예제를 한번 보시죠. 

3-1)  캡슐화가 안된 예제
  1. #include<iostream>  
  2. using namespace std;  
  3.   
  4. class Point  
  5. {  
  6.     int x;   // x좌표의 범위 : 0 ~ 100  
  7.     int y;   // y좌표의 범위 : 0 ~ 100  
  8. public:  
  9.     int GetX(){ return x; }  
  10.     int GetY(){ return y; }  
  11.   
  12.     void SetX(int _x);  
  13.     void SetY(int _y);  
  14. };  
  15.   
  16. void Point::SetX(int _x)  
  17. {  
  18.     if(_x<0 || _x>100) {  
  19.         cout<<"X좌표 입력 오류, 확인 요망"<<endl;  
  20.         return;  
  21.     }  
  22.     x=_x;  
  23. }  
  24. void Point::SetY(int _y)  
  25. {  
  26.     if(_y<0 || _y>100)  
  27.     {  
  28.         cout<<"Y좌표 입력 오류, 확인 요망"<<endl;  
  29.         return;  
  30.     }  
  31.     y=_y;  
  32. }  
  33.   
  34. class PointShow  
  35. {  
  36. public:  
  37.     void ShowData(Point p)  
  38.     {  
  39.         cout<<"x좌표: "<<p.GetX()<<endl;  
  40.         cout<<"y좌표: "<<p.GetY()<<endl;  
  41.     }  
  42.   
  43. };  
  44.   
  45.   
  46. int main()  
  47. {  
  48.     int x, y;  
  49.     cout<<"좌표입력 : ";  
  50.     cin>>x>>y;  
  51.   
  52.     Point p;  
  53.     p.SetX(x);  
  54.     p.SetY(y);  
  55.   
  56.     PointShow show;  
  57.     show.ShowData(p);  
  58.   
  59.     return 0;  
  60. }  
  예를 들어 클래스를 정의하고 나서 프로젝트가 진행된 상황에서 문제를 발견, 포인트 클래스에다가 자기가 지니고 있는 변수 x,y에 대해 출력하는 기능을 넣어 줘야 되겠구나 생각을 하게 된다고 가정을 해봅시다. 하지만 이미 point라는 클래스는 이미 정해져 있고, 프로젝트가 이미 진행이 되어 있기 때문에, 기존의 클래스를 변경하는 것은 어려운 일 일것이다. 
  그래서 일반적으로 출력이란 기능이 없으므로, 이에 대한 기능을 클래스로 정의 하기에 이를 것입니다. 포인트 클래스의 객체를 인자로 받아서 리턴되는 데이터를 출력하는 형태로 클래스를 정의하는 pointshow 라는 클래스를 만들게 되는 것이죠. 
  메인함수에서 포인트 객체도 생성하고, 출력을 위해 show라는 객체도 생성하게 되고, 결과는 무리 없이 출력 되겠지만, 이것은 캡슐화가 무너졌다 라고 볼 수 있습니다.
  showdata라는 함수는 내가 정의한 포인트 함수의 x,y를 출력하기 위한 함수 이므로, 이것은 포인트에 대한 기능을 가진 것이라고 볼 수 있으므로  showdata라는 함수는 포인트 클래스 안에 존재 하는것이 맞는 것이죠. 

3-2) 캡슐화된 예제 :  위 상황에서 캡슐화가 제대로 됐다고 하면, 아래와 같은 예제가 나올 것입니다. 
  1. #include<iostream>  
  2. using namespace std;  
  3.   
  4. class Point  
  5. {  
  6.     int x;   // x좌표의 범위 : 0~100  
  7.     int y;   // y좌표의 범위 : 0~100  
  8. public:  
  9.     int GetX(){ return x; }  
  10.     int GetY(){ return y; }  
  11.   
  12.     void SetX(int _x);  
  13.     void SetY(int _y);  
  14.   
  15.     void ShowData();  //캡슐화를 위해 추가된 함수.  
  16. };  
  17.   
  18. void Point::SetX(int _x)  
  19. {  
  20.     if(_x<0 || _x>100) {  
  21.         cout<<"X좌표 입력 오류, 확인 요망"<<endl;  
  22.         return;  
  23.     }  
  24.     x=_x;  
  25. }  
  26. void Point::SetY(int _y)  
  27. {  
  28.     if(_y<0 || _y>100)  
  29.     {  
  30.         cout<<"Y좌표 입력 오류, 확인 요망"<<endl;  
  31.         return;  
  32.     }  
  33.     y=_y;  
  34. }  
  35.   
  36. void Point::ShowData()  
  37. {  
  38.     cout<<"x좌표: "<<x<<endl;  
  39.     cout<<"y좌표: "<<y<<endl;  
  40. }  
  41.   
  42. int main()  
  43. {  
  44.     int x, y;  
  45.     cout<<"좌표입력 : ";  
  46.     cin>>x>>y;  
  47.   
  48.     Point p;  
  49.     p.SetX(x);  
  50.     p.SetY(y);  
  51.     p.ShowData();  
  52.   
  53.     return 0;  
  54. }  

코드의 재활용성을 높이고 에러발생을 최소화하며 다이나믹한 속성을 높이기 위해서 캡슐화가 필요 하다.

댓글 없음:

댓글 쓰기

[Effective C++] 항목 30 : 인라인 함수는 미주알고주알 따져서 이해해 두자.

인라인 함수를 사용하면 컴파일러가 함수 본문에 대해 문맥별 최적화를 걸기가 용이해집니다. 인라인 함수의 아이디어는  함수 호출문을 그 함수의 본문으로 바꿔치기하자는 것  남발했다가는 코드의 크기가 커질 게 뻔하다. 인라인 함수로 부풀려진 ...