반응형

2장 객체 생성과 파괴

 

아이템 6. 불필요한 객체 생성을 피하라

 

  • 생성자 대신 정적 팩토리 메서드를 사용해 불필요한 객체 생성 회피 가능

    Boolean(String);			// java 9에서 사장
    
    Boolean.valueOf(String);
    
  • 캐싱하여 재사용을 통해 성능 개선

    static boolean isRomanNumeral(String s) {
        return s.matches("^(?=.)M*(C[MD]|D?C{0,3})"
                        + "(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$")
    }
    
    public class RomanNumerals {
        
        private static final Pattern ROMAN = Pattern.compile(
            "^(?=.)M*(C[MD]|D?C{0,3})"
            + "(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$"
        );
        
        static boolean isRomanNumeral(String s) {
            return ROMAN.matcher(s).matchers();
        }
    }
    

    의도치 않은 오토박싱 회피

 

TODO

  • 정규표현식
  • 유한 상태 머신
  • 지연 초기화
  • 어댑터 : 실제 작업은 뒷단 객체에 위임하고, 자신은 제2의 인터페이스 역할을 해주는 객체
  • 방어적 복사
반응형

'Java > OOP' 카테고리의 다른 글

[Effective Java] 2장. 아이템5  (0) 2022.07.17
[Effective Java] 2장. 아이템4  (0) 2022.07.16
[Effective Java] 2장. 아이템3  (0) 2022.07.16
[Effective Java] 2장. 아이템2  (0) 2022.07.15
[Effective Java] 2장. 아이템1  (0) 2022.07.14
반응형

2장 객체 생성과 파괴

 

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

 

사용하는 자원에 따라 동작이 달라지는 클래스에는 정적 유틸리티 클래스나 싱글턴 방식이 적합하지 않음

 

의존 객체 주입 패턴

인스턴스 생성 시 생성자에 필요한 자원을 넘겨주는 방식

 

예시

public class SpellChecker {
    
    private final Lexicon dictionary;
    
    public SpellChecker(Lexicon dictionary) {
        this.dictionary = Objects.requireNonNull(dictionary);
    }
}

 

특징

  • 장점

    • 유연성
    • 테스트 용이성 개선
  • 단점

    • 큰 프로젝트에서 프로젝트를 어지럽게 만듦 => 의존 객체 주입 프레임워크 사용

 

TODO

  • 한정적 와일드카드 타입
반응형

'Java > OOP' 카테고리의 다른 글

[Effective Java] 2장. 아이템6  (0) 2022.07.18
[Effective Java] 2장. 아이템4  (0) 2022.07.16
[Effective Java] 2장. 아이템3  (0) 2022.07.16
[Effective Java] 2장. 아이템2  (0) 2022.07.15
[Effective Java] 2장. 아이템1  (0) 2022.07.14
반응형

2장 객체 생성과 파괴

 

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

 

추상 클래스 한계

  • 하위 클래스를 만들어 인스턴스화 가능

 

private 생성자 장점

  • 인스턴스화 방지
  • 상속 불가

 

예시

public class UtilityClass {
    
    // 기본 생성자 생성 방지(인스턴스화 방지용)
    private UtilityClass() {
        throw new AssertionError();
    }
}

 

반응형

'Java > OOP' 카테고리의 다른 글

[Effective Java] 2장. 아이템6  (0) 2022.07.18
[Effective Java] 2장. 아이템5  (0) 2022.07.17
[Effective Java] 2장. 아이템3  (0) 2022.07.16
[Effective Java] 2장. 아이템2  (0) 2022.07.15
[Effective Java] 2장. 아이템1  (0) 2022.07.14
반응형

2장 객체 생성과 파괴

 

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

 

싱글턴

인스턴스를 오직 하나만 생성할 수 있는 클래스

 

방식

  1. public static final 필드 방식

    public class Elvis {
        
        public static final Elvis INSTANCE = new Elvis();
        private Elvis() { ... }
    }
    
    • 장점

      • 싱글턴임이 API에 명백히 드러남
      • 간결함
    • 단점

      • 리플렉션 API AccessibleObject.setAccessible( )을 사용해 private 생성자 호출 가능
      • 직렬화 시 모든 인스턴스 필드를 transient이라 선언하고, readResolve( )를 제공해야 함 (하지 않는 경우 역직렬화 시 새로운 인스턴스 생성)
  2. 정적 팩토리 방식

    public class Elvis {
        
        private static final Elvis INSTANCE = new Elvis();
        private Elvis() { ... }
        public static Elvis getInstance() {
            return INSTANCE;
        }
    }
    
    • 장점

      • API를 바꾸지 않고 싱글턴이 아니게 변경 가능
      • 정적 팩토리를 제네릭 싱글턴 팩토리로 만들 수 있다.
      • 정적 팩토리의 메서드 참조를 supplier로 사용 가능
    • 단점

      • 리플렉션 API AccessibleObject.setAccessible( )을 사용해 private 생성자 호출 가능
      • 직렬화 시 모든 인스턴스 필드를 transient이라 선언하고, readResolve( )를 제공해야 함 (하지 않는 경우 역직렬화 시 새로운 인스턴스 생성)
  3. 열거 타입 방식

    public enum Elvis {
        
        INSTANCE;
    }
    
    • 장점

      • 직렬화 상황이나 리플렉션 공격에서도 제2의 인스턴스 생성 방지
    • 단점

      • Enum 외의 클래스 상속 시 사용 불가

 

