Dev Book Review/Effective Java

[Effective Java] item6. 불필요한 객체 생성을 피하라

1. 객체 재사용 똑같은 기능의 객체를 매번 사용하기 보다는 객체 하나를 재사용하는 편이 나을 때가 많다. 불변 객체는 언제든 재사용가능하다. String s = new String("hello");// Heap 영역에 존재 String s = "hello"; // String constant pool 영역에 존재 (Perm > Heap) 같은 JVM에서 이와 똑같은 문자열 리터럴을 사용하는 모든 코드가 같은 객체를 재 사용함이 보장된다. string constant pool 영역에 있는지 검색 한 후에, String 객체를 재 사용한다. (== 비교 가능해진다.) Q. string constant pool 영역이 Perm > Heap 일때 이점 [ Java7 ] A. string constant poo..

[Effective Java] item6. 불필요한 객체 생성을 피하라

728x90

1. 객체 재사용

똑같은 기능의 객체를 매번 사용하기 보다는 객체 하나를 재사용하는 편이 나을 때가 많다.

불변 객체는 언제든 재사용가능하다.

String s = new String("hello");	// Heap 영역에 존재
String s = "hello"; // String constant pool 영역에 존재 (Perm > Heap)

같은 JVM에서 이와 똑같은 문자열 리터럴을 사용하는 모든 코드가 같은 객체를 재 사용함이 보장된다.
string constant pool 영역에 있는지 검색 한 후에, String 객체를 재 사용한다. (== 비교 가능해진다.)

Q. string constant pool 영역이 Perm > Heap 일때 이점 [ Java7 ]

A. string constant pool의 모든 문자열도 GC 대상이 될 수 있다.
Perm 영역은 고정된 사이즈고 Runtime에 사이즈가 확장되지 않는다. 따라서 string.intern 메서드 호출은 OutofMemoryException을 발생시킬 수 있었다.

참고 : https://medium.com/@joongwon/string-%EC%9D%98-%EB%A9%94%EB%AA%A8%EB%A6%AC%EC%97%90-%EB%8C%80%ED%95%9C-%EA%B3%A0%EC%B0%B0-57af94cbb6bc
 

Java String 의 메모리에 대한 고찰

Java 언어에서 String은 무심코 사용되는 클래스 중에 하나가 아닐까 생각이 든다. String은 두 가지 생성 방식이 있고 각각의 차이점이 존재한다.

medium.com

 

2. 정적 팩터리 메서드를 제공하는 불변 클래스

Boolean(String); // deprecated
Boolean.valueOf(String); // 권장

팩터리 메서드에서는 호출할 때 마다 새로운 객체를 만들지 않음.
불변객체가 아니라 가변 객체라 해도 사용 중에 변경되지 않음을 안다면 재사용할 수 있다.

 

3. 생성 비용이 비싼 객체라면 캐싱하여 재사용

// 코드 6-1 성능을 훨씬 더 끌어올릴 수 있다!
static boolean isRomanNumeralSlow(String s) {
  return s.matches("^(?=.)M*(C[MD]|D?C{0,3})"
                   + "(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$");
}

// 코드 6-2 값비싼 객체를 재사용해 성능을 개선한다.
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 isRomanNumeralFast(String s) {
  return ROMAN.matcher(s).matches();
}

String.matches() : 성능이 중요한 상황에서 반복해 사용하기 적합하지않다.
정규 표현식용 Pattern 인스턴스를 한번 쓰고 버려져서 GC 대상이 되기 때문

성능 개선을 위해 정규 표현식을 표현하는 (불변) Pattern 인스턴스를 클래스 초기화(정적 초기화) 과정에서 직접 생성해 초기화해두고, 이 Pattern 인스턴스를 재사용한다.

지연초기화 : 불필요한 초기화를 없앨 수 있으나 권하지않는다.
코드를 복잡하게 만드는데 성능은 크게 개선되지 않을 때가 많기 때문

 

4. 어댑터 패턴 사용

