[Java] 9. 캡슐화(Encapsulation)란? (feat.접근제어자(Access Modifier))


캡슐화에 대한 포스팅이다. 객체지향 프로그램에서는 캡슐화 개념은 중요하다.
나중에 다시 캡슐화에 대해서 다른 카테고리에서 포스팅을 할 예정이니 해당 포스팅은 코드로 보는 캡슐화 설명이 목적인 포스팅이다.


1. 캡슐화(Encapsulation)란?

객체의 속성(data fields)과 행위(methods)를 하나로 묶고, 실제 구현 내용 일부를 외부에 감추어 은닉한다.


객체의 속성과 행위를 묶으면 응집도가 올라가므로 자율적인 객체가 된다는 장점이 있다.
그런데 캡슐화가 되지 않는다면 외부에서 내부 속성에 접근하여 사용할 수 있기 때문에
결합도가 높아지면서 다른 객체의 영향을 크게 받기 때문에 자신의 객체 상태를 유지하기 힘들어져 테스트 및 유지보수가 어려워진다.


2. 캡슐화와 접근제어자

캡슐화는 객체 내부의 속성이나 행위를 외부에서 접근제어자를 사용해서 직접 접근을 제한하는 것이다.
캡슐화는 접근제어자를 통해 이루어진다.


접근제어자(Access Modifier)

  • private : 같은 클래스 내에서만 접근이 가능
  • default : 같은 패키지 내에서만 접근이 가능
  • protected : 같은 패키지 내에서, 그리고 다른 패키지의 자손클래스에서 접근이 가능
  • public : 접근 제한이 없음


인스턴스변수(iv)를 private 접근제어자로 직접접근을 막고, public 접근제어자 메서드를 통해서 데이터에 간접 접근으로 객체 내 데이터를 유효성(오류 범위 최소화) 및 보호(잘 못 된 데이터 직접 저장)하는 개념이다. 즉, 다시 말해서 잘 못 된 데이터가 들어가는 것을 막기 위함이다.


은닉화란 캡슐화를 통해 얻어지는 실제 구현 내용 일부를 외부에 감추는 것


3. 코드로 보는 캡슐화

위에서 말했듯이 캡슐화는 객체를 데이터와, 데이터를 처리하는 행위를 묶고, 외부에는 그 행위를 보여주지 않는다.


객체지향에서 객체는 데이터와 데이터를 처리하는 집합이며, 각 객체들간에 메시지로 상호작용을 하는 것인데. 객체에 메시지를 보내는 것은 객체 내 데이터를 꺼내와서 처리하는게 아닌, 객체에게 처리할 행위를 요청하는 의미이다.


3-1. 직접 접근 예제

외부에서 CalcOrder1 객체 내에 인스턴스 변수에 직접 접근을 하는 것을 볼 수 있다.

//데이터 필드 접근제어자가 public
class CalcOrder1 {
    //메뉴 개수
    int quantity = 1;
    //메뉴 가격
    int price;
}

public class ExEncapsulation {

    public static void main(String... args) {

        int amount = 0;

        CalcOrder1 calcOrder1 = new CalcOrder1();
        //iv에 직접 접근으로 데이터 변경
        calcOrder1.price = 1000;
        calcOrder1.quantity = 3;//-3 입력 가능
        //iv에 직접 접근으로 데이터 가져와서 처리
        amount = calcOrder1.quantity * calcOrder1.price;
    }
}


3-2. 간접 접근 예제

외부에서 메서드를 통해서 CalcOrder2 인스턴스 변수에 간접 접근을 하는 것을 볼 수 있다.
일반적으로 객체 생성 후에 getter/setter 로 간접 접근을 하니 캡슐화라고 볼 수 있으나


메뉴 개수가 음수(-)값이 들어 갈 수 있는 객체이며,
객체에서 가격과 개수 데이터를 꺼내와서 금액을 계산이 되기 때문에 외부의 객체에 영향을 받는 다.
즉, CalcOrder2 객체는 다른 객체들과 결합도는 올라가고 응집도는 내려가므로으로 자율적이지 않다고 볼 수 있다.


그래서 CalcOrder2 객체는 데이터가 보호가 되지 않고 자율적이지 않기에 캡슐화라고 볼 수는 없다.

//데이터 필드 접근제어자가 private 으로 행위로 간접접근
class CalcOrder2 {
    //메뉴 개수
    private int quantity = 1;
    //메뉴 가격
    private int price;

    public int getQuantity() {
        return quantity;
    }

    public void setQuantity(int quantity) {
        this.quantity = quantity;
    }

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }
}

public class ExEncapsulation {

    public static void main(String... args) {

        int amount = 0;

        CalcOrder2 calcOrder2 = new CalcOrder2();
        //iv에 간접 접근으로 데이터 변경
        calcOrder2.setPrice(1000);
        calcOrder2.setQuantity(3);//-3 입력 가능
        //iv에 간접 접근으로 데이터 가져와서 금액 계산 처리
        amount = calcOrder2.getPrice() * calcOrder2.getQuantity();
    }
}


3-3. 캡슐화 예제

아래 코드에서는 객체 데이터에 간접 접근을 하면서 데이터를 보호하고 있다. 굳이 예로 quantity 인스턴스 변수의 값은 최소 주문 개수를 1개 이상으로 데이터 오류 범위 최소화했다.


또한 getAmount() 메서드를 통해서 금액을 계산하는 행위가 객체 내부에 있기에 외부 여러 객체로부터 결합도가 낮아지고,
CalcOrder3 객체는 객체의 속성(data fields)과 행위(methods)를 하나로 묶고, 실제 구현 내용 일부를 외부에 감추어 은닉되어 있는 것을 볼 수 있다.

//데이터 필드 접근제어자가 private 으로 행위로 간접접근
class CalcOrder3 {
    //메뉴 개수
    private int quantity = 1;
    //메뉴 가격
    private int price;

    //최소 주문 개수를 1개 이상으로 데이터 오류 범위 최소화
    public void setQuantity(int quantity) {
        this.quantity = (quantity > 0) ? quantity : 1;
    }

    public void setPrice(int price) {
        this.price = price;
    }

    //객체가 금액을 계산하는 메서드(행위)
    public int getAmount() {
        return this.price * this.quantity;
    }
}

public class ExEncapsulation {

    public static void main(String... args) {

        int amount = 0;

        CalcOrder3 calcOrder3 = new CalcOrder3();
        //iv에 간접 접근으로 데이터 변경
        calcOrder3.setPrice(1000);
        calcOrder3.setQuantity(3);//-3 입력 시 1 set
        //iv에 간접 접근으로 행위 처리
        amount = calcOrder3.getAmount();
    }
}



[참고]

  • https://galid1.tistory.com/683