추상화의 기본 단위인 클래스와 인터페이스는 자바 언어의 심장과도 같다.
클래스와 멤버의 접근 권한을 최소화 하라.
잘 설계된 컴포넌트는 외부 세계로부터 내부 로직을 얼마나 잘 숨겼느냐로 갈린다. 오직 API를 통해서만 외부 세계와 소통하는 캡슐화는 소프트웨어 설계 근간이 되는 원리.
정보 은닉 ( 캡술화의 장점 )
- 시스템 개발 속도를 높인다.
- 시스템 관리비용을 낮춘다. ( 내부 로직 파악 없이도 빠르게 컴포넌트 사용이 가능해 디버깅 및 교체 비용이 적다 )
- 성능 최적화 ( 캡슐화를 통해 결합도를 낮추고 응집도를 높여 해당 컴포넌트만 최적화가 가능 )
- 컴포넌트의 재사용성을 높인다. ( 외부에 의존하지않고 독자적으로 동작하기때문 )
자바는 모든 클래스와 접근성을 가능한 한 좁혀야 한다. 라는 기본 원칙이 존재한다.
다만 멤버 접근성을 좁히지 못하는 제약이 하나 존재함.
-> 상위 클래스의 메서드를 재정의 할 때는 그 접근 수준을 상위 클래스보다 좁게 설정 할 수 없다.
왜냐, 리스코프의 원칙 [ 부모클래스는 자식클래스 인스턴스로 대체 할 수 있어야 한다. ] 을 위반하기 때문
또한 테스트를 위해 private 접근 제어를 Public으로 공개하지 말자
private method의 테스트가 필요하단건 그 메서드의 역할과 책임이 커졌단 의미로 컴포넌트 분리를 고려해보자
또한 pulbic 가변 필드는 Thread Safety가 떨어진다. 여러 요청에 쉽게 자원을 공유하기 때문에...
final keyword를 사용해도 여전히 불안 요소를 존재한다고 함.
변경 가능성을 최소화 하라.
불변 클래스란 간단히 말해 인스턴스 내부의 값을 수정할 수 없는 클래스.
불변 인스턴스에 담긴 내부 데이터는 객체가 파괴 될 때까지 변하지 않는다.
클래스를 불변 클래스로 만들기위한 다섯가지 규칙
- 객체의 상태를 변경하는 메서드를 제공하지 않는다.
- 클래스를 확장할 수 없도록 한다.
- 하위 클래스에서 객체의 상태를 변하게 만드는 사태를 막아준다.
- 상속보단 컴포지션...
- 모든 필드를 final Keyword로 선언한다.
- 시스템 단위에서 강제하는 수단을 이용해 불변 클래스 만들기
- 모든 필드의 접근제어자를 private으로 선언한다.
- 가변 객체를 외부 세계에서 접근해 변경하는것을 막아준다. 물론 public final 도 가능은 하지만 클래스 자신의 내부 세계에서 값을 변경해야될 경우 문제가 생길 수 있다.
- 외부 세계로부터 내부 가변 컴포넌트에 접근 할 수 없도록 한다.
- 외부 세계에서 컴포넌트 내부의 필드를 직접적으로 참조 할 수 없게 만든다. ( getter, setter )
책에서는 사칙연산을 계산하는 클래스에서
모든 사칙연산 메서드가 내부 값을 반환하지않고 매번 새로운 인스턴스를 생성해 그 인스턴스를 반환하는 함수형 프로그래밍 패러다임을 예시로 불변성에대해 설명한다.
최근 함수형 프로그래밍을 취미삼아 공부하는 입장에서 OOP의 장점과 FP 의 장점을 모두 가진 멀티 패러다임의 중요성을 다시 느끼게 됬다.
상속보다는 컴포지션
메서드 호출과 달리 상속은 캡슐화를 깨트려버린다.
부모 클래스에따라 자식 클래스의 동작에 이상이 생길 수 있다. ( 객체의 유연성이 약화됨 )
그 여파로 코드 한줄 건드리지않은 자식 클래스에 문제가 발생 할 수 있다.
기존의 클래스를 확장하는 대신 새로운 클래스를 만들고 private 필드로 기존 클래스의 인스턴스를 참조하게 하는것.
그것이 컴포지션 구성이라 한다.
상속은 강력하지만 캡슐화를 해친다는 단점이 존재.
상속의 단점을 피하기위해 컴포지션과 전달을 사용하자.
추상 클래스보다는 인터페이스를 우선시하라
추상 클래스와 인터페이스의 가장 큰 차이는 추상 클래스를 구현하는 클래스는 추상 클래스의 하위 클래스가 되어야한다는 사실.
자바는 단일 상속만을 지원해 새로운 타입을 정의하는데에 문제가 발생.
그에비해 인터페이스는 선언한 메서드를 잘 구현해 놓기만 하면 된다.
인터페이스는 mixin 정의에 안성맞춤이라한다.
믹스인이란 -> 클래스가 구현할 수 있는 타입을 선택적으로 혼합해 사용하는것.
인터페이스를 사용하는편이 확장성과 유연성에 유리.
인터페이스는 구현하는 쪽을 생각해 설계하라
인터페이스에 새로운 메서드 추가는 구현체에 오류를 발생 시킬 수 있다.
태그 달린 클래스보다는 클래스 계층 구조를 활용하라
태그(tag)는 객체나 데이터의 종류나 상태를 나타내는 식별자
하나의 클래스에서 필드값으로 객체의 상태를 기준으로 분기점이 나뉜다면 매우 복잡하고 어려운 코드가 작성된다.
객체의 역할과 책임을 분리해 각각의 클래스로 역할과 책임을 나누어 사용하자.
먼저 기준이될 추상 클래스를 정의 후 각각의 상태 혹은 타입, 기준별로 구현 클래스를 작성해나가자.
이번 4장은 추상화를 통한 역할과 책임의 분리에 대해 이야기하고 있다.
객체지향 설계에서는 캡슐화를 강화하기 위해 역할과 책임을 명확히 분리하는 것이 중요
추상화를 구현하기 위해 인터페이스와 추상 클래스를 적절히 활용하며, 객체의 응집도를 높이기 위해 상속보다는 인터페이스를 사용하는 것이 바람직하다.
객체지향 프로그래밍의 핵심은 이렇게 역할과 책임이 분리된 각각의 객체 간의 협력을 통해 시스템을 구성하는 것이므로,
이 점을 항상 염두에 두고 설계를 진행해야 한다.
'책 > EffectiveJava' 카테고리의 다른 글
10장 예외 (0) | 2024.09.05 |
---|---|
1장 객체의 생성과 파괴 (0) | 2024.08.24 |