정적 팩토리 메서드 적용

손창현·2022년 6월 8일
0

정적 팩토리 메서드

정적 팩토리 메서드란 간단하게 설명해서 생성자를 통해 객체를 생성하는게 아닌 객체 내의 정적 메서드를 정의해 객체를 생성하는 것이다.

왜 굳이 생성자를 사용하지 않고 별도의 메서드로 객체를 생성하는 것일까?
어떤 상황에서 정적 팩토리를 고려해야 할까?

1. 객체 생성 목적에 따른 이름 짓기

생성자에 넘기는 매개변수생성자 자체만으로는 어떤 목적으로 생성된 객체인지 파악하기 힘들 수 있다. 하지만 정적 팩토리 메서드를 사용하면 객체의 생성 목적을 메서드 이름을 통해 알 수 있기 때문에 객체의 생성 시점을 명확히하고 생성 목적을 한번에 알아 볼 수 있게 된다.

아래 코드는 클라이언트를 통해 받은 상품 정보를 new 생성자를 사용하여 새로운 상품을 등록하는 코드이다.

Item item = createItemForm.toEntity();
List<ItemImage> itemImageList = itemImageService.saveItemImageList(createItemForm.getItemImageFiles());

Item saveItem = new Item(item.getItemName(), item.getItemDetail(),
						 item.getPrice(), item.getStockQuantity(), itemImageList);

return itemService.createItem(saveItem);

new 생성자를 사용해 객체를 생성하게 되면 한번에 어떤 목적으로 new 생성자가 사용됬는지 알 수 없다.

따라서 아래 코드와 같이 객체 생성의 목적을 두기 위한 리팩토링을 진행하였다.

Item item = createItemForm.toEntity();
List<ItemImage> itemImageList = itemImageService.saveItemImageList(createItemForm.getItemImageFiles());

Item saveItem = Item.createItem(item, itemImageList);

return itemService.createItem(saveItem);

또한 시그니처가 같은 클래스의 생성자를 해결할 때도 고려할 수 있다.

public class Order {

    private boolean prime;

    private boolean urgent;

    private Product product;

    public Order(Product product, boolean prime) {
        this.product = product;
        this.prime = prime;
    }

    public Order(Product product, boolean urgent) {
        this.product = product;
        this.urgent = urgent;
    }
}

위 코드와 같이 우리가 보기엔 prime과 urgent로 다르지만 boolean이라는 같은 시그니처를 가지고 있기 때문에 자바 컴파일러는 이를 그냥 지나치지 않고 에러를 뱉는다.

위 코드를 당장 언발에 오줌누듯 아래와 같이 변경할 수 있다.

public Order(Product product, boolean prime) {
        this.product = product;
        this.prime = prime;
    }

    public Order(boolean urgent, Product product) {
        this.product = product;
        this.urgent = urgent;
    }

하지만 객체를 생성하는 시점에서 해당 생성자들의 사용 목적을 사용자가 쉽게 구분할 수 없다. 그렇기 때문에 아래 코드와 같이 정적 팩토리 메서드를 사용해 메서드에 이름을 지어줌에 따라 명확한 목적을 알려주고 구분지어줄 수 있다.

public static Order primeOrder(Product product, boolean prime) {
        Order order = new Order();
        order.prime = prime;
        order.urgent = true; // 임의 설정

        return order;
    }

    public static Order urgentOrder(Product product, boolean urgent) {
        Order order = new Order();
        order.prime = true; // 임의 설정
        order.urgent = urgent;

        return order;
    }

2. 객체 생성을 숨기기

여기서 말하는 숨기기란 객체를 생성하는 코드를 해당 객체의 메서드 안에서 구현하는 즉, 객체 생성을 캡슐화하는 것을 의미한다.

아래는 클라이언트에서 회원 정보를 수정한다는 요청이 왔을 때, 서버에서 UpdateMemberForm이란 객체에 회원 정보를 담아 다시 클라이언트로 보내주는 과정이 담긴 Controller코드이다.

public String myInfo(@AuthenticationPrincipal AccountContext accountContext, Model model) throws Exception {

Member member = accountContext.getMember();

UpdateMemberForm UpdateMemberForm = new UpdateMemberForm(member);

model.addAttribute("updateMemberForm", updateMemberForm);

return "member/myInfo";
}

new 생성자를 사용해 객체를 생성하는 코드를 사용해 객체를 생성하는 코드를 외부로 노출시키게 되었다.

하지만 정적 팩토리 메서드를 사용하게 되면 객체 생성을 클래스의 메서드 안으로 숨길 수 있게 됨으로써 객체 생성을 캡슐화할 수 있게 된다.

따라서 아래 코드와 같이 객체 생성을 캡슐화하기 위한 리팩토링을 진행하였다.

public String myInfo(@AuthenticationPrincipal AccountContext accountContext, Model model) throws Exception {

Member loginMember = accountContext.getMember();

UpdateMemberForm updateMemberForm = UpdateMemberForm.from(loginMember);

model.addAttribute("updateMemberForm", updateMemberForm);

return "member/myInfo";
}
public static UpdateMemberForm from(Member member) {

        Address address = member.getAddress();
        
        return UpdateMemberForm.builder()
                .loginId(member.getLoginId())
                .memberName(member.getMemberName())
                .email(member.getEmail())
                .city(address.getCity())
                .street(address.getStreet())
                .zipcode(address.getZipcode())
                .build();
    }

정적 팩토리 메서드 네이밍 컨벤션

  • from : 하나의 매개 변수를 받아서 인스턴스를 생성.
  • of : 여러개의 매개 변수를 받아서 인스턴스를 생성.
  • getInstance | instance : 인스턴스를 생성. 항상 동일한 인스턴스를 반환한다는 보장을 하지 않음.
  • newInstance | create : 새로운 인스턴스를 생성.
  • get[OtherType] : 특정한 타입의 인스턴스를 생성.
  • new[OtherType] : 특정한 타입의 새로운 인스턴스를 생성.
profile
백엔드개발자 손창현입니다.

0개의 댓글