티스토리 뷰
어떤 객체의 인스턴스를 얻기 위해 보통 public 생성자를 통해 객체를 생성한다.
이러한 방법 말고 public static 팩토리 메소드를 사용해 해당 클래스의 인스턴스를 만들 수 있다.
static 팩토리 메소드에는 다음과 같은 장단점이 있다.
장점 1. 이름을 가질 수 있다.
public class Person {
String name;
String address;
int age;
public Person(String name, String address,int age) {
this.name = name;
this.address = address;
this.age = age;
}
public static Person 김꺽정() {
return new Person("김꺽정", "북한",32);
}
}
생성자는 class 이름을 그대로 따라야 해서 생성자가 반환하는 객체를 잘 설명하지 못할 수 있다.
static 팩토리를 사용하면 김꺽정 씨의 객체정보를 가진다고 명시적으로 알 수 있다.
또한 생성자의 시그니처 제약으로 부터 자유로울 수 있다.
public Person(String name) {
this.name = name;
}
// 오류!!
public Person(String address) {
this.address = address;
}
public static Person withName(String name) {
Person p = new Person();
p.name = name;
return p;
}
public static Person withAddress(String address) {
Person p = new Person();
p.address = address;
return p;
}
똑같은 타입을 파라미터로 받는 생성자 여러 개는 공존할 수 없다.
이런 경우에 public static 팩토리 메소드를 사용할 수 있다.
장점 2. 호출될 때마다 인스턴스를 새로 생성하지 않을 수 있다.
private Person() {}
private static final Person p = new Person();
public static Person getInstance() {
return p;
}
위와 같은 방식으로 클래스를 singletone, noninstantiable화 할 수 있다.
장점 3. 리턴 타입의 하위 객체를 리턴할 수 있다.
자바 8 이전에는 인터페이스에 static 메소드를 선언할 수 없었다고 한다.
그래서 이름이 Type인 인터페이스를 리턴하는 static 메소드가 필요하면
Types라는 noninstantiable 클래스를 만들어 그 안에 정의해 주었다.
public class Collections {
// Suppresses default constructor, ensuring non-instantiability.
private Collections() {
}
...
}
public interface Collection<E> extends Iterable<E> {
...
}
// Collections는 클래스고 Collection은 인터페이스다
그 예시가 Collection 프레임워크이다.
자바 8 이후부터는 저렇게 따로 안 빼고 인터페이스 자체에 public static 메소드를 정의할 수 있다.
그러나 다른 메소드에서 호출되는 private static 메소드는 자바 9부터 가능하다.
자바 8 버전에서 private static 메소드처럼 쓰고 싶으면 Collections처럼 파일을 밖으로 빼야 할 수 있다,
장점 4. 입력 매개변수에 따라 매번 다른 객체를 반환할 수 있다.
public static Object Drunken(boolean flag) {
return flag?new Animal():new Person();
}
여기서 Object는 Animal과 Person의 상위 인터페이스라고 생각하면 좋겠다. 귀찮아서 안 만들었다.
위 코드처럼 들어오는 파라미터 값에 따라 리턴되는 객체를 다르게 할 수 있다.
실제로 EnumSet 클래스는 public 생성자 없이 static 팩토리 메소드만 제공된다.
이때 enum의 개수에 따라 RegularEnumSet과 JumboEnumSet으로 달라진다.
EnumSet의 하위 타입은 감춰져 있기 때문에 수정과 삭제에 용이하다. (이건 인터페이스의 특징 같기도 하고)
장점 5. static 펙토리 메소드를 작성하는 시점에 반환할 객체의 클래스가 존재하지 않아도 된다.
무슨 소리인지 이해가 잘 안 된다. 정말 화가 난다.
책에서 예시를 JDBC로 들어 옛날에 쓴 JDBC 코드를 들고 와봤다.
public class DBUtil {
private static final String DRIVER_CLASSNAME = "com.mysql.cj.jdbc.Driver";
private static final String DB_URL = "jdbc:mysql://127.0.0.1:3306/비밀";
private static final String DB_USER = "비밀";
private static final String DB_PASS = "비밀";
static {
try {
// step1
Class.forName(DRIVER_CLASSNAME);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(DB_URL, DB_USER, DB_PASS);
}
...
}
DriverManager.getConnection()은 Driver, user, password에 따라 다른 객체를 반환한다.
장점 3, 4번과 마찬가지로 유연하게 객체를 반환할 수 있다고 이해했다.
장점이 다섯 개나 되니 단점이 없을 수가 없다.
단점 1. 상속을 할 때는 public 혹은 protected 생성자가 필요한데 static 팩토리 메소드만 제공을 하면 하위 클래스를 만들 수 없다.
상속보다 컴포지션을 유도하고 immutable 하게 만들려고 한다면 이 제약이 장점이 될 수 있다.
단점 2. 프로그래머가 팩토리 메소드를 찾기 힘들다.
생성자는 자동으로 문서가 생성되지만 팩토리 메소드는 그렇지 않다. static 팩토리 메소드들에서
암묵적으로 널리 알려진 명명 방식이 있다. 이를 잘 지키면 어느 정도 해결 할 수 있을 것 같다.
'Book > Effective Java' 카테고리의 다른 글
Effective Java - Item 6. 불필요한 객체 생성을 피하라 (0) | 2023.01.05 |
---|---|
Effective Java - Item 5. 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라 (0) | 2023.01.03 |
Effective Java - Item 4. 인스턴스화를 막으려거든 private 생성자를 사용하라. (0) | 2023.01.02 |
Effective Java - Item 3. private 생성자나 열거 타입으로 싱글턴임을 보증하라 (0) | 2023.01.02 |
Effective Java - Item 2. 생성자에 매개변수가 많다면 빌더를 고려하라 (0) | 2023.01.02 |