어댑터(뷰) = 실제 작업은 뒷단 객체에 위임하고, 자신은 제2 인터페이스 역할을 해주는 객체어댑터는 뒷단 객체만 관리한다. 뒷단 객체 하나당 어댑터 하나

Map 인터페이스의 KeySet 메서드 : Map 객체안의 키를 전부 담은 Set 뷰 (어댑터)를 반환

뷰 객체를 여러개 만들거라 생각할 수 있지만, 사실은 매번 같은 인스턴스를 반환한다.

@DisplayName("keyset은 같은 Map을 바라본다")
@Test
void keyset(){
  Map<String, Object> javabom = new HashMap<>();
  javabom.put("Javabom", "Hello");

  Set<String> javabomSet1 = javabom.keySet();
  Set<String> javabomSet2 = javabom.keySet();

  assertThat(javabomSet1).isSameAs(javabomSet2);
}

 

5. 오토박싱(auto boxing)

오토박싱 : 기본타입과 그에 대응하는 박싱된 기본타입의 구분을 흐려주지만 완전히 없애주진 않는다.
성능에서 좋아진다 볼 수 없다.

private static long sum(){
  Long sum = 0L;	
  for(long i =0; i <= Integer.MAX_VALUE; i++){
    sum += i;	// 불필요한 Long 인스턴스가 만들어진다.
  }
  return sum;
}

박싱된 기본타입보다는 기본 타입을 사용하고, 의도치 않은 오토박싱이 숨어들지 않게 주의하자.

 

6. 본인만의 객체 풀(pool)을 만들지 말자

가벼운 객체를 다룰때는 직접 만든 객체 풀보다 훨씬 빠르다. (JVM GC가 잘 되있다.)데이터베이스 연결 - 생성비용이 워낙 비싸니 재사용이 낫다.

아이템 50 : 새로운 객체를 만들어야 한다면 기존 객체를 재사용하지 마라 : 방어적 복사 -> 버그와 보안으로 이어짐
아이템 6 : 불필요한 객체 생성을 피하라 -> 코드형태 성능에 영향

 

댓글

Comments

Dev Book Review/Effective Java

[Effective Java] item5. 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라

사용하는 자원에 따라 동작이 달라지는 클래스에는 정적 유틸리티 클래스나 싱글턴 방식이 적합하지 않다. 인스턴스를 생성할 때 생성자에 필요한 자원을 넘겨주는 방식을 사용하자 의존 객체 주입의 형태!! // 정적 유틸리티 클래스 X public class SpellChecker{ private static final Lexicon dictionary = ...; private SpellChecker(){} // 객체 생성 방지 } // 싱글턴 X public class SpellChecker{ private final Lexicon dictionary = ...; private SpellChecker(...){} public static SpellChecker INSTANCE = new SPellChecke..

[Effective Java] item5. 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라

728x90

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

  • 인스턴스를 생성할 때 생성자에 필요한 자원을 넘겨주는 방식을 사용하자
  • 의존 객체 주입의 형태!!
// 정적 유틸리티 클래스 X
public class SpellChecker{
  private static final Lexicon dictionary = ...;
  private SpellChecker(){} // 객체 생성 방지
}

// 싱글턴 X
public class SpellChecker{
  private final Lexicon dictionary = ...;
  private SpellChecker(...){}
  public static SpellChecker INSTANCE = new SPellChecker(...);
}

// 의존 객체 주입 형태 O
public class SpellChecker{
  private final Lexicon dictionary;
  
  public SpellChecker(Lexicon dictionary){
    this.dictionary = Objects.requireNonNull(dictionary);
  }
  
  public boolean isValid(String word){...}
  public List<String> suggestions(String typo){...}
}

스펠 체커가 여러 사전을 사용할 수 있다고 할 때, 싱글턴이나 정적 유틸리티 클래스에서 새로운 사전으로 교체하는 메서드를 추가하기보단, 의존 객체 주입 형태가 올바르다.

  • 불변을 보장한다.
  • 생성자, 정적 팩터리, 빌더 모두에 똑같이 응용할 수 있다.

 

생성자에 자원팩터리를 넘겨주는 방식

