본문 바로가기

Effective Java/객체 생성과 파괴

#01 생성자 대신 정적 팩터리 메서드를 고려하라

학습 요약

  • 정적 팩터리 메서드 ( static factory method) 란? 
  • 정적 팩터리 메서드가 생성자보다 좋은 장점 다섯가지
  • 정적 팩터리 메서드가 생성자보다 좋지않은 장점 두가지

정적 팩터리 메서드 ( static factory method) 란? 

클래스의 인턴스를 얻는 전통적인 public 생성자가 아닌 객체 생성의 역할을 하는 클래스 메서드라는 의미로 요약해 볼 수 있다. 다시 말해 new 키워드가 아닌 해당 클래스의 메서드로 객체생성을 할 수 있다.

 

정적 팩터리 메서드가 생성자보다 좋은 장점 다섯가지

1. 이름을 가질 수 있다.

- 생성자에 넘기는 매개변수와 생성자 자체 만으로는 반활될 객체의 특성을 제대로 설명하지 못한다. 하지만 정적 팩터리는 클래스와 다른 이름을 지을수 있기 때문에 이름만 잘 지으면 객체의 특성을 조금 더 쉽게 사용자 에게 알려줄 수 있다. 책에서 나오는 예제는 BigInteger 클래스 이다.  조금 더 정확한 이해를 위해서 실제 BigInteger의 생성자를 살펴보자 

 

BigInteger 생성자의 오버로딩

자 이렇게만 보고 내가 BigInteger를 처음 사용한다면 ... 아니 오랜만에 사용 한다면 이름이 같은 생성자 중에서 내가 사용하고 싶은 생성자가 어떤것 인지 알 수 없다. 그래서 책에서는 소수값을 반환하는 생성자와 팩터리메소드를 비교하면서 설명해 준다. 

 

BigInteger(int bitLength, int certainty, Random rnd);  생성자  

BigInteger.probablePrime(int bitLength, Random rnd) 정적 팩터리 메소드 

 

사용자가 BigInteger.probablePrime 을 보는순간 메소드 명에서 어느정도의 감을 잡을 수 있기 때문에 반환되는 객체의 특성을 예측하기 쉬워진다.

 

 

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

- 반복되는 요청에 같은 객체를 반환하는 식으로 정적 팩터리 방식의 클래스는 언제 어느 인스턴스를 살아 있게 할지를 철저하게 통제할 수 있다. 이런 클래스를 인스턴스 통제(instance-controlled)  클래스라 한다. 이렇게 통제를 통해 싱글톤이나 인스턴스화 불가 클래스를 만들수 있고 생성 비용이 큰 객체의 반복적인 생성을 피할수 있다.

 

 

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

- 이 능력은 반환할 객체의 클래스(리턴 타입의 상속을 받은 모든 클래스 가능)를 자유롭게 선택할 수 있게 하는 엄청난 유연성을 선물한다.

 

