Effective Java 3/E - (4) 클래스와 인터페이스

신복호·2020년 12월 6일
0

Effactive JAVA 3/E

목록 보기
4/12
post-thumbnail

4장 클래스와 인터페이스

아이템 15. 클래스와 맴버의 접근 권한을 최소화해라

  • 정보 은닉의 장점

    • 개발 속도가 높다. (여러 컴포넌트를 병렬로 개발할수 있기 때문에)

    • 관리비용이 낮다. (빨리 파악할수 있고 다른 컴포넌트 교체의 비용 적다)

    • 성능 최적화에 도움이 된다.

    • 소프트 웨어 재사용성이 높여진다.

    • 큰 시스템을 제작하는 난이도를 낮춰준다.

  • 접근 지정자의 종류

    • private
      • 맴버를 선언한 탑레벨 클래스에서만 접근 가능하다.
    • package-private (생략)
      • 맴버가 소속된 패키지 안에 모든 클래스에서 접근할수 있다. 기본 접근 지정자이다.
      • 단 인터페이스의 맴버는 기본적으로 public이 적용된다.
    • protected
      • package-private의 접근 범위를 포함하며, 이 멤버를 선언한 클래스의 하위 클래스에서도 접근할 수 있다.
    • public
      • 모든 곳에서 접근할 수 있다.
  • public 클래스의 인스턴스 필드는 되도록 public이 아니어야 한다.

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

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


아이템 16. public 클래스에는 public 필드가 아닌 접근자 메서드를 사용해라

  • public 클래스는 절대 가변 필드를 직접 노출해서는 안된다.

  • 불변 필드라면 노출에 덜 위험하지만 그래도 안심할수 없다.

  • 클래스의 맴버 변수는 private로 바꾸고 public 접근자를 추가해서 사용하자

class Circle {
    private int x;
    private int y;
	
    public int getX() {return x;}
    public int getY() {return y;}
    
    public void setX(int x) {this.x = x;}
    public void setY(int y) {this.y = y;}
}

아이템 17. 변경 가능성을 최소화해라

불변 클래스란 인스턴스의 내부 값을 수정할수 없는 클래스를 말한다. 객체가 소멸되지 전까지 절대로 달라지지 않는다. 불변 클래스는 가변 클래스보다 설계하고 구현하고 사용하기 쉬우며 오류가 발생한 소지도 적고 안전하다.

  • 클래스를 불변으로 만들려면 다음 다섯가지 규칙을 따라야 한다.

    • 객체의 상태를 변경하는 메서드를 제공하지 않는다.

    • 클래스를 확장할 수 없도록 한다.

      • 하위 클래스에서 객체의 상태를 변하게 하는것을 막는다. (final 선언)
    • 모든 필드를 final로 선언한다.

      • 개발자의 의도를 명확하게 드러내는 방법
    • 모든 필드를 private으로 선언한다.

      • 필드가 참조하는 가변 객체에 대한 직접 접근을 막는다.
    • 자신 이외에는 내부의 가변 컴포넌트에 접근할수 없도록 한다.

  • 불변 클래스와 불변 객체의 특징

    • 불변 객체는 단순하다.

    • 불변 객체는 근본적으로 스레드 안전하여 따로 동기화 할 필요가 없다.

    • 불변 객체는 자유롭게 공유할 수 있음을 물론, 불변 객체 끼리는 네부 데이터를 공유할수 있다.

    • 객체를 만들때 다른 불변 객체들을 구성요소로 사용하면 이점이 많다.

    • 불변 객체는 그 자체로 실패 원자성을 제공한다.

    • 불변 클래스는 값이 다르면 반드시 독힙된 객체로 만들어야 하는 단점이 있다.

클래스는 꼭 필요한 경우가 아니라면 불변으로 설계해야 한다.


아이템 18. 상속보다는 컴포지션을 사용해라

  • 상속 ??

    상위 클래스로부터 하위클래스에 변수/메소드를 물려 받아 사용하는것

  • 메소드 호출과 달리 상속은 캡슐화를 깨트린다.

  • 컴포지션 ??

    다른 객체들로 하나의 객체를 생성하는 행위

class Engine {
   public Engine() {
   
   }
}

class Vehicle {
   Engine engine;

   Vehicle(Engine e) {
      engine = e;
   }
}

public class Ride {
   public static void main(String[] args) {
      Engine e = new Engine();
      Vehicle v = new Vehicle(e);
   }
}

출처: https://bounce4me.tistory.com/63 [내 멋대로 살기]
  • 컴포지션의 장점

    • 상위 클래스가 바뀌어도, 전달 클래스만 수정해주면 된다.

    • 하위 클래스에서 기능을 선택적으로 구현할 수 있다.

상속은 상위 클래스와 하위 클래스가 순수한 is-a 관계일때만 사용합시다.


아이템 19. 상속을 고려해 설계하고 문서화 해라. 그렇지 않다면 상속을 금지하자

  • 상속을 고려한 설계와 문서화

    • 상속용 클래스는 재정의할 수 있는 메소드들을 내부적으로 어떻게 이용하는지 문서로 남겨야 한다. (@implSpec : 자바 개발팀이 내부적으로 자바독의 커스텀태그 기능을 사용하는 규약)

    • 클래스의 내부 동작 과정 중간에 끼어들 수 있는 훅을 잘 선별하여 protected 메소드 형태로 공개해야 할 수도 있다.

    • 상속용으로 설계한 클래스는 배포 전에 반드시 하위 클래스를 만들어 검증해야 한다.

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

    • 상속용으로 설계하지 않은 클래스는 상속을 금지한다.


