[JAVA / Design Pattern] 생성자 대신 사용하는 정적 팩토리 메서드



클래스는 기본적으로 객체를 생성할 때, 자동적으로 생성자를 호출하게 되어있다.

예를 들어 Person p = new Person() 에서 new는 객체를 동적할당 시키고,

Person()은 매개변수가 없는 생성자 이다.

생성자는 여러개 만들 수 있다. 하지만 매개변수의 갯수는 달라야 한다.

이런것을 오버로딩(Overloading) 이라고 한다.

생성자는 클래스의 이름과 똑같아야 되므로 항상 클래스이름() 이다.

그래서 사용자가 생성자를 호출할 때 이 생성자가 무슨동작을 하는지 알 수 없다.

그래서 나온 것이 정적 팩토리 메서드인데, 정적 팩토리 메서드를 사용함으로써

생성자에게 이름을 지어줄 수 있는 기타 등등으로 사용한다.

한번 알아보자.



1. 생성자와는 달리 정적 팩터리 메서드에는 이름이 있다.

위에서 얘기했듯이 생성자에게 이름을 지어줄 수 있다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Person {
    private Person() {
    
    }
    
    public static class Programmer {
    private Programmer() {
            
        }
    }
    
    public static Programmer getProgrammer() {
        return new Programmer();
    }
}
cs
1
2
3
4
5
6
7
8
9
public class StaticFactoryClass {
    public static void main(String[] args) {
        
        Programmer programmer = Person.getProgrammer();
    }
}
cs


2. 생성자와 달리 호출할 때마다 새로운 객체를 생성할 필요가 없다.


이러한 장점으로 나온 디자인 패턴중의 하나가 싱글톤 기법이다.

싱글톤의 장점은 새로운 객체를 만들지 않고 객체를 만들어 놓고,

그 객체를 계속 가져다가 사용한다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Person {
    
    private static Person person = new Person();
    
    //생성자로 객체 생성 불가능 
    private Person() {
    
    }
    
    public static Person getInstance() {
        return person;
    }
    
    
}
cs

1
2
3
4
5
6
7
8
9
10
11
12
public class StaticFactoryClass {
    public static void main(String[] args) {
        
        Person person = Person.getInstance();
        Person person2 = Person.getInstance();
        
        System.out.println(person.hashCode());
        System.out.println(person2.hashCode());
        
    }
}
cs







person 과 person2의 hashCode값은 모두 동일한 것을 알 수 있다.




3. 생성자와는 달리 반환값 자료형의 하위 자료형 객체를 반환할 수 있다는 것이다.


Person 클래스

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Person {
    
    private static Person person = new Person();
    
    // 상속 받아야되므로 public으로 바꿈.
    //private면 상속 불가능하다.
    public Person() {
    
    }
    
    public static Programmer getProgrammerInstance() {
        return Programmer.getInstance();
    }
    
}
cs




Person 클래스를 상속받는 Programmer 클래스

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Programmer extend Person {
    private static Programmer programmer = new Programmer();
    
    //생성자 제한.
    private Programmer() { 
        
    }
    
    public static Programmer getInstance() {
        return programmer;
    }
    
    
}
cs


Main 함수

1
2
3
4
5
6
7
8
public class TypeExMain {
    
    public static void main(String[] args) {
        
        Programmer programmer = Person.getProgrammerInstance();
    }
}
cs

Person  클래스에서 Programmer 객체(하위자료형)를 반환 시키고 있다.



4. 형인자 자료형 객체를 만들 때 편하다는 점.


여기서 형인자 자료형이란

class Box<T> { .. }

Box<T>는 generic Type 이다.

제너릭타입은 형식매개변수로 선언한다.



Box<Integer> b;

Box<Integer>가 형인자 자료형이다.

형인자 자료형은 실제매개변수로 선언한다.

List<String> ...  등 이런것들도 다 형인자 자료형이다.


java.util.Hashmap에 있는 Map으로 예를 들어보면

1
Map<String,List<String>> m = new HashMap<String, List<String>>();



문맥상 자료형 명백하더라도 new HashMap 부분에 한번더 자료형 작성해줘야 한다.

그래서 보통 자로형을 연달아 두 번 사용하게 된다.

이처럼 자료형을 중복하면, 형인자가 늘어남에 따라 길고 복잡한 코드가 만들어 진다.

하지만 정적 팩터리 메서드를 사용하면 컴파일러가 형인자를 스스로 알아 낼 수 있다.

이런 기법을 자료형 유추라고 부른다.

예를 들어, HashMap 클래스가 아래의 제너릭 정적 팩터리 메서드를 제공한다고 가정해보면


1
2
3
public static <K, V> HashMap<K, V> newInstance() {
    return new HashMap<K, V>();
}




이런 메서드가 있으면 앞에 선언문을 좀 더 간결하게 작성할 수 있다.


1
Map<String, List<String>> m = HashMap.newInstance();


언젠가는 생성자를 호출할 때 도 자료형 유추가 이루어지지 않을까 생각 된다.


정적 팩터리 메서드만 있는 클래스를 만들면 어떤 문제가 생기는지 알아보자.

1. public이나 protected로 선언된 생성자가 없으므로 하위 클래스를 만들 수 없다.
- public 정적 팩터리 메서드가 반환하는 비-public 클래스도 마찬가지다.
  예를 들어  자바의 컬렉션(Collections) 프레임워크에 포함된 기본 구현 클래스들의 하위 클래스는 만들 수 없다.

2. 정적 팩터리 메서드가 다른 정적 메서드와 확연히 구분되지 않는다.
- API 문서를 보면 생성자는 다른 메서드와 뚜렷이 구별되지만, 정적 팩터리 메서드는 그렇지 않다.
그러니 생성자 대신 정적 팩터리 메서드를 통해 객체를 만들어야 하는 클래스는 사용법을 파악하기가 쉽지 않다. 지금으로서는 클래스나 인터페이스 주석을 통해 정적 팩터리 메서드임을 알리거나 정적 팩터리 메서드 이름을 지을ㄷ 때 조심하는 수 밖에 없다.

댓글

댓글 쓰기

이 블로그의 인기 게시물

Filter url 제외시키기

[Spring,Java] Validator 구현하기

[Spring] Mock framework에 대하여