캡슐화(encapsulation)는 객체 지향 프로그래밍에서 다음 2가지 측면이 있다.
우리는 캡슐 약 내부에 무엇이 들어있는지 알 수 없다.
그런데 분명 캡슐 약 안에는 여러 가지 성분들이 들어있는 것을 알고,
캡슐의 용도 또한 알고 있다. 약 먹으면 몸이 나아지는 것!!그런데 알약 껍질에 의해 물리적으로 접근할 수 없기 때문에
캡슐에 무엇이 들어있는지 우리는 모른다.
자바의 캡슐화는 알약 껍질처럼 외부 접근에 대한 차단이고, 차단의 방법은 접근 제어자를 사용한다.
접근 제어자 | 접근 제한 범위 |
---|---|
public | 접근 제한 x |
protected | 동일 패키지 + 상속관계에 있는 클래스만 접근 가능 |
default | 동일 패키지 내에서만 접근 가능 |
private | 동일 클래스 내에서만 접근 가능 |
간단한 소스코드로 알아보자.
class Capsule {
private int number;
public Capsule(int number) {
this.number = number;
}
public double getHalf() {
return number / 2;
}
}
class Main {
public static void main(String[] args) {
Capsule capsule = new Capsule(10);
System.out.println(capsule.getHalf());
}
}
👉 int
값을 초기값으로 갖는 객체가 있고, 그 값의 절반을 반환하는 getHalf()
라는 메소드가 존재한다.
❓ 여기서 캡슐화는 바로 Capsule 클래스 자체를 의미한다.
위 코드가 캡슐화의 정의를 만족하는지 보자.
객체 지향 캡슐화 : 데이터와, 데이터를 처리하는 행위를 묶고, 외부에는 그 행위를 보여주지 않는 것.
Capsule 클래스는
int
값의 데이터를 가지고 있다.getHalf
라는 데이터를 처리하는 행위 또한 가지고 있다.getHalf()
를 사용할 수는 있지만, 구현이 어떻게 되어 있는지는 알 수 없다.즉, 캡슐화되어 있다고 말할 수 있다!
잘 된 캡슐화를 통해 얻을 수 있는 가장 큰 이점은
이 이점들이 왜 필요한지 코드로 알아보자!
class Product {
int price = 10000;
public Product(int price) {
this.price = price;
}
}
class Product {
private int price = 10000;
public Product(int price) {
this.price = price;
}
public int getPrice() { // getter 추가
return price;
}
}
public void first(Product product) {
double discountedPrice = product.getPrice() * 0.9;
show(discountedPrice);
}
상품의 가격을 가지고 와서 10프로 할인된 가격을 구하고, 다른 로직으로 넘겼다.
public void first(Product product) {
double discountedPrice = product.getPrice() * 0.9; // 중복
show(discountedPrice);
}
public void second(Product product) {
double discountedPrice = product.getPrice() * 0.9; // 중복
secondShow(discountedPrice); // 할인된 금액을 show와 다르게 사용하는 메서드
}
코드에서 중복이 일어났다.
한 줄짜리 코드 중복 좀 되면 어때??? 할 수 있지만, 이러한 로직이 수십 개,, 수백 개 더 필요하면 일일이 타이핑하는 것은 정말 힘들 것이다.
🤔 코드의 중복은 좀 별로군?
다음으로는 데이터를 처리하는 방식이 외부에 드러나지 않는 것은
어떤 이점이 있는지 알아보자.
public void first(Product product) {
double discountedPrice = product.getPrice() * 0.8; // 20프로 할인
show(discountedPrice);
}
🤔 캡슐화를 지키기 위한 규칙 중에는 Tell, Don't Ask 라는 원칙이 있다.
객체 내부의 데이터를 꺼내와서 처리하는 게 아닌, 객체에게 처리할 행위를 요청하라는 행위이다.
이러한 행위를 우리는 "객체에 메세지를 보낸다" 라고 말한다.
데이터를 객체로부터 받아오는 것이 아닌, 객체에게 처리를 요청하는 방식의 코드를 작성해 보자.
class Product {
private int price = 10000;
public Product(int price) {
this.price = price;
}
public int getDiscountedPrice() { // 할인된 금액을 반환하도록
return price * 0.9;
}
}
public void first(Product product) {
double discountedPrice = getDiscountedPrice(); // 할인된 금액을 알려줘!
show(discountedPrice);
}
이렇게 하면 위에서 생겼던 문제들이 다 해결된다.
public int getDiscountedPrice() {
return price * 0.8; // 20프로 할인
}
Product 클래스의 getDiscountedPrice()
로직만 수정하면 끝이다.
객체지향 프로그래밍에서 캡슐화는 단순히 데이터를 숨기는 개념을 넘어서, 코드를 더 안정적이고 유지보수하기 쉽게 만들어주는 핵심 원칙이다.
캡슐화를 잘 활용하면
탄탄한 코드 구조를 만들 수 있다.
앞으로 코드를 작성할 때
"이 객체가 스스로 처리하도록 만들 수 있을까?"
"외부에서 직접 데이터를 조작하지 않도록 보호할 수 있을까?"
이런 질문을 스스로에게 계속해서 던지면서 캡슐화를 적용할 수 있도록 노력해 봐야겠다! 🚀