아이템 20. 추상 클래스보다는 인터페이스를 우선순위에 두자

추상 클래스

하나 이상의 추상 메소드 (abstract method)를 포함하는 클래스이거나 abstract로 정의된 클래스를 말한다.

abstract class Rank {
	abstract void rankName();
} // 생략... 

-> 일종의 미완성 설계도로 표현할수 있다.

인터페이스

다른 클래스를 작성할때 기본이 도는 틀을 제공하면서, 다른 클래스 사이의 중간 매개 역할꽈지 담당하는 역할을 한다.

public interface Rank {
 //.. 생략
}

public interface RankController implements Rank {
	//.. 생략
}

-> 일종의 기본 설계도로 표현할수 있다.

  • 인터페이스 장점
    • 기존 클래스에도 손쉽게 새로운 인터페이스를 구현해 넣을수 있다.
    • 인터페이스로는 계층구조가 없는 타입 프레임워크를 만들수 있다.
    • 레퍼클래스와 함께 사용하면, 인터페이스는 기능을 향상시키는 안전하고 강력한 수단이된다.(상속해서 만든 클래스는 래퍼 클래스보다 활용도가 떨어지고 꺠지기는 쉽다.)

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

Java 8 이전에는 기존 구현체를 깨트리지 않고는 인터페이스에 메소드를 추가할 방법이 없었다.
Java 8이 등장하면서 기존 인터페이스에 메소드를 추가할 수 있도록 디폴트 메소드가 등장하였지만 위험이 완전하게 없어지진 않았다.

-> 즉 디폴트 메소드는 컴파일이 성공하더라도 기존 구현체에 런타임 오류를 일으킬수 있다.

-> 인터페이스를 설계할 떄는 여전히 세심한 주의를 기울여야 한다.


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

  • 인터페이스는 자신을 구현한 클래스의 인스턴스를 참조할 수 있는 타입 역할을 한다.

    -> 클래스가 어떤 인터페이스를 구현한다는 것은 자신의 인스턴스로 무엇을 할 수 있는지를 클라이언트에 얘기해주는 것이다.

    -> 맞지 않는 예) 상수 인터페이스 안티 패턴

    public interface Context {    
    
       static final douber AVERAGE_SCORE = 6.022;
     }
    

    -> 상수를 공개 할 목적이라면 여러가지 선택지가 있다. (Integer와 Double 클래스에 선언된 MIN_VALUE, MAX_VALUE 상수)

  • 특정클래스나 인터페이스와 강하게 연관된 상수라면 그 클래스나 인터페이스 자체에 추가해야 한다.

    -> ex) 모든 숫자 기본 타입의 박싱 클래스(Integer,Double의 MIN_VALUE / MAX_VALUE)

  • 열거 타입으로 나타내기 적합한 상수라면 열거타입으로 만든 후에 공개한다 (아이템 34)

  • 그것도 아니라면, 인스턴스화 할수 없는 유틸리티 클래스(아이템 4)에 담아 공개

정리 : 인터페이스는 타입을 정의하는 용도로만 사용하고 , 상수 공개용 수단으로 사용하지 말자


아이템 23. 테그 달린 클래스 보다는 클래스 계층 구조를 활용해라

테그 달린 클래스에는 단점이 많다.

  • 열거 타인 선언, 테그 필드, switch문 등 쓸데 없는 코드가 많다.

  • 다른 의미를 위한 코드도 언제나 같이 따라다녀 메모리도 많이 사용한다.

  • 또 다른 의미를 추가하려면 코드를 수정해야 한다.

  • 인스턴스 타입만으로는 현재 나타내는 의미를 알길이 전혀 없다.

정리 : 테그 달린 클래스를 써야 하는 상황이 거의 없다. 새로운 클래스를 작서하는데 테그 필드가 등장한다면 테그를 없애고 계층 구조로 대체하는 방법을 살표보자


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

중첩 클래스에는 네 가지가 있으며, 가각의 쓰임이 다르다. 메소드 밖에서도 사용해야 하거나
메소드 안에서 정의하기엔 너무 길다면 맴버 클래스를 만들어라,. 맴버 클래스에 인스턴스 가각이 바깥 인스턴스를 참조한다면 비정적으로,
그렇지 않다면 정적으로 만들자.중첩 클래스가 한 메소드 안에서만 쓰이면서 그 인스턴스를 생성하는 지점이 단 한곳이고
해당 타입으로 쓰기에 적합한 클래스나 인터페이스가 이미 있다면 익명 클래스로 만들고 아니면 지역 클래스로 만들자


아이템 25. 톱레벨 클래스는 한 파일에 하나만 담으라

소스 파일 하나에는 반드시 톱 레벨의 클래스 (또는 인터페이스) 하나만 담자 이 규칙만 따른다면 컴파일러가 한 클래스에 대한 정의를 여러개 만들어 내는 일은 사라지고, 소스 파일을 어떤 순서로 컴파일 하던 바이너리 파일이나 프로그램의 동작이 달라지는 일은 결코 일어나지 않을 것이다.

profile
한참 열정이 가득한 백엔드 개발자입니다.

0개의 댓글