이펙티브 자바-01.생성자 대신 정적 팩터리 메서드를 고려하라


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

장점

  1. 이름을 가질 수 있다. 생성자에 비해 가독성이 좋다.
  2. 호출될 때마다 인스턴스를 새로 생성하지는 않아도 된다.
  3. 반환 타입의 하위 자료형 객체를 반환할 수 있는 능력이 있다..
  4. 입력 매개변수에 따라 매번 다른 클래스의 객체를 반환할 수 있다.
  5. 정적 팩토리 메서드를 작성하는 시점에는 반환할 객체의 클래스가 존재하지 않아도 된다.

단점

  • 정적 팩토리 메서드만 있는 클래스라면, 생성자가 없으므로 하위 클래스를 못 만든다.
  • 정적 팩토리 메서드는 다른 정적 메서드와 잘 구분되지 않는다. (문서만으로 확인하기 어려울 수 있음, 프로그래머가 찾기 어렵다.)

코드를 통한 이해

장점1. 가독성이 좋다

다음은 캐릭터 클래스이다.(직업이 전사, 마법사 존재)

class Character {

    int intelligence, strength, hitPoint, magicPoint;

    public Character(int intelligence, int strength, int hitPoint, int magicPoint) {
        this.intelligence = intelligence;   // 지능
        this.strength = strength;           // 힘
        this.hitPoint = hitPoint;           // HP
        this.magicPoint = magicPoint;       // MP
    }

    // 정적 팩토리 메소드
    public static Character newWarrior() {
        return new Character(5, 15, 20, 3);     // 전사는 힘과 HP가 높다
    }

    // 정적 팩토리 메소드
    public static Character newMage() {
        return new Character(15, 5, 10, 15);    // 마법사는 지능과 MP가 높다
    }
}

만약 생성자를 사용해 전사나 마법사를 생성한다면 다음과 같을 것이다.

Character warrior = new Character(5, 15, 20, 3);
Character mage = new Character(15, 5, 10, 15);

변수명이 없었다면 5, 15, 20, 3 같은 연속된 숫자만으로는 캐릭터의 직업을 알아보기 어려웠을 것이다.

하지만 정적 팩토리 메서드를 사용한다면 좀 더 읽기 쉬운 코드가 된다.

Character warrior = Character.newWarrior();
Character mage = Character.newMage();

장점2. 호출될 때마다 인스턴스를 새로 생성하지는 않아도 된다.
Boolean.valueOf(boolean) 같은 경우 객체를 아예 생성하지 않는다.
반환만 한다, 비싼 new 를 계속 사용하지 않는다.
이해하기 좀 더 쉽게 코드로 살펴보면..

일반적으로 테스트 코드를 작성하면서 보았겠지만

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.request;

@ExtendWith(SpringExtension.class)
@WebMvcTest
class SampleControllerTest {

    @Autowired
    MockMvc mockMvc;

    @Test
    public void eventForm() throws Exception {
        mockMvc.perform(get("/events/form"))
                .andDo(print())
                .andExpect(view().name("events/form"))
                .andExpect(model().attributeExists("event"))
                .andExpect(request().sessionAttribute("event", notNullValue()))
        ;
    }
public abstract class MockMvcResultMatchers {

	private static final AntPathMatcher pathMatcher = new AntPathMatcher();
	public static RequestResultMatchers request() {
		return new RequestResultMatchers();
	}
    
	public static HandlerResultMatchers handler() {
		return new HandlerResultMatchers();
	}

	public static ModelResultMatchers model() {
		return new ModelResultMatchers();
	}
...

정적 메서드를 사용한 것을 볼 수 있다.
한번만 new로 생성해놓고, 계속 사용한다.




© 2019. by jaeuk