팩터리 = 호출할 때마다 특정 타입의 인스턴스를 반복해서 만들어주는 객체 (팩터리 메서드 패턴)

Supplier<T> 인터페이스: 팩터리를 표현함
한정적 와일드 카드 타입 (bounded wildcard type)으로 팩터리 타입 매개변수를 제한한다. (ITypeFactory.class)

IType의 하위타입이어도 map에 put이 가능하다. (리스코프의 치환원칙에 적용 되는 듯)

public class IType {
    private static final int TYPE_Z = 0;
    private static final int TYPE_A = 1;
    private static final int TYPE_B = 2;

    final static Map<Integer, Supplier<? extends ITypeFactory>> map = new HashMap<>();
    static {
	map.put(TYPE_Z, ITypeFactory::new);
        map.put(TYPE_A, A::new);
        map.put(TYPE_B, B::new);
    }
}

class ITypeFactory {}
class A extends ITypeFactory {}
class B extends ITypeFactory {}

 

의존객체 주입 프레임 워크

대거(Dagger), 주스(Guice), 스프링(Spring)
의존객체를 직접 주입하도록 설계된 API를 알맞게 응용해 사용하고 있다.

댓글

Comments

Dev Book Review/Effective Java

[Effective Java] item4. 인스턴스화를 막으려거든 private 생성자를 사용하라

1. 정적 메서드와 정적 필드만을 담을 클래스의 쓰임새 기본타입 값이나 배열 관련 메서드를 모아 둘 때 (java.lang.Math, java.util.Arrays) 특정 인터페이스를 구현하는 객체를 생성해주는 정적 메서드(팩터리)를 모아둘 때 (java.util.Collections) final 클래스와 관련한 메서드 2. 인스턴스화를 막는 방법. 정적 멤버만 담을 때는 인스턴스로 만들어 쓰려고 설계한게 아님. 문제 ) 생성자를 명시하지 않을 때 자동으로 기본 생성자가 만들어짐 (인스턴스화가 가능해진다) 해결 ) private 생성자를 추가해서 클래스의 인스턴스 화를 막을 수 있다 상속을 불가능하게 하는 효과 (하위가 상위 생성자 접근을 할 수 없다.) 직관적이지 않을 수 있으니 적절한 주석을 달자 p..

[Effective Java] item4. 인스턴스화를 막으려거든 private 생성자를 사용하라

728x90

1. 정적 메서드와 정적 필드만을 담을 클래스의 쓰임새

  • 기본타입 값이나 배열 관련 메서드를 모아 둘 때 (java.lang.Math, java.util.Arrays)
  • 특정 인터페이스를 구현하는 객체를 생성해주는 정적 메서드(팩터리)를 모아둘 때 (java.util.Collections)
  • final 클래스와 관련한 메서드

 

2. 인스턴스화를 막는 방법.

정적 멤버만 담을 때는 인스턴스로 만들어 쓰려고 설계한게 아님.

문제 ) 생성자를 명시하지 않을 때 자동으로 기본 생성자가 만들어짐 (인스턴스화가 가능해진다)

해결 ) private 생성자를 추가해서 클래스의 인스턴스 화를 막을 수 있다

  • 상속을 불가능하게 하는 효과 (하위가 상위 생성자 접근을 할 수 없다.)
  • 직관적이지 않을 수 있으니 적절한 주석을 달자
public class UtilityClass {
    // 기본 생성자가 만들어지는 것을 막는다(인스턴스화 방지용).
    private UtilityClass() {
        throw new AssertionError();
    }
    // 나머지 코드는 생략
}

댓글

Comments

Dev Book Review/Effective Java

[Effective Java] item3. private 생성자나 열거 타입으로 싱글턴임을 보증하라

