이펙티브 자바 4장: 모든 객체의 공통 메서드

Adam·2024년 7월 25일
0

이펙티브 자바

목록 보기
3/10
post-thumbnail

클래스와 맴버의 접근 권한을 최소화하라

정보 은닉을 얼마나 잘했는지가 컴포넌트가 잘 설계 됐는지 알 수 있는 여부

정보 은닉의 장점

  • 시스템 개발 속도를 높인다
  • 시스템 관리 비용을 낮춘다
  • 성능 최적화에 도움을 준다
  • 소프트웨어 재사용성을 높인다
  • 큰 시스템을 제작하는 난이도를 낮춰준다

모든 클래스와 맴버의 접근성을 가능한 좁혀야 한다

  • private: 멤버를 선언한 톱레벨 클래스에서만 접근할 수 있다
  • package-private: 멤버가 소속된 패키지 안의 모든 클래스에서 접근할 수 있다
  • protected: package-private 접근 범위를 포함하며, 이 멤버를 선언한 클래스의 하위 클래스에서도 접근할 수 있다
  • public: 모든 곳에서 접근할 수 있다

리스코프 치환 원칙을 지키기 위해서는 상위 클래스의 메서드를 재정의할 때는 그 접근 수준을 상위 클래스에서보다 좁게 설정할 수 없다

public 클래스의 인스턴스 필드는 되도록 public이 아니어야 한다

public 가변 필드를 갖는 클래스는 일반적으로 스레드 안전하지 않다

클래스에서 public static final 배열 필드를 두거나 이 필드를 반환하는 접근자 메서드를 제공해서는 안 된다

모듈이 도입되면서 모듈 중 공개할 것들을 선언할 수 있다→ public이나 protected여도 공개하지 않으면 모듈 외부에서는 접근이 불가능하다

public 클래스에서는 public 필드가 아닌 접근자 메서드를 사용하라

public 클래스에서는 가변 필드를 직접 노출해서는 안 된다

불변 필드라면 노출해도 위험이 덜하지만 안심할 수는 없다

변경 가능성을 최소화하라

불변 클래스: 인스턴스의 내부 값을 수정할 수 없는 클래스, 불변 인스턴스에 간직된 정보는 고정되어 객체가 파괴되는 순간까지 절대 달라지지 않는다

불변 클래스는 가변 클래스보다 설계하고 구현하고 사용하기 쉽고, 오류가 생길 여지도 적다

클래스를 불변으로 만들기 위한 5가지 규칙

  1. 객체의 상태를 변경하는 메서드를 제공하지 않는다
  2. 클래스를 확장할 수 없도록 한다
  3. 모든 필드를 final로 선언
  4. 모든 필드를 private으로 선언한다
  5. 자신 외에는 내부의 가변 컴포넌트에 접근할 수 없도록 한다

불변 객체는 근본적으로 안전하여 따로 동기화할 필요 없고 안심하고 공유할 수 있다

불변 객체 끼리는 내부 데이터를 공유할 수 있다

객체를 만들 때 불변 객체들을 구성요소로 사용하면 그 구조가 아무리 복잡하더라도 불변식을 유지하기 훨씬 수월하다

불변 객체는 그 자체로 실패 원자성을 제공

불변 클래스의 단점은 값이 다르면 반드시 독립된 객체로 만들어야 한다는 것

클래스는 꼭 필요한 경우가 아니라면 불변이여야 하기 때문에 게터가 있다고 해서 무조건 세터를 만들지 말자

불변으로 만들 수 없는 클래스라도 변경할 수 있는 부분을 최소한으로 줄이자

다른 합당한 이유가 없다면 모든 필드는 private final이어야 한다

생성자는 불변식 설정이 모두 완료된 초기화가 완벽히 끝난 상태의 객체를 생성해야 한다

상속보다는 컴포지션을 사용하라

메서드 호출과 달리 상속은 캡슐화를 깨뜨린다

상위 클래스의 구현이 달라지면 하위 클래스가 오작동 할 수 있다

상위 클래스에서 메서드가 추가되면 하위 클래스에서 허용되지 않은 원소를 추가할 수 있게 될 가능성이 생긴다

새로운 클래스를 만들고 private 필드로 기존 클래스의 인스턴스를 참조하게 해서 문제를 해결할 수 있다

상속은 반드시 하위 클래스가 상위 클래스의 진짜 하위 타입인 상황에서만 쓰여야 한다

상속을 고려해 설계하고 문서화하라. 그러지 않았다면 상속을 금지하라

상속용 클래스는 재정의할 수 있는 메서드들을 내부적으로 어떻게 이용하는지 문서로 남겨야 한다

효율적인 하위 클래스를 큰 어려움 없이 만들 수 있게 하려면 클래스의 내부 동작 과정 중간에 끼어들 수 있는 훅을 잘 선별하여 protected 메서드 형태로 공개해야 할 수 도 있다

상속용 클래스를 시험하는 방법은 직접 하위 클래스를 만들어 보는 것이 유일하기 때문에 상속용으로 설계한 클래스는 배포 전에 반드시 하위 클래스를 만들어 검증해야 한다

