[Java] 4. 2 접근 지정자

4. 2 접근 지정자

앞에서 OOP에서는 캡슐화 과정에서 멤버의 가시성을 정의하여 접근 수준을 지정할 수 있다고 하였습니다.

Java 언어에서는 public, protected, private 키워드를 이용하여 접근 지정할 수 있는데 이와 같은 접근 지정을 왜 해야 하는지 알아봅시다.

멤버의 가시성을 public으로 지정하면 모든 곳에서 접근이 가능합니다. 따라서 형식 개체를 사용하는 곳에서 접근해야 하는 멤버에는 가시성을 public으로 지정합니다.

멤버의 가시성을 protected로 지정하면 클래스 자신의 내부와 파생 클래스에서는 접근할 수 있지만 그 외의 클래스에서는 접근할 수 없습니다. 이에 관한 문법은 상속을 다루면서 소개하기로 할게요.

private으로 가시성을 지정하면 클래스 자신 내부에서만 접근할 수 있습니다. 개체의 상태를 나타내는 멤버 필드처럼 정보를 보호할 필요가 있는 멤버에 지정합니다. 그리고 접근 지정을 하지 않으면 디폴트 가시성이 private입니다.

[그림 4.3] 접근 지정에 따른 가시성

예를 들어 유닛을 제어하는 프로그램에서 유닛이 1시간 운동하면 체력이 1 올라가는데 최대 100까지 올라갈 수 있다고 가정합시다. 만약 유닛을 정의할 때 체력을 기억하는 멤버 필드를 다른 클래스에서 접근하여 값을 설정할 수 있으면 어떻게 될까요?

유닛 개체를 사용하는 곳에서는 체력의 최대값을 벗어나지 않게 제어하는 로직을 작성해야 할 것입니다. 그런데 시나리오에 다른 부분에서도 체력을 변경하는 부분이 여러 군데 있다면 매 번 최대값을 벗어나지 않게 제어하는 로직을 작성해야 합니다. 이는 전체 작업량을 늘어나게 합니다. 특히 유닛 개체를 사용하는 클래스가 여러 곳이라면 매 번 이와 같은 작업을 수행하는 것은 작업량 뿐만 아니라 제대로 제어하지 않는 곳이 일부분에 있으면 이것이 버그로 나타나며 이를 해결하는 비용이 상당할 수 있습니다.

[소스 4.3] 정보 은닉을 하지 않은 예

public class Unit {
    public int hp;
    public Unit(){
        hp = 0;
    }
}
public class Program {
    public static void main(String[] args){
        Unit unit = new Unit();
        System.out.println("유닛의 체력:"+unit.hp);
        unit.hp += 50;
        System.out.println("유닛의 체력:"+unit.hp);
        unit.hp += 70;
        System.out.println("유닛의 체력:"+unit.hp);
    }
}

실행 결과

유닛의 체력:0
유닛의 체력:50
유닛의 체력:120

OOP 언어에서는 캡슐화 과정에서 멤버의 가시성을 정의할 수 있게 함으로써 이와 같은 문제를 효과적으로 해결할 수 있는 방법을 제공하고 있습니다.

클래스의 멤버 필드를 캡슐화할 때 다른 클래스 영역에서 접근할 수 없게 private으로 접근 지정하고 public으로 지정한 개체의 메서드에서 멤버 필드에서 값을 설정하게 하는 것입니다. 물론 멤버 메서드도 다른 클래스 영역에서 접근할 필요 여부에 따라 접근 지정해야 높은 신뢰성을 제공하여 잠제적 버그를 줄일 수 있습니다.

먼저 정보 은닉이 필요한 체력에 관한 멤버 필드의 가시성을 private로 지정하여 다른 클래스 영역에서 접근할 수 없게 막습니다.

public class Unit {

int hp;

}

생성자 메서드에서는 기본 값으로 체력을 설정합니다.

public Unit(){

hp = 0;

}

그리고 체력을 설정하는 메서드를 캡슐화하여 최대 값을 벗어나지 않게 정의합니다. 이와 같은 메서드를 설정자 메서드라 말합니다. 만약 다른 행위에 의해 멤버 필드 값을 변경하는 것을 허용하고 다른 클래스에서 직접적으로 사용하지 말아야 한다면 이에 관한 접근 지정도 public으로 하지 않는 것이 좋습니다.

void setHp(int value){

if(value<100){

hp = value;

}

else{

hp = 100;

}

}

유닛의 체력을 제공하는 접근자 메서드를 제공합시다. 접근자 메서드는 일반적으로 단순히 멤버 필드값을 반환하는 작업을 수행합니다.

public int getHp(){

return hp;

}

유닛이 운동하면 설정자 메서드를 호출하여 개체의 상태를 변경합니다.

public void play(int tcnt){

System.out.println(tcnt+”시간 운동하다.”);

setHp(hp+tcnt);

}

이와 같이 Unit 클래스를 정의하면 Unit 클래스 외부에서 Unit의 체력을 직접 접근하여 값을 변경하지 못하기 때문에 정보 은닉을 통해 데이터 신뢰성을 높일 수 있습니다.

[소스 4.4] 정보 은닉으로 데이터 신뢰성을 높인 예

public class Unit {
    int hp;
    public Unit(){
        hp = 0;
    }
    public void play(int tcnt){
        System.out.println(tcnt+"시간 운동하다.");
        setHp(hp+tcnt);
    }
    void setHp(int value){
        if(value<100){
            hp = value;
        }
        else{
            hp = 100;
        }
    }

    public int getHp(){
        return hp;
    }
}
public class Program {
    public static void main(String[] args){
        Unit unit = new Unit();
        System.out.println("유닛의 체력:"+unit.getHp());
        unit.play(50);
        System.out.println("유닛의 체력:"+unit.getHp());
        unit.play(70);
        System.out.println("유닛의 체력:"+unit.getHp());
    }
}

실행 결과

유닛의 체력:0
50시간 운동하다.
유닛의 체력:50
70시간 운동하다.
유닛의 체력:100