1. 은닉, 캡슐화 (Encapsulation)
1) 정보은닉
- 사용자에게 상세한 내부 구현을 숨기고, 필요한 부분만 보이게 하는 것
- 은닉을 사용하기 위해서는 클래스 멤버변수의 접근제한자를 private으로 설정
- 은닉된 멤버변수에 접근하기 위해서는 공개된(public) 메서드를 통해 접근
① 변수의 값 변경 : setter 메서드
② 변수의 값 얻어오기 : getter 메서드
- 공개 메서드를 이용하여 데이터 변경 시 메서드 내에 데이터 유효성을 검증할 수 있는 루틴을 넣을 수 있음
- 접근 권한 체크 로직을 통해 인가되지 않은 사용자에게 중요한 데이터나 로직을 숨길 수도 있고, 제어할 수도 있음
- 외부에 공개하고 싶지 않은 메서드도 private으로 선언 가능
2) 예시
① badcase
package encapsulation.bad;
public class MyBirthday {
int year;
int month;
int day;
void showDateInfo() {
System.out.println("내 생일은");
System.out.println(year + "년");
System.out.println(month + "월");
System.out.println(day + "일");
System.out.println("이니까 선물을 준비하세요!");
}
}
package encapsulation.bad;
public class MainClass {
public static void main(String[] args) {
// 같은 패키지 내부 클래스파일을 가져다 쓸 때는 import 필요 없음
MyBirthday b = new MyBirthday();
b.year = 2024;
b.month = 13; // 13월을 넣어도 그냥 동작
b.day = 32; // 32일을 넣어도 그냥 동작
b.showDateInfo();
}
}
위와 같이 누구나 정보에 접근할 수 있기 때문에 아무런 값을 넣어도 그냥 동작한다.
② goodcase
※ 리팩토링이란?
→ 기능을 유지하면서 코드의 구조를 유지보수하기 좋게 개선하는 것
package encapsulation.good;
public class MyBirthday {
// 은닉(캡슐화) 시 변수는 무조건 private으로 처리
private int year;
private int month;
private int day;
// alt+shift+s 혹은 마우스 우클릭 후 source
// generate constructor using fields 선택
public MyBirthday(int year, int month, int day) {
this.year = year;
setMonth(month);
setDay(day);
}
// 은닉된 변수에 접근하기 위해서는
// 클래스 설계 시 미리 설정해둔 setter/getter 메서드를 이용해 접근
// setter 메서드 선언
// 1. setter 메서드는 은닉변수에 값을 저장(세팅)하기 위해 선언
// 2. 메서드의 접근제한자는 public으로 설정하고, 이름은 일반적으로 set+변수명으로 지정
// setMonth를 설계하여 오로지 1~12 중 하나만 받아서 저장하도록 지정
public void setMonth(int month) {
if(month < 1) {
this.month = 1;
} else if (month > 12) {
this.month = 12;
} else {
this.month = month;
}
}
public void setDay(int day) {
if(day < 1 || day > 31) {
this.day = 1; // 범위를 벗어나는 값이 들어올 경우 1로 고정
} else {
this.day = day; // 범위 내의 값이면 그대로 지정
}
}
void showDateInfo() {
System.out.println("내 생일은");
System.out.println(year + "년");
System.out.println(month + "월");
System.out.println(day + "일");
System.out.println("이니까 선물을 준비하세요!");
}
}
package encapsulation.good;
public class MainClass {
public static void main(String[] args) {
// 12월 32일같은 없는 날짜를 걸러주는지 체크
MyBirthday b = new MyBirthday(2024, -37, 50);
// b.day = 50; // private이므로 외부인 main에서 직접 주입 불가능
b.showDateInfo();
}
}
위와 같이 잘못된 값을 넣어도 setter로 정의해둔 값에 따라 결과가 잘 출력되는 것을 확인할 수 있다.
생성자 생성 후 메인함수에서 값을 변경할 수 없도록 하고 싶다면 setter 함수를 private 접근 지정자로 설정해두면 된다.
일반적으로 setter 함수는 private으로 두는 것이 바람직하다.