상속용 클래스의 생성자는 직접적으로든 간접적으로든 재정의 가능 메서드를 호출해서는 안된다

클래스를 상속용으로 만들려면 엄청난 노력이 들고 제약이 있는데, 상속용으로 설계하지 않은 클래스는 상속을 금지하는 것이 좋다

상속을 금지시키는 방법

  1. 둘 중 더 쉬운 쪽은 클래스를 final로 선언하는 방법
  2. 모든 생성자를 private이나 package-private으로 선언하고 public 정적 팩터리를 만들어 주는 방법

추상 클래스보다는 인터페이스를 우선하라

둘의 가장 큰 차이점은 추상 클래스가 정의한 타입을 구현하는 클래스는 반드시 추상 클래스의 하위 클래스가 되어야 하지만 인터페이스가 선언한 메서드를 모두 정의하고 그 일반 규약을 잘 지킨 클래스라면 다른 어떤 클래스를 상속했든 같은 타입으로 취급

인터페이스로는 계층구조가 없는 타입 프레임워크를 만들 수 있다

인터페이스는 기능을 향상시키는 안전하고 강력한 수단

복잡한 인터페이스는 골격 구현을 함께 제공하면 좋다

인터페이스는 구현하는 쪽을 생각해 설계하라

디폴트 메서드는 기존 구현체에 런타임 오류를 일으킬 수 있기 때문에 기존 인터페이스에 디폴트 메서드로 새 메서드를 추가하는 일은 꼭 필요한 경우가 아니면 피해야 한다

인터페이스는 타입을 정의하는 용도로만 사용하라

상수 인터페이스 안티패턴은 인터페이스를 잘못 사용한 예

태그 달린 클래스보다는 클래스 계층구조를 활용하라

태그 달린 클래스는 장황하고, 오류 내기 쉽고, 비효율적이다

태그 달린 클래스를 클래스 계층구조로 바꾸는 방법

  1. 계층구조의 루트가 될 추상 클래스를 정의하고 태그 값에 따라 동작이 달라지는 메서드들을 루트 클래스의 추상 메서드로 선언
  2. 태그 값에 상관없이 동작이 일정한 메서드들을 루트 클래스에 일반 메서드로 추가
  3. 모든 하위 클래스에서 공통으로 사용하는 데이터 필드들도 전부 루트 클래스로 올린다
  4. 그 결과 루트 클래스에는 추상 메서드만 남게 된다

맴버 클래스는 되도록 static으로 만들라

중첩 클래스: 다른 클래스 안에 정의된 클래스

중첩 클래스는 자신을 감싼 바깥 클래스에서만 쓰여야 하며, 그 외에 쓰임새가 있다면 톱레벨 클래스로 만들어야 한다

중첩 클래스의 종류: 정적 멤버 클래스, 비정적 멤버 클래스, 익명 클래스, 지역 클래스

정적 멤버 클래스

  • 다른 클래스 안에 선언되고, 바깥 클래스의 private 멤버에도 접근할 수 있다는 점만 제외하고 일반 클래스와 같다

비정적 멤버 클래스

  • 바깥 클래스의 인스턴스와 암묵적으로 연결
  • 정규화된 this를 사용해 바깥 인스턴스의 메서드를 호출하거나 바깥 인스턴스의 참조를 가져올 수 있다

멤버 클래스에서 바깥 인스턴스에 접근할 일이 없다면 무조건 static을 붙여서 정적 멤버 클래스로 만들어야 한다

→ static을 생략하면 인스턴스로의 숨은 외부 참조를 갖게 되어 가비지 컬렉터가 바깥 클래스의 인스턴스를 수거하지 못해 메모리 누수가 생길 수 있다

익명 클래스

  • 쓰이는 시점에 선언과 동시에 인스턴스가 만들어진다
  • 코드의 어디서든 만들 수 있다
  • 오직 비정적인 문맥에서 사용될 때만 바깥 클래스의 인스턴스를 참조할 수 있다
  • 선언한 지점에서만 인스턴스를 만들 수 있고, instanceof 검사나 클래스의 이름이 필요한 작업은 수행할 수 없다

지역 클래스

  • 지역변수를 선언할 수 있는 곳이면 실질적으로 어디서든 선언할 수 있고 유효 범위도 지역변수와 같다
  • 이름이 있고 반복해서 사용 가능
  • 비정적 문맥에서 사용될 때만 바깥 인스턴스를 참조할 수 있으며, 정적 멤버는 가질 수 없고 가독성을 위해 짧게 작성해야 한다

톱레벨 클래스는 한 파일에 하나만 담으라

해당 규칙을 따르면 컴파일러가 한 클래스에 대한 정의를 여러 개 만들어 내는 일은 사라지고, 소스 파일을 어떤 순서로 컴파일하든 바이너리 파일이나 프로그램의 동작이 달라지는 일은 결코 일어나지 않는다

profile
Keep going하는 개발자

0개의 댓글