1. 싱글턴이란? 싱글턴(singleton) : 인스턴스를 오직 하나만 생성할 수 있는 클래스 함수와 같은 무상태(stateless) 객체 - 정적 멤버클래스 이야기인가? (Enum 같은거?) 설계상 유일해야하는 시스템 컴포넌트 클래스를 싱글턴으로 만들면 이를 사용하는 클라이언트를 테스트하기 어려울 수 있다. : mock 대체가 불가능 2. 방식 1) public static 멤버가 final 필드 Private 생성자가 public static final 필드를 초기화 할 때 딱 한번만 호출된다. 예외 ) 권한이 있는 클라이언트가 리플렉션 API인 AccessibleObject.setAccessible을 이용해 private 생성자를 호출 방어 ) 생성자를 수정해 두 번째 객체 생성때 예외 던지기 pu..

[Effective Java] item3. private 생성자나 열거 타입으로 싱글턴임을 보증하라

728x90

1. 싱글턴이란?

싱글턴(singleton) : 인스턴스를 오직 하나만 생성할 수 있는 클래스

  • 함수와 같은 무상태(stateless) 객체 - 정적 멤버클래스 이야기인가? (Enum 같은거?)
  • 설계상 유일해야하는 시스템 컴포넌트
  • 클래스를 싱글턴으로 만들면 이를 사용하는 클라이언트를 테스트하기 어려울 수 있다. : mock 대체가 불가능

 

2. 방식 1) public static 멤버가 final 필드

Private 생성자가 public static final 필드를 초기화 할 때 딱 한번만 호출된다.

예외 ) 권한이 있는 클라이언트가 리플렉션 API인 AccessibleObject.setAccessible을 이용해 private 생성자를 호출

방어 ) 생성자를 수정해 두 번째 객체 생성때 예외 던지기

public class Elvis {
    public static final Elvis INSTANCE = new Elvis();
    private Elvis() { }

    public void leaveTheBuilding() {...}
}

[ 장점 ]

  • 이 클래스가 싱글턴임이 API에 명백히 들어난다.
  • 간결하다.

 

3. 방식 2) 정적 팩터리 메서드를 public static 멤버로 제공

Elvis.getInstance는 항상 같은 객체의 참조를 반환한다. (리플렉션 예외는 똑같이 적용)

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

    public void leaveTheBuilding() {...}
}

 

[ 장점 ]

  • API를 바꾸지 않고도 싱글턴이 아니게 변경할 수 있다.
  • 정적 팩터리를 제네릭 싱글턴 팩터리로 만들수 있다. (아이템 30)
  • 정적 팩터리의 메서드 참조를 공급자(supplier)로 사용할 수 있다. (아이템 43,44) [예: Instant::now]

 

4. 방식 3) Enum 타입 방식의 싱글턴

public enum Elvis {
    INSTANCE;
    public void leaveTheBuilding() {...}
}

[ 장점 ]

  • 간결하다
  • 추가 노력 없이 직렬화가 가능하다.
  • 직렬화나 리플렉션 공격에서도 제2의 인스턴스가 생기는 일을 막아준다.

조건 : 싱글턴이 부모클래스가 되어야하는 상황이 생기면 사용할 수 없다.

"대부분 상황에서 원소가 하나뿐인 Enum이 싱글턴을 만드는 가장 좋은 방법이다."

 

5. 싱글턴 클래스의 직렬화

Serializable 구현한다고 선언하는 것만으로는 부족하다. 인스턴스 필드를 transient라고 선언하고 readResolve 메서드를 제공해야한다. (readResolve에서 인스턴스를 반환한다. - 새로운 인스턴스 생성을 방지한다.)

참조 링크 : https://github.com/Java-Bom/ReadingRecord/issues/4

 

추가1. 제네릭 싱글턴 팩터리

public class GenericSingletonFactory {
    // 코드 30-4 제네릭 싱글턴 팩터리 패턴 (178쪽)
    private static UnaryOperator<Object> IDENTITY_FN = (t) -> t;

    @SuppressWarnings("unchecked")
    public static <T> UnaryOperator<T> identityFunction() {
        return (UnaryOperator<T>) IDENTITY_FN;
    }

