[Effective Java] 아이템20 ~ 아이템24까지

두별·2023년 5월 6일
0

TIL

목록 보기
42/46

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

  • 클래스 계층구조를 만들어내는 가장 일반적인 방법은 상속이다. 상속은 강력한 도구지만, 남용하면 위험하다.
  • 상속의 단점
    • 상위 클래스의 변경사항이 하위 클래스에 영향을 줄 수 있다.
    • 상위 클래스의 작성자와 하위 클래스의 작성자 모두 그 클래스의 내부 구현에 의존한다.
    • 캡슐화가 깨진다.
  • 인터페이스의 장점
    • 인터페이스는 구현하는 클래스의 독립적인 진화를 가능하게 한다.
    • 인터페이스를 구현하는 클래스들은 서로 독립적이다. 따라서 하나의 클래스에서 다른 클래스로 쉽게 교체할 수 있다.
    • 인터페이스는 믹스인(mixin) 정의에 이상적이다.
    • 인터페이스를 사용하면 선언된 타입에 매개변수화할 수 있는 기능을 제공한다.
  • 추상 클래스의 장점
    • 추상 클래스는 클래스의 일반적인 구현을 제공할 수 있다.
    • 추상 클래스는 인터페이스로 구현할 수 없는 protected 멤버를 제공할 수 있다.
    • 추상 클래스는 하나의 클래스만 확장할 수 있지만, 인터페이스는 여러 개 구현할 수 있다.
  • 디폴트 메서드(default method)
    • Java 8부터 인터페이스에 디폴트 메서드를 제공한다. 이로 인해 인터페이스를 변경하면 이 인터페이스를 구현하는 모든 클래스가 자동으로 새로운 기능을 제공하게 된다.
    • 디폴트 메서드는 해당 인터페이스를 구현하는 모든 클래스에서 동작한다.
    • 디폴트 메서드는 Object 클래스의 메서드를 재정의할 수 없다.
  • 핵심 정리
    • 인터페이스는 다양한 구현을 허용하는 범용적인 타입을 정의하는 가장 좋은 방법이다.
    • 인터페이스로는 계층구조가 필요 없거나 작을 때도 적합하다.
    • 상속은 클래스 간의 강력한 관계가 있을 때만 사용하고, 이 외의 경우엔 항상 인터페이스를 사용하는 것이 좋다.



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

  • 인터페이스를 설계할 때는 이를 구현할 클래스를 생각해야 한다.
  • 인터페이스를 사용하는 클라이언트가 원하는 기능을 모두 제공하는 인터페이스를 만드는 것이 중요하다.
  • 인터페이스 설계 시 고려해야 할 사항
    • 인터페이스의 이름을 신중하게 선택하라.
    • 인터페이스는 자신을 구현하는 클래스의 객체를 반환하는 메서드를 작성하지 말아야 한다.
    • 인터페이스에 static 메서드를 작성할 수 있으나, 일반적으로 인스턴스 메서드보다는 기능이 제한된다.
    • 인터페이스는 기본 메서드(default method)를 제공할 수 있으나, 상황에 따라 사용 시 주의해야 한다.
  • 인터페이스를 사용하는 클라이언트 관점에서 설계하면, 인터페이스는 간결하고 사용하기 쉬워진다.
  • 핵심 정리
    • 인터페이스 설계는 까다롭다. 설계자는 인터페이스를 구현할 클라이언트를 생각하며, 세심한 주의를 기울여야 한다.
    • 인터페이스를 릴리스한 후에는 절대로 변경하지 말아야 하며, 새로운 메서드를 추가하는 것은 기존 구현체들과 충돌하지 않는 디폴트 메서드를 추가하는 것 뿐이다.



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

  • 인터페이스는 자신을 구현한 클래스의 인스턴스를 참조할 수 있는 타입 역할을 하는 것이 목적이다.
  • 상수 인터페이스(Constant Interface) 패턴은 사용하지 말아야 한다.
    • 인터페이스에 상수를 선언하는 것은 그 인터페이스를 구현하는 클래스의 네임스페이스를 오염시키는 결과를 낳는다.
    • 상수를 클래스 내부에 private static final 필드로 선언하면, 해당 상수를 사용하는 클래스 내부에서만 사용할 수 있어서, 네임스페이스 오염을 방지할 수 있다.
  • 인터페이스는 추상 메서드와 정적(static) 메서드만을 담아야 한다.
  • 디폴트(default) 메서드는 인터페이스를 설계할 때 고려해야 하지만, 남용해서는 안 된다.
    • 인터페이스의 버전을 업그레이드 하면서 디폴트 메서드가 추가될 수 있기 때문에, 디폴트 메서드는 반드시 문서화되어야 한다.
    • 디폴트 메서드를 사용할 때는, 상속받는 클래스에서 반드시 재정의해야 하는지를 검토해봐야 한다.
  • 핵심 정리
    • 인터페이스는 오직 타입을 정의하는 용도로만 사용해야 한다.
    • 인터페이스에 상수를 정의하는 것은 안 좋은 생각이다.
    • 디폴트 메서드는 인터페이스의 진화를 도와주는 기능이지만, 일관성 없는 동작을 일으킬 수 있으므로, 사용에 주의해야 한다.