반환할 객체의 자유도를 높일 수 있기에 원하는 객체를 리턴 할 수 있습니다. 만약 특정 코드를 받아 코드에 따른 반환 객체를 자신 하위 타입 중 선택하게 한다면 아래와 같이 쉽게 처리할 수 있습니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public abstract class StaticFactoryMethod {
    
    abstract void getName();
    
    public static StaticFactoryMethod getNewInstance( String code ) {
        StaticFactoryMethod staticFactoryMethod = null;
        if( code.indexOf("2"== 1 ) {
            staticFactoryMethod = new Point();
        } else {
            staticFactoryMethod = new Coupon();
        }
        return staticFactoryMethod;
    }
}
 
class Coupon extends StaticFactoryMethod {
    public void getName() {
        System.out.println("쿠폰을 발행합니다.");
    }
}
 
class Point extends StaticFactoryMethod {
    public void getName() {
        System.out.println("포인트 1000점을 적립합니다.");
    }
}
 
cs

 

 

 

1
2
3
4
5
6
public static void main(String args[]){
    StaticFactoryMethod staticFactoryMethod = StaticFactoryMethod.getNewInstance("223123");
    StaticFactoryMethod staticFactoryMethod1 = StaticFactoryMethod.getNewInstance("123123");
    staticFactoryMethod.getName();
    staticFactoryMethod1.getName();
}
cs

 

// 결과출력 

쿠폰을 발행합니다.

포인트 1000점을 적립합니다.

 

 

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

- 반환타입의 하위 타입이기만 하면 어떤 클래스의 객체를 반환하든 상관없다. 같은 이름의 메서드지만 매개변수의 개수에 따라 리턴받는 클래스를 아무 하위타입 클래스를 리턴 받을 수 있다.

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public abstract class StaticFactoryMethodType {
    public abstract void getName();
    
    static public StaticFactoryMethodType getNewInstance( String code ) {
        return new OneClass();
    }
    static public StaticFactoryMethodType getNewInstance( String code, String name ) {
        return new TwoClass();
    }
 
}
 
class OneClass extends StaticFactoryMethodType {
    public void getName() {
        System.out.println("쿠폰을 발행합니다.");
    }
}
 
class TwoClass extends StaticFactoryMethodType {
    public void getName() {
        System.out.println("포인트 1000점을 적립합니다.");
    }
}
cs

 

1
2
3
4
5
6
public static void main(String args[]){
    StaticFactoryMethod staticFactoryMethod = StaticFactoryMethod.getNewInstance("223123");
    StaticFactoryMethod staticFactoryMethod1 = StaticFactoryMethod.getNewInstance("123123");
    staticFactoryMethod.getName();
    staticFactoryMethod1.getName();
}
cs

 

// 결과출력 

쿠폰을 발행합니다.

포인트 1000점을 적립합니다.

 

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

- 장점 3, 4와 관련된 유연함에 관한 내용으로 메서드 안에서 객체를 반환할 때, 당장 클래스가 존재하지 않아도 특정 텍스트 파일에서 인터페이스 구현체의 위치를 알려주는 곳의 정보를 가지고 해당 객체를 읽어 생성할 수 있다.

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
 
 
package algorithm.dataStructure;
 
public abstract class StaticFactoryMethodType {
    
    public abstract void getName();
    
    public static StaticFactoryMethodType getNewInstance() {
        StaticFactoryMethodType temp = null;
        try {
            Class<?> childClass = Class.forName("algorithm.dataStructure.StaticFactoryMethodTypeChild");
            temp = (StaticFactoryMethodType) childClass.newInstance();
            
        }catch (ClassNotFoundException e) {
           System.out.println("클래스가 없습니다.");
        } catch (InstantiationException  e) {
            System.out.println("메모리에 올릴수 없습니다.");
        } catch (IllegalAccessException  e) {
            System.out.println("클래스파일 접근 오류입니다.");
        }
        
        return temp;
    }
}
cs

 

1
2
3
4
5
6
7
8
9
10
package algorithm.dataStructure;
 
public class StaticFactoryMethodTypeChild extends StaticFactoryMethodType {
 
    @Override
    public void getName() {
        System.out.println("정상 로드 되었습니다");
    }
 
}
cs

 

1
2
3
4
5
public static void main(String args[]){
    StaticFactoryMethodType staticFactoryMethodType = StaticFactoryMethodType.getNewInstance();
    
    staticFactoryMethodType.getName();
}
cs

// 결과출력 

정상 로드 되었습니다

 

예제 코드 참조 https://honbabzone.com/java/effective-java-static-factory-method/#%EC%9E%A5%EC%A0%90-1--%EC%9D%B4%EB%A6%84%EC%9D%84-%EA%B0%80%EC%A7%88-%EC%88%98-%EC%9E%88%EB%8B%A4

 

생성자 대신 정적 팩터리 메서드를 고려하라.

이펙티브 자바 책을 보면서 Static Factory Method에 대해 정리한 부분을 공유합니다.

honbabzone.com

 

정적 팩터리 메서드가 생성자보다 좋지않은  단점 두가지

 

1. 상속을 하려면 public 이나 protected 생성자가 필요하니 정적 팩터리 메서드만 제공하면 하위 클래스를 만들 수 없다.

 

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