    // 코드 30-5 제네릭 싱글턴을 사용하는 예 (178쪽)
    public static void main(String[] args) {
        String[] strings = { "삼베", "대마", "나일론" };
        UnaryOperator<String> sameString = identityFunction();
        for (String s : strings)
            System.out.println(sameString.apply(s));

        Number[] numbers = { 1, 2.0, 3L };
        UnaryOperator<Number> sameNumber = identityFunction();
        for (Number n : numbers)
            System.out.println(sameNumber.apply(n));
    }
}

코드 요구사항 : IDENTITY_FN를 UnaryOperator<T>로 형변환 하면 비검사 형변환 경고가 발생 Object는 T가 아니기 때문. 그러나 여기 요구사항에서 항등함수를 담은 클래스를 만들고 싶은 것이기 때문에, 비검사 형변환 경고를 숨겨도 안심할 수 있다.

 

추가2. 메서드 참조 & 공급자

아이템 43 참고

메서드 참조 유형 같은 기능을 하는 람다
정적 Integer::parseInt str -> Integer.parseInt(str)
한정적 (인스턴스) Instant.now()::isAfter Instant then = Instatn.now();
t->then.isAfter(t);
비한정적 (인스턴스) String::toLowerCase str -> str.toLowerCase();
클래스 생성자 TreeMap<K,V>::new () -> new TreeMap<K,V>();
배열 생성자 int[]::new len -> new int[len]

공급자 = Supplier

인터페이스 명 추상 메서드 설명
Supplier<T> T get() T 객체를 리턴한다.

댓글

Comments

Dev Book Review/Effective Java

[Effective Java] item2. 생성자에 매개변수가 많다면 빌더를 고려해라

정적 팩터리 + 생성자의 제약 = 선택적 매개변수가 많을 때 적절히 대응하기 어렵다 1. 대안A) 점층적 생성자 패턴 (telescoping constructor pattern) 필수 매개변수 + 선택 매개변수 원하는 매개변수를 모두 포함한 생성자 중 가장 짧은 것을 골라 호출한다 권장하지 않는다. 매개변수 개수가 많아지면 클라이언트 코드를 작성하거나 읽기 어렵다. 관련 코드 : https://github.com/WegraLee/effective-java-3e-source-code/blob/master/src/effectivejava/chapter2/item2/telescopingconstructor/NutritionFacts.java 2. 대안B) 자바빈즈 패턴 (JavaBeans pattern) 자바..

[Effective Java] item2. 생성자에 매개변수가 많다면 빌더를 고려해라

728x90

정적 팩터리 + 생성자의 제약 = 선택적 매개변수가 많을 때 적절히 대응하기 어렵다

 

1. 대안A) 점층적 생성자 패턴 (telescoping constructor pattern)

필수 매개변수 + 선택 매개변수

원하는 매개변수를 모두 포함한 생성자 중 가장 짧은 것을 골라 호출한다

권장하지 않는다. 매개변수 개수가 많아지면 클라이언트 코드를 작성하거나 읽기 어렵다.

관련 코드 : https://github.com/WegraLee/effective-java-3e-source-code/blob/master/src/effectivejava/chapter2/item2/telescopingconstructor/NutritionFacts.java

 

2. 대안B) 자바빈즈 패턴 (JavaBeans pattern)

자바빈즈 패턴의 단점

  • 객체 하나를 만들려면 메서드를 여러개 호출해야 한다. (객체 1개 : 메서드 호출 N개)
  • 객체가 완전히 생성되기 전까지는 일관성(consistency)이 무너진 상태에 놓인다. -> 런타임문제 디버깅 하드해진다.
    • 클래스를 불변으로 만들 수 없다. -> thread 안전하지 않다.

단점 해소를 위한 freezing 방법이 있지만, freeze 메서드를 확실히 호출했는지 컴파일러가 보증할 방법이 없어서 런타임에 취약하다.

관련 코드 : https://github.com/WegraLee/effective-java-3e-source-code/blob/master/src/effectivejava/chapter2/item2/javabeans/NutritionFacts.java

 