아이템 23: 태그 달린 클래스보다는 클래스 계층구조를 활용하라

  • 태그 달린 클래스(Tagged Class)란, 하나의 클래스 내부에 여러 개의 태그(Enum 등) 필드를 두고, 이 필드의 값에 따라 객체의 동작을 달리하는 클래스를 말한다.
  • 태그 달린 클래스의 문제점
    • 가독성이 나쁘며, 잘못된 태그 값을 지정할 가능성이 있다.
    • 필드가 많아지면 클래스의 크기가 커지며, 메모리 공간도 낭비된다.
    • 자료형 검사나 형 변환 같은 불필요한 코드가 많이 추가된다.
  • 대신 클래스 계층구조(Class Hierarchy)를 활용하자.
    • 태그 달린 클래스 대신, 각 태그 값마다 자신만의 클래스를 작성하고, 공통적인 기능은 상위 클래스로 묶어서 처리하자.
    • 이를 통해 가독성이 좋아지고, 잘못된 태그 값 설정을 방지할 수 있으며, 코드 중복도 줄어든다.
  • 또한, 추상 클래스나 인터페이스로 공통 코드를 정의하면 코드 중복을 더 줄일 수 있다.
  • 핵심 정리
    • 태그 달린 클래스는 쓰지 말고, 클래스 계층구조를 활용하자.
    • 클래스 계층구조를 잘 설계하면, 가독성, 코드 중복도, 유지보수성 등이 향상된다.



아이템 24: 멤버 클래스는 되도록 static으로 만들어라

  • 멤버 클래스(Member Class)란, 다른 클래스 내부에 정의된 클래스를 말한다.
  • 멤버 클래스에는 정적 멤버 클래스(Static Member Class)와 비정적 멤버 클래스(Non-static Member Class, Inner Class)가 있다.
  • 정적 멤버 클래스는 외부 클래스의 인스턴스와 독립적으로 생성될 수 있으며, 외부 클래스의 정적 멤버처럼 다루어질 수 있다.
  • 비정적 멤버 클래스는 외부 클래스의 인스턴스와 함께 생성되며, 외부 클래스의 인스턴스를 참조할 수 있다.
  • 비정적 멤버 클래스는 외부 클래스의 인스턴스를 갖고 있기 때문에, 메모리를 더 많이 사용하고 생성 시간도 더 오래 걸린다.
  • 따라서, 멤버 클래스를 사용할 때는 되도록 정적 멤버 클래스를 사용하는 것이 좋다.
    • 비정적 멤버 클래스를 사용해야 하는 경우
      • 외부 클래스의 인스턴스와 함께 생성되어야 하는 경우
      • 외부 클래스의 private 필드나 메서드에 쉽게 접근해야 하는 경우
      • 다른 클래스에서는 사용하지 않을 것이라는 확신이 있을 때
  • 핵심 정리
    • 멤버 클래스는 되도록 static으로 만들어야 한다.
    • 비정적 멤버 클래스는 메모리 사용량과 생성 시간이 더 많이 들기 때문에, 사용하는 경우에는 신중해야 한다.

0개의 댓글