TODO

  • API를 바꾸지 않고 싱글턴이 아니게 변경
  • 제네릭 싱글턴 팩토리
  • Serializable.readResolve()
반응형

'Java > OOP' 카테고리의 다른 글

[Effective Java] 2장. 아이템6  (0) 2022.07.18
[Effective Java] 2장. 아이템5  (0) 2022.07.17
[Effective Java] 2장. 아이템4  (0) 2022.07.16
[Effective Java] 2장. 아이템2  (0) 2022.07.15
[Effective Java] 2장. 아이템1  (0) 2022.07.14
반응형

2장 객체 생성과 파괴

 

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

 

선택 매개변수가 많은 경우 대안

  1. 점층적 생성자 패턴

    public class NutritionFacts {
        
        private final int servingSize;	// 필수
        private final int servings;		// 필수
        private final int calories;		// 선택
        private final int fat;			// 선택
        private final int sodium;		// 선택
        
        public NutritionFacts(int servingSize, int servings) {
            this(servingSize, servings, 0);
        }
        
        public NutritionFacts(int servingSize, int servings, int calories) {
            this(servingSize, servings, calories, 0);
        }
        
        public NutritionFacts(int servingSize, int servings, int calroies, int fat) {
            this(servingSize, servings, calories, fat, 0);
        }
        
        public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium) {
            this.servingSize 	= servingSize;
            this.servings 		= servings;
            this.calories		= calories;
            this.fat 			= fat;
            this.sodium			= sodium;
        }
    }
    
    • 필수 매개변수만 받는 생성자를 만들고 선택 매개변수를 받는 생성자를 늘려가는 방식

    • 단점

      • 매개변수 개수가 많아지면서 클라이언트 코드를 작성하거나 읽기 어려움.
  2. 자바빈즈 패턴

    NutritionFacts cocaCola = new NutritionFacts();
    cocaCola.setServingSize(240);
    cocaCola.setServings(8);
    cocaCola.setCalories(100);
    cocaCola.setSodium(35);
    
    • 매개변수가 없는 생성자로 객체를 만든 후, setter 메서드를 호출하여 값 설정

    • 단점

      • 객체 하나를 만들려면 메서드를 여러 개 호출해야 하고, 객체가 완전히 생성되기 전까지 일관성이 무너진 상태
      • 클래스를 불변으로 만들 수 없음
  3. 빌더 패턴

    public class NutritionFacts {
        
        private final int servingSize;	// 필수
        private final int servings;		// 필수
        private final int calories;		// 선택
        private final int fat;			// 선택
        private final int sodium;		// 선택
        
        public static class Builder {
            
            private final int servingSize;	// 필수
            private final int servings;		// 필수
    
            private final int calories;		// 선택
            private final int fat;			// 선택
            private final int sodium;		// 선택
    
            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 NutritionFacts build() {
                return new NutritionFacts(this);
            }
        }
        
        private NutritionFacts(Builder builder) {
            this.servingSize 	= builder.servingSize;
            this.servings 		= builder.servings;
            this.calories		= builder.calories;
            this.fat 			= builder.fat;
            this.sodium			= builder.sodium;
        }
        
    }
    
    • 필수 매개변수만으로 빌더 객체를 생성하고, 선택 매개변수 값을 설정한 후 build 메서드 호출

    • 생성할 클래스 안에 정적 멤버 클래스로 만듦.

    • 메서드 연쇄 방식 : 클래스 자신을 반환하는 메서드를 연쇄적으로 호출

    • 장점

      • 읽고 쓰기 쉬움 ( named optional parameters 방식과 유사 )

      • 계층적으로 설계된 클래스와 함께 쓰기 좋음

        public abstract class Pizza {
            
            public enum Topping {
                HAM, MUSHROOM, ONION, PEPPER, SAUUSAGE
            }
            final Set<Topping> Toppings;
            
            abstract static class Builder<T extends Builder<T>> {
                
                EnumSet<Topping> toppings = EnumSet.noneOf(Topping.class);
                public T addTopping(Topping topping) {
                    toppings.add(Objects.requireNonNull(topping));
                    return self();
                }
                
                abstract Pizza build();
                
                protected abstract T self();
            }
            
            Pizza(Builder<?> builder) {
                toppings = builder.toppings.clone();
            }
            
        }
        
        public calass NyPizza extends Pizza {
            
            public enum Size { SMALL, MEDIUM, LARGE }
            private final Size size;
            
            public static class Builder extends Pizza.Builder<Builder> {
                
                private final Size size;
                
                public Builder(Size size) {
                    this.size = Objects.requireNonNull(size);
                }
                
                @Override
               	public NyPizza build() {
                    return new NyPizza(this);
                }
                
                @Override
                protected Builder self() {
                    return this;
                }
            }
            
            private NyPizza(Builder builder) {
                super(builder);
                size = builder.size;
            }
        }
        
        public class Calzone extends Pizza {
            
            private final boolean sauceInside;
            
            public static class Builder extends Pizza.Builder<Builder> {
                
                private boolean sauceInside = false;
                
                public Builder sauceInside() {
                    this.sauceInside = true;
                }
                
                @Override
                public Calzone build() {
                    return new Calzone(this);
                }
                
                @Override protected Builder self() {
                    return this;
                }
            }
            
            private Calzone(Builder builder) {
                super(builder);
                this.sauceInside = builder.sauceInside;
            }
        }
        
      • 가변인수 매개변수를 여러 개 사용 가능

    • 단점

      • 빌더 구현 => Lombok

 

