어설프게 설계된 컴포넌트와 잘 설계된 컴포넌트의 가장 큰 차이는 바로 클래스 내부 데이터와 내부 구현 정보를 외부 컴포넌트로부터
얼마나 잘 숨겼느냐다.
잘 설계된 컴포넌트는 모든 내부 구현을 완벽히 숨겨, 구현과 API를 깔끔히 분리한다.
오직 API를 통해서만 다른 컴포넌트와 소통하며 서로의 내부 동작 방식에는 전혀 개의치 않는다.
이것이 바로 정보 은닉, 캡슐화이다.
캡슐화의 장점은 정말 많다.
그 중 대부분은 시스템을 구성하는 컴포넌트들을 서로 독립시켜서 개발, 테스트, 최적화, 적용, 분석, 수정을 개별적으로 할 수 있게
해주는 것과 연관되어 있다.
- 여러 컴포넌트를 병렬로 개발할 수 있기 때문에 시스템 개발 속도를 높인다.
- 시스템 관리 비용을 낮춘다. 각 컴포넌트를 더 빨리 파악하여 디버깅할 수 있고, 다른 컴포넌트로 교체하는 부담도 적어진다.
- 정보 은닉 자체가 성능을 높여주지는 않지만, 성능 최적화에 도움을 준다. 다른 컴포넌트에 영향을 주지 않고 해당 컴포넌트만 최적화 할 수 있기 때문이다.
- 소프트웨어 재사용성을 높인다. 외부에 거의 의존하지 않고 독자적으로 동작할 수 있는 컴포넌트라면 그 컴포넌트와 함께 개발되지 않은 낯선 환경에서도 유용하게 쓰일 가능성이 크기 때문이다.
- 큰 시스템을 제작하는 난이도를 낮춰준다. 시스템 전체가 완성되지 않아도 개별 컴포넌트의 동작을 검증할 수 있기 때문이다.
자바는 정보 은닉을 위한 다양한 장치를 제공하는데, 각 요소의 접근성은 그 요소가 선언된 위치와 접근 제한자로 정해진다.
이 접근 제한자를 제대로 활용하는 것이 정보 은닉의 핵심이다.
기본 원칙은 간단하다.
모든 클래스와 멤버의 접근성을 가능한 한 좁혀야 한다.
소프트웨어가 돌바로 동작하는 한 항상 가장 낮은 접근 수준을 부여해야 한다는 뜻이다.
가장 바깥의 클래스와 인터페이스에 부여할 수 있는 접근 수준은 package-private ( defualt 생성자 ) , public 두가지다.
public으로 선언하면 공개 API가 되며, package-private로 선언하면 패키지 안에서만 접근 할 수 있다.
전에 이 내용에 대해 찾아본적이 있다.
어느 개발자가 본인의 경험담을 토대로 말해줬는데,
프로젝트 시 내가 만든 모듈을 다른 사람이 사용하거나 코드를 수정할 수 있다.
그럴 때 캡슐화를 제대로 신경쓰지 않으면 호출해서는 안되는 내부 요소를 호출하거나 수정해서 모듈이 엉망이 될 수도 있다는 것이 였다.
그 개발자는 시니어의 입장에서 주니어가 뭔가 잘 못 건드려서 해결하는데 시간을 날리거나... 버그가 터져 사고가 난다거나... 하는 것이
너무 두려워서 해커같은 외부의 적 보다 같이 프로젝트를 하는 내부의 적을 생각하며 캡슐화를 열심히 했다고 한다..ㅋㅋㅋㅋ
이 썰을 보고 캡슐화의 중요성에 대해 깨달음이 왔었던 기억이 있다.
패키지 외부에서 쓸 이유가 없다면 package-private를 선언하는 것이 좋다고 하지만,
사실 현업에서 package-private를 많이 사용하지 않는다고 생각한다.
물론 위에 적은 대로 모든 클래스와 멤버의 접근성을 가능한 한 좁혀야 하는 것이 좋다.
package-private 접근제한을 잘 활용하는 경우가 분명히 존재하긴 하나, 대부분의 경우 package-private을 제대로 활용하기가 어려운 편이다.
특히나 규모가 계속해서 커지는 프로젝트의 경우, 패키지 구조가 변경될 때마다 접근제한자가 발목을 잡을 가능성이 매우 높다는 디메리트가 있다.
그래서 현업에서는 package-private 보단 public을 주로 사용한다고 알고 있다.
public 클래스의 인스턴스 필드는 되도록 public이 아니어야 한다.
필드가 가변 객체를 참조하거나, final이 아닌 인스턴스 필드를 public으로 선언하면 그 필드에 담을 수 있는 값을 제한할 힘을 잃게 된다.
그 필드와 관련된 모든 것은 불변식을 보장할 수 없게 된다는 뜻이다.
여기에 더해, 필드가 수정될 때 다른 작업을 할 수 없게 되므로 public 가변 필드를 갖는 클래스는 일반적으로 스레드 안전하지 않다.
심지어 필드가 final이면서 불변 객체를 참조하더라도 문제는 여전히 남는다.
내부 구현을 바꾸고 싶어도 그 public 필드를 없애는 방식으로는 리팩터링 할 수 없게 된다.
이러한 문제는 정적 필드에서도 마찬가지이나, 예외가 하나 있다.
해당 클래스가 표현하는 추상 개념을 완성하는 데 꼭 필요한 구성요소로써의 상수라면 public static final 필드로 공개해도 좋다.
관례상 이런 상수의 이름은 대문자 알파벳으로 쓰며, 각 단어 사이에 밑줄(_)을 넣는다.
이런 필드는 반드시 기본 타입 값이나 불변 객체를 참조해야 한다.
특히 길이가 0이 아닌 배열은 모두 변경 가능하니, 클래스에 public static final 배열 필드를 두거나 이 필드를 반환하는 접근자 메서드를 제공해서는 안된다.
'JAVA > 이펙티브 자바' 카테고리의 다른 글
17. 변경 가능성을 최소화하라 (0) | 2023.07.01 |
---|---|
16. public 클래스에서는 public 필드가 아닌 접근자 메서드를 사용하라 (0) | 2023.07.01 |
14. Comparable을 구현할지 고려하라 (0) | 2023.07.01 |
13. clone 재정의는 주의해서 진행하라 (0) | 2023.07.01 |
12. toString을 항상 재정의하라 (0) | 2023.07.01 |