캡슐화(Encapsulation)는 객체 지향 프로그래밍의 중요한 개념 중 하나다. 캡슐화는 데이터와 해당 테이터를 처리하는 메서드를 하나로 묶어서 외부에서의 접근을 제한하는 것을 말한다. 캡슐화를 통해 데이터의 직접적인 변경을 방지하거나 제한할 수 있다.
캡슐화는 쉽게 이야기해서 속성과 기능을 하나로 묶고, 외부에 꼭 필요한 기능만 노출하고 나머지는 모두 내부로 숨기는 것이다.
데이터와 데이터를 처리하는 메서드를 하나로 모으는 것에 더 나아가 캡슐화를 안전하게 완성할 수 있게 해주는 장치가 바로 접근 제어자다.
그럼 어떤 것을 숨기고 어떤 것을 노출해야 하는지 알아보자.
객체에는 속성(데이터)과 기능(메서드)이 있다. 캡슐화에서 가장 필수로 숨겨야 하는 것은 속성(데이터)이다.
Speaker
의 volume
을 살펴보면 객체 내부의 데이터를 외부에서 함부로 접근하게 두면, 클래스 안에서 데이터를 다루는 모든 로직을 무시하고 데이터를 변경할 수 있다. 결국 모든 안전망을 다 빠져나가게 되고 따라서 캡슐화가 깨진다.
객체의 데이터는 객체가 제공하는 기능인 메서드를 통해서 접근해야 한다.
객체의 기능 중에서 외부에서 사용하지 않고 내부에서만 사용하는 기능들이 있다. 이런 기능도 모두 감추는 것이 좋다. 사용자에게 내부에서 사용하는 기능까지 모두 알려준다면, 사용자가 객체에 대해 너무 많은 것을 알아야한다.
사용자 입장에서 꼭 필요한 기능만 외부에 노출하고 나머지 기능은 모두 내부로 숨기자.
정리하면, 데이터는 모두 숨기고, 기능은 꼭 필요한 기능만 노출하는 것이 좋은 캡슐화이다.
package access;
public class BankAccount {
private int balance;
public BankAccount() {
balance = 0;
}
// public 메서드 : deposit
public void deposit(int amount) {
if (isAmountValid(amount)) {
balance += amount;
} else {
System.out.println("유효하지 않은 금액입니다.");
}
}
// public 메서드 : withdraw
public void withdraw(int amount) {
if (isAmountValid(amount) && balance - amount >= 0) {
balance -= amount;
} else {
System.out.println("유효하지 않은 금액이거나 잔액이 부족합니다.");
}
}
// public 메서드 : getBalance
public int getBalance() {
return balance;
}
// private 메서드 : isAmountValid
private boolean isAmountValid(int amount) {
// 금액이 0보다 커야함
return true;
}
}
command
+ N
→ Constructor
package access;
public class BankAccountMain {
public static void main(String[] args) {
BankAccount account = new BankAccount();
account.deposit(10000);
account.withdraw(3000);
System.out.println("balance = " + account.getBalance());
}
}
balance
: 데이터 필드는 외부에 직접 노출하지 않는다. BankAccount
가 제공하는 메서드를 통해서만 접근할 수 있다.isAmountValid()
: 입력 금액을 검증하는 기능은 내부에서만 필요한 기능이다. 따라서 private
을 사용했다.deposit()
: 입금withdraw()
: 출금getBalance()
: 잔고BankAccount
를 사용하는 입장에서는 단 3가지 메서드만 알면 된다. 나머지 복잡한 내용은 모두 BankAccount
내부에 숨어있다.
만약 isAmountValid()
를 외부에 노출한다면? BankAccount
를 사용하는 개발자 입장에서는 아마도 입금과 출금 전에 본인이 먼저 isAmountValid()
를 사용해서 검증을 해야 하나? 라고 의문을 가질 것이다.
만약 balance
필드를 외부에 노출하면 어떻게 될까? BankAccount
를 사용하는 개발자 입장에서는 이 필드를 직접 사용해도 된다고 생각할 수 있다. 왜냐하면 외부에 공개하는 것은 그것을 외부에서 사용해도 된다는 뜻이기 때문이다. 결국 모든 검증과 캡슐화가 깨지고 심각한 문제가 발생할 수 있다.
접근 제어자와 캡슐화를 통해 데이터를 안전하게 보호하는 것은 물론이고, 객체를 사용하는 개발자 입장에서 해당 기능을 사용하는 복잡도도 낮출 수 있다.