객체의 필드(멤버 변수들), 메소드를 하나로 묶고 실제 구현 내용 일부를 외부에 감추어 은닉한다.
즉, 외부에서 변수에 직접 접근할 수 없도록 하고 오직 메소드를 통해서만 값이 변경될 수 있도록 한다. (e.g. getter, setter)
정보은닉 또는 캡슐화를 사용하는 이유
정보은닉은 시스템을 구성하는 컴포넌트 사이의 결합도 또는 의존성을 낮춘다 (decouple)
구현 코드를 외부에서 변경 불가하게 하므로, 해당 기능을 변경해야 하는 상황이 발생할 경우 특정 클래스로만 변화가 수렴된다. (즉, 요구사항 변화에 따른 코드 수정 범위를 최소화)
개발 속도 UP
각각의 컴포넌트를 병렬적으로 개발할 수 있다
관리 비용 down
컴포넌트 각각에 대한 이해가 빨라지고, 다른 컴포넌트에 영향 끼칠 걱정없이 디버깅 진행이 가능하다.
성능 최적화 UP
시스템이 다 완성된 다음, 어떤 컴포넌트가 문제를 일으키는지 프로파일링하기에 좋다.
재사용성 UP
컴포넌트간 의존성이 낮으므로, 각 컴포넌트은 다른 소프트웨어 개발에도 사용 가능하다.
큰 시스템 개발시 위험 부담 Down
시스템 전체가 덜 되었더라도 각 모듈 별 성공 여부를 입증할 수 있음
정보 은닉을 위한 접근 제어(Access Modifier) 메커니즘
자바는 정보 은닉을 위한 장치로 접근 제어 메커니즘을 사용하는데 이 접근 제한자를 잘 사용하는 것이 정보 은닉의 핵심!
(이 장의 핵심)
적용 문법
class 선언구 : public or package-private 만 가능 (protected 나 private는 올 수 없음)
변수, 생성자, 메소드 선언구 : 4가지 다 적용 가능
종류 및 용도
키워드
class
subclass
package
world
설명
private
O
동일클래스
package-private (=default =friendly)
O
O
동일클래스, 동일패키지의 (다른/하위)클래스
protected
O
O
O
동일클래스, 동일패키지의 (다른/하위)클래스, 다른 패키지의 하위클래스
public
O
O
O
O
동일클래스, 동일패키지의 (다른/하위)클래스, 다른 패키지의 하위클래스, 다른 패키지의 다른 클래스
public > protected > default > private
그래서 잘 설계된 컴포넌트를 만들려면 어떻게?
모든 클래스와 멤버의 접근성을 가능한 한 좁히자
최상위 레벨 클래스나 인터페이스는 가능한 package-private로 선언
public으로 선언하게 되면 호환성을 보장하기 위해 해당 개체를 계속 지원해야된다
package-private로 선언된 최상위 클래스나 인터페이스를 사용하는 클래스(사용자 클래스)가 하나뿐이라면 사용자 클래스의 private static 중첩 클래스로 고려 (inner class or interface)
필드나 메서드, 중첩 클래스(nested class), 중첩 인터페이스(neseted interface) 같은 멤버의 접근 권한은 최대한 private (꼭 다른 클래스가 사용해야 하는 경우만 package-private)
public 클래스의 멤버들은 protected 사용을 자제(최대한 package-private)
공개 API일수록 영원히 지원돼야 하기 때문 (또는 내부 동작 방식을 API 문서에 적어 사용자에게 공개해야할 수 있음)
객체 필드(instance field)는 절대로 public으로 선언하면 안된다
인스턴스 필드가 final이 아니거나 또는 가변 객체의 final 참조일 때 그 필드를 public으로 지정하면 외부에서 이 필드의 값을 임의로 변경 가능하므로 그 필드가 갖는 값을 제한 할 수 없게 된다.
그 필드를 포함하는 상수식을 사용할 수 없게 된다는 말
또한, 변경 가능 public 필드를 가진 클래스는 다중 스레드에 안전하지 않다
필드의 값이 변경될 때 어떤 조치도 취할 수 없음
요소가 하나라도 있는 배열은 항상 가변적이라는 것에 주의하자.
이런 경우 반드시 기본 자료형 값들을 갖거나, 변경 불가능 객체를 참조
public static final 배열 필드를 두거나, 배열 필드를 반환하는 접근자(accssor)를 정의하면 안된다(길이가 0이 아닌 배열은 언제나 변경가능)
class Main {
// 보안 문제를 초래할 수 있는 코드
public static final Integer[] VALUES = {1 , 2, 3 };
}
class Test{
public static void main(String [] args){
System.out.println(Main.VALUES[0]); // 1
Main.VALUES[0] = 1293812;
System.out.println(Main.VALUES[0]); // 1293812
}
}
방안1) public으로 선언되었던 배열을 private 로 바꾸고, 변경 불가능한 public 리스트를 만든다.
단, VALUES 수정 시 런타임 에러 발생 주의 (컴파일 시점에 알 수 없음)
private static final Thing[] PRIVATE_VALUES = { ... };
public static final List<Thing> VALUES =
Collections.unmodifiableList(Arrays.asList(PRIVATE_VALUES));
방안2) 배열은 private로 선언하고, 해당 배열을 복사해서 반환하는 public 메서드 추가
private static final Thing[] PRIVATE_VALUES = { ... };
public static final Thing[] values() {
return PRIVATE_VALUES.clone();
}
JAVA 9의 모듈 시스템
패키지가 클래스들의 묶음이듯, 모듈은 패키지들의 묶음이다.
모듈은 자신에 속하는 패키지 중 공개(export)할 거들을 선언 (module-info.java 파일에)
클래스들 외부에 공개하지 않으면서도 같은 모듈을 이루는 패키지 사이에서는 자유롭게 공유할 수 있음
요약
접근성을 가능한 한 최소한으로 하라
최소한의 public API를 설계한 다음 나머지는 API에서 제외하라
public static final 필드를 제외한 어떤 필드도 public으로 선언하지 마라
어설프게 설계된 컴포넌트와 잘 설계된 컴포넌트의 가장 큰 차이
정보은닉(information hiding) 또는 캡슐화(encapsulation)
정보은닉 또는 캡슐화를 사용하는 이유
정보 은닉을 위한 접근 제어(Access Modifier) 메커니즘
적용 문법
종류 및 용도
그래서 잘 설계된 컴포넌트를 만들려면 어떻게?
모든 클래스와 멤버의 접근성을 가능한 한 좁히자
객체 필드(instance field)는 절대로 public으로 선언하면 안된다
요소가 하나라도 있는 배열은 항상 가변적이라는 것에 주의하자.
JAVA 9의 모듈 시스템
요약
참고 자료