3. 대안 C) 빌더 패턴 (Builder pattern)

  • 필수 매개변수만으로 생성자(정적 팩터리)를 호출해 빌더 객체를 얻는다.
  • 일종의 세터메서드로 원하는 선택 매개변수를 설정한다.
  • build 메서드를 호출해 필요한 객체를 얻는다 (주로 불변)

관련 코드 : https://github.com/WegraLee/effective-java-3e-source-code/blob/master/src/effectivejava/chapter2/item2/builder/NutritionFacts.java

빌더 패턴의 메서드 호출 연결 : 풀루언트 API(fluent API) or 메서드 연쇄(method chaining)

주의 : build() 메서드에서 호출하는 생성자에서 여러 매개변수의 불변식(invariant)을 검사하자

불변식 : 프로그램이 실행되는 동안, 정해진 기간동안 반드시 만족해야하는 조건 (불변[immutable]은 불변식의 극단적인 예)

 

4. 빌더패턴의 쓰임새

계층적으로 설계된 클래스와 함께 사용하기 좋다.

시뮬레이트한 셀프 타입 (simulated self-type) : 추상메서드 self를 더해 하위클래스에서 형변환 없이 메서드 연쇄를 지원한다. [Pizza.Builder 클래스는 재귀적 타입 한정을 이용하는 제네릭 타입이다.]

재귀적 타입 한정 : 자기 자신이 들어간 표현식을 사용해 타입 매개변수의 허용범위를 한정한다.

Pizza.java : https://github.com/WegraLee/effective-java-3e-source-code/blob/master/src/effectivejava/chapter2/item2/hierarchicalbuilder/Pizza.java

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

빌더를 이용하면 가변인수(varargs) 매개변수를 여러개 사용할 수 있다.
addTopping 메서드

 

5. 빌더 패턴의 단점

객체를 만들때 빌더부터 마들어야하는데, 빌더 생성비용이 크지는 않지만, 성능에 민감한 상황에서는 문제이다.

매개변수가 4개이상은 되어야 값어치를 한다.

댓글

Comments

Dev Book Review/Effective Java

[Effective Java] item1. 생성자 대신 정적 팩터리 메서드를 고려하라

1. 정적 팩터리 메서드의 장점 ㄱ. 이름을 가질 수 있다. 생성자정적 팩터리 메서드 시그니처가 같은 생성자가 여러개 필요할 것 같을때 정적 팩터리 메서드를 사용하자 생성자 정적 팩터리 메서드 특징설명 X O 시그니처 1개 N개 ㄴ. 호출될 때 마다 인스턴스를 새로 생성하지 않아도 된다. 불변클래스: 인스턴스를 미리 만들어 놓거나 인스턴스 캐싱으로 재사용하여 불필요한 객체 생성을 피할 수 있다. ex ) Boolean.valueOf(boolean b); 플라이웨이트 패턴 (Flyweight pattern) : 데이터를 공유하여 메모리를 절약하는 패턴, 공통으로 사용되는 객체는 한번만 사용되고 Pool에의해서 관리, 사용된다. (JVM의 String Pool에서 같은 String이 잇는지 먼저 찾는다. [..

[Effective Java] item1. 생성자 대신 정적 팩터리 메서드를 고려하라

728x90

1. 정적 팩터리 메서드의 장점

ㄱ. 이름을 가질 수 있다.

생성자정적 팩터리 메서드
시그니처가 같은 생성자가 여러개 필요할 것 같을때 정적 팩터리 메서드를 사용하자

  생성자 정적 팩터리 메서드
특징설명 X O
시그니처 1개 N개

 

ㄴ. 호출될 때 마다 인스턴스를 새로 생성하지 않아도 된다.

  •  불변클래스: 인스턴스를 미리 만들어 놓거나 인스턴스 캐싱으로 재사용하여 불필요한 객체 생성을 피할 수 있다.
    ex ) Boolean.valueOf(boolean b);
  •  플라이웨이트 패턴 (Flyweight pattern) : 데이터를 공유하여 메모리를 절약하는 패턴, 공통으로 사용되는 객체는 한번만 사용되고 Pool에의해서 관리, 사용된다.
    (JVM의 String Pool에서 같은 String이 잇는지 먼저 찾는다. [불변객체 String])
  •  인스턴스 통제(instance-controlled) 클래스 : 정적 팩터리 방식의 클래스는 언제 어느 인스턴스를 살아 있게 할지를 철저히 통제할 수 있다.
    • 싱글턴 / noninstatntialbe로 만들 수 있다.
    • 불변 값 클래스에서 동치 인스턴스 하나임을 보장 ex ) Enum : 인스턴스가 하나만 만들어짐

 

 

