네로개발일기

개발자 네로의 개발 일기, 자바를 좋아합니다 !

'2021/09/06'에 해당되는 글 2건


반응형

# 생성자에 매개변수가 많다면 빌더를 고려하라

- 생성자나 정적 팩터리가 처리해야 할 매개변수가 많다면 빌더 패턴을 선택하는 것이 낫다.

- 매개변수 중 다수가 필수가 아니거나 같은 타입이면 빌더 패턴을 선택하는 것이 낫다.

- 빌더는 점층적 생성자보다 클라이언트를 읽고 쓰기가 훨씬 간결하다.

- 빌더는 자바빈즈보다 훨씬 안전하다.

 

정적 팩터리와 생성자는 "선택적 매개변수"가 많을 때 적절하게 대응하기 어렵다는 단점이 있다.

 

## 점층적 생성자 패턴(Telescoping Constructor Pattern)

- 필수 매개변수를 받는 생성자 1개, 그리고 선택 매개변수를 하나씩 늘려가며 생성자를 만드는 패턴      

=> 단점     

1. 클라이언트가 초기화하고 싶은 필드만 포함한 생성자가 없으면, 원하지 않는 필드까지 불가피하게 초기화

2. 복잡하고 읽기 어려움

 

## 자바빈즈 패턴(JavaBeans Pattern)

- setter를 사용하자       

=> 단점     

1. 객체 하나를 만들려면 메서드를 계속해서 호출해야하고,

2. 그 사이 일관성을 유지하기 어렵다. => 런타임 문제 디버깅이 하드해진다. (1회의 함수 호출로 객체 생성을 끝낼 수 없으므로...)   

    - 즉, setter가 가지는 모든 단점을 가지게 된다. "불변 객체로 만들 수 없다." => Thread 안전하지 않다.       

 

## 빌더 패턴

- 필수 매개변수만으로 생성자(정적 팩터리)를 호출해 빌더 객체를 얻는다.

- 일종의 setter 메서드로 원하는 선택 매개변수를 설정한다.

- build 메서드를 호출해 필요한 객체를 얻는다. (주로 불변)

 

## 빌더 패턴의 쓰임새

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

- simulated self-type: 추상 메서드 self를 더해 하위 클래스에서 형 변환없이 메서드 연쇄를 지원한다. 

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

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

 

## 빌더 패턴의 단점

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

- 매개변수가 4개 이상은 되어야...

728x90
반응형
blog image

Written by ner.o

개발자 네로의 개발 일기, 자바를 좋아합니다 !

반응형

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

 

인스턴스를 생성하는 방법은  

(1) public 생성자   

(2) 정적 팩터리 메서드

 

## 정적 팩터리 메서드의 장점

### 1. 이름을 가질 수 있다.

생성자는 클래스명 형태로만 구현할 수 있지만,   

정적 팩터리 메서드는 자신의 이름을 가질 수 있다.    

직접 네이밍을 할 수 있기 때문에 반환될 객체의 특성을 쉽게 파악할 수 있다.   

 

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

**static 자원은 JVM 클래스로더의 "초기화" 부분에서 할당된다.**

반복되는 요청에 같은 객체를 반환하는 정적 팩터리 방식의 클래스는 언제 어느 인스턴스를 살아있게 할지를 철저히 통제할 수 있다.

인스턴스를 통제하면 해당 클래스로 만들어질 객체의 수량을 조절할 수 있다.

- 객체 1개 제한: Singleton

 

=> 불변 클래스 (immutable class)를 만들 수 있음.

@Test
void booleanTest() {

    Boolean boolean1 = Boolean.valueOf(true);
    Boolean boolean2 = Boolean.valueOf("true");
    Boolean boolean3 = new Boolean(true);

    assertThat(boolean1).isSameAs(boolean2);
    assertThat(boolean1).isSameAs(boolean3);    
}

Boolean.valueOf()는 Boolean 객체를 반환하지만 객체를 생성하지 않음.  

true를 지칭하는 Boolean 타입의 변수는 같은 레퍼런스를 바라보고있다. 하지만 new 를 통해 생성하는 인스턴스는 새로운 인스턴스이기 때문에 같은 레퍼런스를 바라보고 있지 않다.

 

* 플라이웨이트 패턴

- 데이터를 공유하여 메모리를 절약하는 패턴

- 공통으로 사용되는 객체는 한 번만 생성되고 공유를 통해 풀(Pool)에 의해 관리, 사용된다. 없으면 만들고 있으면 있는 것을 반환하는 패턴

 

### 3. 반환 타입의 하위 타입 객체를 반환할 수 있다.

반환되는 객체의 클래스를 훨씬 유연하게 결정할 수 있다.  

인터페이스로 정적 팩토리 메서드를 구현하면 다형성을 가진 객체를 제공할 수 있다.     

 

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

정적 팩터리 메서드를 사용하면 동적으로 적절한 타입의 객체를 반환할 수 있다.     

public class Ticket {

    public static Ticket getTicketByType(String type) {
        if (type.equals("vip"))
            return VipTicket.INSTANCE;
            
        if (type.equals("general"))
            return GeneralTicket.INSTANCE;

        new IllegalArgumentException("wrong type");
    }
}

 

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

- 반환값이 인터페이스여도 된다.

- 정적 팩터리 메서드의 변경없이 구현체를 바꿔끼울 수 있다. => 반환값의 구현체이기만 한다면 가능함. 이를 통해 **유연한 시스템 구현이 가능하다.**

 

## 정적 팩터리 메서드의 활용 예시 - 서비스 제공자 프레임워크

- 서비스 인터페이스: 구현체의 동작 정의

- 제공자 등록 API: 제공자가 구현체 등록

- 서비스 접근 API: 클라이언트가 서비스의 인스턴스 얻을 때 => 원하는 구현체의 조건을 입력하고 그에 따라 기본 구현체를 반환할 수 있음. 이것을 정적 팩터리로 작성할 수 있다.

- 서비스 제공자 인터페이스 (SPI): 서비스 인터페이스 인스턴스를 생성하는 팩터리 객체 설명

 

## 정적 팩터리 메서드의 단점

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

- Collections는 상속할 수 없다. 생성자가 private으로 구현되어 있다.

- 상속보다 컴포지션을 유도할 수 있기 때문에 OCP에 적합하다.

 

### 2. 찾기 어려움. 통용되는 네이밍 준수

- from: 매개변수를 받아서 해당 타입의 인스턴스 반환. 형 변환

- of: 여러 매개변수를 받아 적합한 인스턴스 반환

- valueOf: from, of 보다 자세한 버전

- instance, getInstance: 매개변수 인스턴스를 반환하지만 보장하지는 않음

- create, newInstance: 매번 새로운 인스턴스 생성해 반환

- getType: 반환 타입과 팩터리 메서드 클래스가 다름. Type은 반환타입 명시

- type: getType, newType 간결한 버전

 

## 핵심 정리

- 정적 팩터리 메서드와 public 생성자는 각자의 쓰임새가 있으니 상대적인 장단점을 이해하고 사용하는 것이 좋다.

- 정적 팩터리 메서드를 사용하는 게 유리한 경우가 있을 수 있으므로 public 생성자를 제공하던 습관이 있다면 고치도록 한다. 

 

728x90
반응형
blog image

Written by ner.o

개발자 네로의 개발 일기, 자바를 좋아합니다 !