-
Notifications
You must be signed in to change notification settings - Fork 93
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[#1] 생성자 대신 정적 팩터리 메서드를 고려하라.(rockjoon)
[#1] 생성자 대신 정적 팩터리 메서드를 고려하라.
- Loading branch information
Showing
1 changed file
with
124 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
# 생성자 대신 정적 팩토리 메소드를 고려하라. | ||
|
||
## 1. 개요 | ||
|
||
클라이언트가 클래스의 인스턴스를 얻는 수단은 다음과 같이 두 가지로 나눌 수 있다. | ||
* public 생성자 | ||
```java | ||
public Book(){} | ||
``` | ||
* 정적 팩터리 메서드 | ||
```java | ||
public static Book createBook(){ | ||
return instance(); | ||
} | ||
``` | ||
일반적인 public생성자에 비해 정적 팩터리 메서드는 다양한 장점을 가지고 있다. | ||
그 장점이 무엇인지 알아보자. | ||
|
||
## 2. 정적 팩터리 메서드의 장점 | ||
--- | ||
### (1). 이름을 가질 수 있다. | ||
* 정적 팩터리 메소드는 '메소드'이기 때문에 이름을 가질 수 있다. | ||
* 그 이름을 통해 해당 정적 메서드가 어떤 특징을 갖는지, `어떤 인스턴스를 반환하는지 명시적으로 표현`할 수 있다. | ||
|
||
--- | ||
|
||
### (2). 호출될 때마다 인스턴스를 새로 생성하지는 않아도 된다. | ||
정적 팩터리 메서드를 통해 `인스턴스 캐싱`과 `인스턴스 통제`를 활용할 수 있다. | ||
|
||
1). `인스턴스 캐싱` | ||
- 인스턴스를 미리 생성해 놓고, 필요할 때마다 이를 가져다 쓰는 방식을 뜻한다. | ||
- 불필요한 객체 생성을 피하고, 객체 생성의 비용을 줄여 준다. | ||
|
||
2). `인스턴스 통제` | ||
- 해당 인스턴스의 생명주기를 통제하는 것을 뜻한다. | ||
- 인스턴스를 통제하는 이유 | ||
+ 싱글턴을 만들기 위해 | ||
+ 인스턴스화 불가로 만들기 위해 | ||
+ 불변 클래스에서 값(value)이 같은 인스턴스는 단 하나임을 보장 | ||
|
||
|
||
--- | ||
### (3). 반환 타입의 하위 타입 객체를 반환할 수 있는 능력이 있다. | ||
정적 팩터리 메서드의 리턴 타입을 인터페이스로 할 경우 하위 타입 객체를 반환 할 수 있다. | ||
이는 주로 동반 클래스에서 볼 수 있다. | ||
|
||
>동반 클래스 : 자바 1.8 이전에는 인터페이스에 정적 메서드를 선언할 수 없었다. 따라서 인터페이스에 기능을 추가하기 위해서는 `동반 클래스`라는 것을 만들어 그 안에 정적 메서드를 추가했다. | ||
``` | ||
인터페이스 Collection의 동반 클래스 -> Collections | ||
``` | ||
다음은 Collections 클래스의 정적 팩토리 메소드 unmodifiableList의 모습이다. | ||
```java | ||
public static <T> List<T> unmodifiableList(List<? extends T> list) { | ||
return (list instanceof RandomAccess ? | ||
new UnmodifiableRandomAccessList<>(list) : | ||
new UnmodifiableList<>(list)); | ||
} | ||
``` | ||
* 이와 같이 팩터리 메서드를 통해 `메소드의 반환 타입은 List이지만 실제로는 List의 하위 객체를 반환`시킬 수 있다. | ||
* 이럴 경우의 장점은, 사용자들이 해당 인터페이스의 구현체를 일일이 알아 볼 필요가 없다는 것이다. | ||
|
||
--- | ||
### (4). 입력 매개변수에 따라 매번 다른 클래스의 객체를 반환할 수 있다. | ||
다음은 EnumSet의 정적 팩터리 메서드이다. | ||
메서드 내부에서 `universe.length 따라 리턴 타입을 다르게` 반환 한다. | ||
```java | ||
public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) { | ||
Enum<?>[] universe = getUniverse(elementType); | ||
if (universe == null) | ||
throw new ClassCastException(elementType + " not an enum"); | ||
|
||
if (universe.length <= 64) | ||
return new RegularEnumSet<>(elementType, universe); | ||
else | ||
return new JumboEnumSet<>(elementType, universe); | ||
} | ||
``` | ||
* 이와 같이 팩터리 메서드를 통해 `사용자가 내부의 구현에 대해 알 필요 없이` 원하는 반환 값을 전달 받을 수 있다. | ||
* 또한 메서드의 내부 구현이 변경되어도, 반환타입만 같다면 사용자에게 영향을 끼치지 않는다. | ||
--- | ||
|
||
## 3. 정적 팩터리 메서드의 단점 | ||
--- | ||
|
||
### (1). 상속을 하려면 public이나 protected 생성자가 필요하니 정적 팩터리 메서드만 제공하면 하위 클래스를 만들 수 없다. | ||
* 상속을 위해서는 하위 클래스에서 사용할 수 있는 생성자가 필요하다. | ||
* private 생성자를 통해 외부 생성을 막고, 정적 팩터리 메서드만을 사용하여 인스턴스를 반환할 경우, 하위 클래스를 만들 수 없다. | ||
|
||
### (2) 정적 팩터리 메서드는 프로그래머가 찾기 어렵다. | ||
* 생성자는 이름이 클래스의 이름과 같아서 명확히 알 수 있다. | ||
* 하지만 정적 팩터리 메서드는 다른 메서드와 섞여 찾기 어려울 수 있다. | ||
|
||
## 4. 정적 팩터리 메서드의 네이밍 | ||
정적 팩터리 메서드는 찾기 어렵다는 단점 때문에, 일반적으로 쓰이는 네이밍 방식들이 있다. | ||
그 내용은 다음과 같다. | ||
|
||
* from : 매개변수를 하나 받아, 해당 타입의 인스턴스를 반환하는 형변환 메서드 | ||
```java | ||
// instant 타입을 Date로 변환하여 반환 | ||
Date d = Date.from(instant); | ||
``` | ||
* of : 매개변수를 여러개 받아 적합한 타입의 인스턴스를 반환하는 집계 메서드 | ||
```java | ||
// 파라미터로 전달 받은 타입의 Enum을 담은 Set을 반환 | ||
Set<Rank> faceCards = Enumset.of(JACK, QUEEN, KING); | ||
``` | ||
* valueOf : from과 of의 더 자세한 버전 | ||
* instance 혹은 getInstance : 인스턴스를 반환, 그러나 같은 인스턴스임을 보장하지는 않음. | ||
* create 혹은 newInstance : 새로운 인스턴스 생성하여 반환 | ||
* getXXX : getInstance와 같으나, 생성할 클래스가 아닌 다른 클래스에 팩터리 메서드를 정의할 때 사용. | ||
```java | ||
// Files 클래스에서 FileStore의 인스턴스를 반환 | ||
FileStore fs = Files.getFileStore(path); | ||
``` | ||
* newXXX : newInstance와 같으나, 생성할 클래스가 아닌 다른 클래스에 팩터리 메서드를 정의할 때 사용. | ||
```java | ||
// Files 클래스에서 BufferedReader를 반환 | ||
BufferedReader br = Files.newBufferedReader(path); | ||
``` | ||
* xxx : getXXX과 newXXX의 간결한 버전 | ||
```java | ||
List<Complaint> litany = Collections.list(legacyListany); | ||
``` |