정리

생성자나 정적 팩토리가 처리해야 할 매개변수가 많다면 빌더 패턴 선택

 

용어

공변 반환 타이핑 ( covariant return typing) : 하위 클래스의 메서드가 상위 클래스의 메서드가 정의한 반환 타입이 아닌 하위 타입을 반환하는 기능

반응형

'Java > OOP' 카테고리의 다른 글

[Effective Java] 2장. 아이템6  (0) 2022.07.18
[Effective Java] 2장. 아이템5  (0) 2022.07.17
[Effective Java] 2장. 아이템4  (0) 2022.07.16
[Effective Java] 2장. 아이템3  (0) 2022.07.16
[Effective Java] 2장. 아이템1  (0) 2022.07.14
반응형

2장 객체 생성과 파괴

 

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

 

정적 팩토리 메서드

정의 : 클래스의 인스턴스를 반환하는 정적 메서드

특징 )

  • 장점

    • 이름을 가질 수 있다.

      • 반환 객체의 특성을 쉽게 묘사
      • 시그니처가 같은 생성자 여러 개 생성 가능
    • 호출될 때마다 인스턴스를 새로 생성하지 않아도 된다.

      • 불필요한 객체 생성 회피
      • 플라이웨이트 패턴과 유사
      • 인스턴스 통제 클래스 ➡️ 싱글턴, 인스턴스화 불가, 불변 값 클래스
    • 반환 타입의 하위 타입 객체를 반환할 수 있다.

      • 동반 클래스 => Java 8부터 인터페이스에 정적 메서드 허용
    • 입력 매개변수에 따라 다른 클래스 객체를 반환할 수 있다.

      • 클라이언트는 팩토리가 건네주는 객체가 어느 클래스의 인스턴스인지 알 필요없이 하위 클래스이기만 하면 된다.
    • 정적 팩토리 메서드를 작성하는 시점에 반환할 객체의 클래스가 존재하지 않아도 된다.

      • 서비스 제공자 프레임워크를 만드는 근간

      * 서비스 제공자 프레임워크 핵심 컴포넌트 : 서비스 인터페이스, 제공자 등록 API, 서비스 접근 API, 서비스 제공자 인터페이스

      ex ) JDBC - 서비스 인터페이스 : Connection, 제공자 등록 API : DriverManager.registerDriver, 서비스 접근 API : DriverManager.getConnection, 서비스 제공자 인터페이스 : Driver

  • 단점

    • 정적 팩토리 메서드만 제공하면 하위 클래스를 만들 수 없다. => 컴포지션 사용 유도, 불변 타입으로 만들 경우 지켜야하는 제약

    • 프로그래머가 찾기 힘들다. => 흔히 사용하는 명명 방식 사용

      • from( ) : 매개변수를 하나 받는 형변환 메서드
      • of( ) : 여러 매개변수를 받는 집계 메서드
      • valueOf( ) : from( )과 of( )의 더 자세한 버전
      • getInstance( ) : 인스턴스 반환 ( 동일 인스턴스 보장 X )
      • newInstance( ) : 새로운 인스턴스 반환
      • getType( ) : Type 클래스 인스턴스 반환
      • newType( ) : 새로운 Type 클래스 인스턴스 반환
      • type( ) : getType( )과 newType( )의 간결한 버전

 

Java version

  • Java 6 : 범용 서비스 제공자 프레임워크 (java.util.ServiceLoader) 제공
  • Java 8 : 인터페이스에서 public 정적 멤버 허용
  • Java 9 : private 정적 메서드 허용 (단, 정적 필드와 정적 멤버 클래스는 public)

 

정리

정적 팩토리 메서드와 public 생성자의 장단점을 이해하고 사용하자.

 

TODO

  • 플라이웨이트 패턴
  • 인스턴스 통제 클래스
  • 동반 클래스
  • 리플렉션
  • 서비스 제공자 프레임워크
  • 브리지 패턴
반응형

'Java > OOP' 카테고리의 다른 글

[Effective Java] 2장. 아이템6  (0) 2022.07.18
[Effective Java] 2장. 아이템5  (0) 2022.07.17
[Effective Java] 2장. 아이템4  (0) 2022.07.16
[Effective Java] 2장. 아이템3  (0) 2022.07.16
[Effective Java] 2장. 아이템2  (0) 2022.07.15

+ Recent posts