ㄷ. 반환타입의 하위타입 객체를 반환할 수 있는 능력이 있다.

반환할 객체의 클래스를 자유롭게 선택할 수 있다. (유연성)

인터페이스 기반 프레임워크 : 인터페이스를 정적 팩터리 메서드의 반환 타입으로 사용한다.

  • 실제 구현 클래스가 무엇인지 알아보지 않아도 된다.
  • 정적 팩터리 메서드를 사용하는 클라이언트가 얻은 객체를 인터페이스만으로 다룰 수 있다.

 

ㄹ. 입력 매개변수에 따라 매번 다른 클래스의 객체를 반환할 수 있다.

하위타입이기만 하면 어떤 클래스의 객체를 반환하든 상관없다.

EnumSet 클래스 : public 생성자 없이 오직 정적 팩터리만 제공한다. (public 대신 default 생성자.) 이때 원소의 개수에 따라, 반환 타입이 달라진다. RegularEnumSet . JumboEnumSet

하지만 클라이언트에서는 이 두 객체의 존재를 모르고, 다음 릴리즈에서 이 내용을 변경할 수 있는 유연성을 가진다.

 

ㅁ. 정적 팩터리 메서드를 작성하는 시점에는 반환할 객체의 클래스가 존재하지 않아도 된다.

서비스 제공자 프레임워크(service provider framework)를 만드는 근간이 된다. (JDBS)제공자 (provider) : 서비스의 구현체

service provider framework의 통제하에 provider를 클라이언트에 제공한다. (클라이언트와 구현체의 분리)

service provider framework의 컴포넌트

  • 서비스 인터페이스 (service interface) : 구현체의 동작 정의
    JDBC ) Connection
  • 제공자 등록 API (provider registration API) : provider가 구현체를 등록할 때 사용
    JDBC ) DriverManager.registerDriver
  • 서비스 접근 API (service access API) : 클라이언트가 서비스의 인스턴스를 얻을 때 사용클라이언트가가 이 service access API 사용시 원하는 구현체 조건을 명시할 수 있다.
    JDBC ) DriverManager.getConnection
  • 서비스 제공자 인터페이스(service provider interface) : service interface의 인스턴스를 생성하는 팩터리 객체를 설명해준다. (없으면 리플렉션 사용)
    JDBC ) Driver

https://github.com/Java-Bom/ReadingRecord/blob/4e6cb4ad6b586ffa9a93100ceaa8ecccc23a4788/%EC%9D%B4%ED%8E%99%ED%8B%B0%EB%B8%8C%20%EC%9E%90%EB%B0%94/effectiveJava/src/main/java/item1/jdbc/JdbcSample.java

예제 코드에서 DriverManaer.registerDriver(new Driver()) 이런식으로 등록했기 때문에, driver에 따라서 반환객체가 달라지는 걸로 추정

 

2. 정적 팩터리 메서드의 단점

ㄱ. 정적 팩터리 메서드만 제공하면 하위 클래스를 만들 수 없다.

but 이 제약은 상속의 경우에 일어날 수 있는 문제점을 컴포넌트로 유도해서 해결하는 방식이나, 불변타입으로 만들 때 이 제약을 지켜야한다는 점에서 오히려 장점으로 받아들일 수 있다.

 

ㄴ. 정적 팩터리 메서드는 프로그래머가 찾기 어렵다.

사용자는 정적 팩터리 메서드 방식 클래스를 인스턴스화 할 방법을 문서를 이용해 찾아야한다.

from() | of() | valueOf() | instance | create | getType | newType | type

댓글

Comments