Effective Java 3/E 북스터디 2장_item)1~5

tth-k·2023년 12월 7일

북스터디

목록 보기
1/2
post-thumbnail

2장 객체 생성과 파괴(Item 1 ~ 5)

아이템 1) 생성자 대신 정적 팩터리 메서드를 고려하라

  • 정적 팩터리의 장점
    - 이름을 가질 수 있다.
    - 호출될 때마다 인스턴스를 새로 생성하지는 않아도 된다.
    - 반환 타입의 하위 타입 객체를 반환할 수 있는 능력이 있다.
    - 입력 매개변수에 따라 매번 다른 클래스의 객체를 반환할 수 있다.
    - 정적 팩터리 메서드를 작성하는 시점에는 반환할 객체의 클래스가 존재하지 않아도 된다.

  • 정적 팩터리의 단점
    - 정적 팩터리 메서드만 제공하면 public이나 protected 생성자가 필요하니 상속받은 클래스를 만들 수 없다.
    - 프로그래머가 인지하기 어렵다.

  • 핵심정리
    - 정적 팩터리 메서드와 public 생성자는 각자의 쓰임새가 有
    - 상대적인 장단점을 이해 후 사용
    - 정적 팩터리를 사용하는데 유지한 경우가 더 많으므로 무작정 public 생성자를 제공하던 습관은 고치자

아이템 2) 생성자에 매개변수가 많다면 빌더를 고려하라

package step2;

// item2 : 생성자에 대한 매개변수가 많으면 빌더를 고려하라
// 빌더 패턴 - 점층적 생성자 패턴과 자바빈즈 패턴의 장점으로 형성
public class item2 {
    private final int servingSize;
    private final int servings;
    private final int carlories;
    private final int fat;
    private final int sodium;
    private final int carbohyrate;

    public static class Builder{
        // 필수 매개변수
        private final int servingSize;
        private final int servings;

        // 선택 매개변수 - 기본값으로 초기화
        private int calories = 0;
        private int fat = 0;
        private int sodium = 0;
        private int carbohydrate = 0;

        public Builder(int servingSize, int servings){
            this.servingSize = servingSize;
            this.servings =servings;
        }

        public Builder calories(int val)
        {calories = val; return this;}

        public Builder fat(int val)
        {fat = val; return this;}

        public Builder sodium(int val)
        {sodium = val; return this;}

        public Builder carbohydrate(int val)
        {carbohydrate = val; return this;}

        public item2 build(){
            return new item2(this);
        }
        private item2(Builder builder){
            servingSize = builder.servingSize;
            servings = builder.servings;
            calories = builder.calories;
            fat = builder.fat;
            sodium = builder.sodium;
            carbohydrate = builder.carbohydrate;
        }
    }
}
  • 핵심정리
    - 생성자나 정적 팩터이가 처리해야 할 매개변수가 많다면 빌더 패턴을 선택하는 게 더 낫다.
    - 매개변수 중 다수가 필수가 아니거나 같은 타입니면 특히 더 그렇다.
    - 빌더는 점층적 생성자보다 클라이언트 코드를 읽고 쓰기가 더 간결하고, 자바빈즈보다 훨씬 안전하다.

아이템 3) private 생성자나 열거 타입으로 싱글턴임을 보증하라

  • 싱글턴(singleton)
    - 인스턴스를 오직 하나만 생성할 수 있는 클래스
    - 클래스를 싱글턴으로 만들면 이를 사용하는 클라이언트를 테스트하기 어려워질 수 있다.

  • 종류 및 특징

  1. public static final 필드 방식
    • 해당 클래스가 싱글턴임이 API에 명백히 드러난다.
    • 간결하다.
package step2;

// item3 : private 생성자나 열거 타입으로 싱글턴임을 보증하라

public class item3_1 {
    // public static final 필드 방식의 싱글턴
    public static final item3_1 INSTANCE = new item3_1();
    private item3_1(){}

    public void leaveTheBuilding(){}
}
  1. 정적 팩터리 방식
    • API를 바꾸지 않고도 싱글턴이 아니게 변경할 수 있다.
    • 정적 팩터리를 제네릭 싱글턴 팩터리로 만들수 있다.
    • 정적 팩터리의 메서드 참조를 공급자(supplier)로 사용할 수 있다.
    • 코드 예시대로라면 가짜 Elvis가 탄생한다. 가짜 Elvis를 예방하고싶다면 Evlis 클래스에 다음의 readResolve 메서드를 추가하면 된다.
package step2;

// item3 : private 생성자나 열거 타입으로 싱글턴임을 보증하라

public class item3_2 {
    // 정적 팩터리 방식의 싱글턴
    public static final item3_2 INSTANCE = new item3_2();
    private item3_2(){}
    public static item3_2 getInstance(){return INSTANCE;}

    public void leaveTheBuilding(){}

    // 싱글턴임을 보장해주는 readResolve 메서드
    private Object readResolve(){
        // '진짜' item3_2를 만들고 가짜 item3_2는 GC에 맡긴다.
        return INSTANCE;
    }
}
  1. 열거 타입 방식
package step2;

// item3 : private 생성자나 열거 타입으로 싱글턴임을 보증하라

public enum item3_3 {
    // 열거타입 방식의 싱글턴
    INSTANCE;

    public void leaveTheBuilding(){}
}
  • 핵심정리
    - 조금 부자연스러워 보일 수는 있다.
    - 대부분 상황에서는 원소가 하나뿐인 열거 타입이 싱글턴을 만드는 가장 좋은 방법이다.
    - 단, 만들려는 싱글턴이 Enum 외의 클래스를 상속해야한다면 이 방법은 사용할 수 없다.

아이템 4) 인스턴스화를 막으려거든 private 생성자를 사용하라

package step2;

// item 4 : 인스터스화를 막으려거든 private 생성자를 사용하라

// 인스턴스를 막을 수 없는 유틸리티 클래스
public class item4 {
    // 기본 생성자가 만들어지는 것을 막는다.(인스턴스화 방지용)
    private item4(){
        throw new AssertionError();
    }
    // 나머지 코드 생략
}
  • 추상 클래스로 만드는 것은 인스턴스화를 막을 수 없다.
    → 하위 클래스를 만들어 인스턴스화하면 된다.

  • 해결방안 : private 생성자를 추가하면 클래스의 인스턴스화를 막을 수 있다.

아이템 5) 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라

  • 사용하는 자원에 따라 동작이 달라지는 클래스에는 정적 유틸리티 클래스나 싱글턴 방식이 적합하지 않다.
  • 해결방안 : 인스턴스를 생성할 때 생성자에 필요한 자원을 넘겨주는 방식을 사용한다.
package step2;

// item5 : 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라

public class item5 {
    // 의존 객체 주입은 유연성과 테스트 용이성을 높여준다.
    private final Lexticon dictionary;

    public item5(Lexticon dictionary){
        this.dictionary = Objects.requireNonNull(dictionary);
    }
    public boolean isValid(String word){}
    public List<String> suggestions(String typo){}
}
  • 핵심정리
    - 클래스가 내부적으로 하나 이상의 자원에 의존하고, 그 자원이 클래스 동작에 영향을 준다면 싱글턴과 정적 유틸리티 클래스는 사용하지 않는 것이 좋다.
    - 이 자원들을 클래스가 직접 만들게 해서도 안된다.
    - 대신 자원을 생성자에 넘겨준다면 의존 객체 주입 방법은 클래스의 유연성, 재사용성, 테스트 용이성을 개선해준다.

    ** 이미지출처) Google yes24

profile
백엔드 취준생 / 코린이 ヾ(≧▽≦*)o

0개의 댓글