들어가며
자바에서 말하는 '정보'는
클래스의 '인스턴스 변수'를
의미한다.
따라서 정보를 은닉한다는
것은 인스턴스 변수를
숨긴다는 것이다.
정보를 은닉해야 하는 이유
다음 예제를 시작으로
이야기를 전개하고자 한다.
다음 예제는 컴파일도
실행도 잘 된다.
다만 내용상 그리고 의미상
문제가 되는 부분이 있다.
class Circle{
double rad = 0; // 원의 반지름
final double PI = 3.14;
public Circle(double r){
setRad(r); // 아래에 정의된 setRad 메소드 호출을 통한 초기화
}
public void setRad(double r){
if(r < 0){ // 반지름은 0보다 작을 수 없으므로
rad = 0;
return; // 이 위치에서 메소드를 빠져 나감
}
rad = r;
}
public double getArea(){
return (rad * rad) * PI; // 원의 넓이 반환
}
}
class UnsafeCircle{
public static void main(String args[]){
Circle c = new Circle(1.5);
System.out.println(c.getArea());
c.setRad(2.5);
System.out.println(c.getArea());
c.setRad(-3.3);
System.out.println(c.getArea());
c.rad = -4.5; // 잘못된 접근 방법, 그리고 문제가 되는 부분
System.out.println(c.getArea());
}
}
/*
실행 결과
7.065
19.625
0.0
63.585
*/
위 예제의 다음 부분에서
보이듯이
인스턴스 변수는 선언과
동시에 초기화를 할 수
있다.
특히 PI의 경우 그 값이
상수이므로
생성자를 통한 초기화보다
이러한 방식의 초기화가 더
어울린다.
class Circle{
double rad = 0;
final double PI = 3.14;
. . .
}
이어서 다음 메소드의
정의를 관찰하자.
public void setRad(double r){
if(r < 0){ // 반지름은 0보다 작을 수 없으므로
rad = 0;
return; // 이 위치에서 메소드를 빠져 나감
}
rad = r;
}
위 메소드의 정의를 통해서
Circle 클래스를
정의한 이의 다음 의도를
읽을 수 있다.
반지름의 길이 rad에 0보다 작은 값이
저장되는 일이 발생하지 않도록 하겠다.
때문에 이러한 의도를 따르기
위해서라도
반지름의 길이를 변경할 때에는
반드시 위의 메소드 호출을
통해서만 변경을 진행해야
한다.
이렇듯 인스턴스 변수에
저장되는 값의 종류와
범위는
해당 클래스를 정의한
사람이 가장 정확히 안다.
따라서 클래스 사용자가
잘못된 값을
인스턴스 변수에 저장하지
않도록
위와 같은 유형의 메소드를
제공해야 한다.
그런데 위의 예제에서는
프로그램 사용자의 실수로
다음과 같은 잘못된 접근이
발생하였다.
c.rad = -4.5; // 잘못된 접근 방법, 그리고 문제가 되는 부분
이렇듯 인스턴스 변수의
직접적인 접근을 허용하면,
컴파일 과정에서 드러나지
않는 중대한 실수가 발생할
수 있다.
이러한 오류는 실행 결과에서도
드러나지 않아 더 큰 문제가 된다.
때문에 위와 같은 접근을
허용하지 않도록
클래스를 설계할 필요가 있다.
그리고 이러한 클래스의
설계를 가리켜 '정보 은닉'
이라 한다.
정보의 은닉을 위한 private 선언
'정보 은닉'이 의미하는
바는 매우 중요하지만
실제로 클래스를 대상으로
정보를 은닉하는 방법은
어렵지 않다.
먼저 인스턴스 변수 앞에
private 선언을 추가한다.
그리고 앞서 예제에서
보였듯이
해당 인스턴스 변수에
접근할 수 있는 메소드를
별도로 제공하면
'정보 은닉'이 완료된다.
그럼 이와 관련하여 다음
예제를 관찰하자.
class Circle{
private double rad = 0;
final double PI = 3.14;
public Circle(double r){
setRad(r);
}
public void setRad(double r){
if(r < 0){
rad = 0;
return;
}
rad = r;
}
public double getRad(){
return rad;
}
public double getArea(){
return (rad * rad) * PI; // 원의 넓이 반환
}
}
class UnsafeCircle{
public static void main(String args[]){
Circle c = new Circle(1.5);
System.out.println("반지름: " + c.getRad());
System.out.println("넓 이: " + c.getArea() + '\n');
c.setRad(3.4);
System.out.println("반지름: " + c.getRad());
System.out.println("넓 이: " + c.getArea());
}
}
/*
실행 결과
반지름: 1.5
넓 이: 7.065
반지름: 3.4
넓 이: 36.2984
*/
클래스 Circle의 다음 인스턴스
변수가 private로 선언되었다.
private double rad = 0; // 클래스 내부 접근만 허용
그리고 이것이 의미하는
바는 다음과 같다.
변수 rad는 클래스 내부에서만 접근을 허용하겠다.
즉, Circle 클래스 내에
정의된 메소드 내에서의
접근만 허용하겠다는 뜻이다.
따라서 다음과 같이 클래스
외부에서 private로 선언된
멤버에 접근할 경우
컴파일 오류가 발생한다.
public static void main(String args[]){
Circle c = new Circle(1.5);
. . .
c.rad = -4.5; // 컴파일 오류 발생
. . .
}
물론 인스턴스 변수 rad를
private로 선언했으니,
다음과 같은 유형의 두
메소드를 제공할 필요는
있다.
// rad의 값을 저장(수정)
public void setRad(double r){
if(r < 0){
rad = 0;
return;
}
rad = r;
}
// rad에 저장된 값을 반환
public double getRad(){
return rad;
}
메소드 setRad는 '값의 설정'을
위한 메소드이고,
메소드 getRad는 '값의 참조'를
위한 메소드이다.
이렇듯 값의 설정과 참조를
위한 메소드를 가리켜
각각 다음과 같이 부른다.
- 게터(Getter)
→ 인스턴스 변수의 값을 참조하는 용도로 정의된 메소드
→ 변수의 이름이 name일 때, 메소드의 이름은 getName으로 짓는 것이 관례 - 세터(Setter)
→ 인스턴스 변수의 값을 설정하는 용도로 정의된 메소드
→ 변수의 이름이 name일 때, 메소드의 이름은 setName으로 짓는 것이 관례
private로 선언된 모든 인스턴스
변수를 대상으로
게터와 세터를 반드시 정의해야
하는 것은 아니다.
필요에 따라 정의하면 된다.
그러나 당장 필요하지 않더라도
나중을 고려하여
게터와 세터를 정의하기도
한다.
참고 및 출처
|