1. Question

팩토리(factory) 디자인 패턴에서 람다를 활용하는 방법은?

2. Answer

A. 팩토리 디자인 패턴

인스턴스화 로직을 클라이언트에 노출하지 않고 객체를 만들 때 팩토리 디자인 패턴을 사용한다. 예를 들어 우리가 은행에서 일하고 있는데 은행에서 취급하는 대출, 채권, 주식 등 다양한 상품을 만들어야 한다고 가정해본다.

다음 코드에서 보여주는 것처럼 다양한 상품을 만드는 Factory 클래스가 필요하다.

public class ProductFactory {
  public static Product createProduct(String name) {
    switch(name) {
      case "loan": return new Loan();
      case "stock": return new Stock();
      case "bond": return new Bond();
      default: throw new RuntimeException("No such product " + name);
    }
  }
}

여기서 Loan, Stock, Bond는 모두 Product의 서브형식이다. createProduct 메서드는 생산된 상품을 설정하는 로직을 포함할 수 있다. 이는 부가적인 기능일 뿐 위 코드의 진짜 장점은 생성자와 설정을 외부로 노출하지 않음으로써, 클라이언트가 단순하게 상품을 생산할 수 있다는 것이다.

Product p = ProductFactory.createProduct("loan");

B. 람다 표현식 사용

생성자도 메서드 참조처럼 접근할 수 있다. 예를 들어 다음은 Loan 생성자를 사용하는 코드다.

Supplier<Product> loanSupplier = Loan::new;
Loan loan = loanSupplier.get();

이제 다음 코드처럼 상품명을 생성자로 연결하는 Map을 만들어서 코드를 재구현할 수 있다.

final static Map<String, Supplier<Product>> map = new HashMap<>();
static {
  map.put("loan", Loan::new);
  map.put("stock", Stock::new);
  map.put("bond", Bond::new);
}

이제 Map을 이용해서 다양한 상품을 인스턴스화할 수 있다.

public static Product createProduct(String name) {
  Supplier<Product> p = map.get(name);
  if(p != null) return p.get();
  throw new IllegalArgumentException("No such product " + name);
}

팩토리 패턴이 수행하던 작업을 Java 8의 새로운 기능으로 깔끔하게 정리할 수 있다. 하지만 팩토리 메서드 createProduct가 상품 생성자로 여러 인수를 전달하는 상황에서는 이 기법을 적용하기 어렵다. 단순한 Supplier 함수형 인터페이스로는 이 문제를 해결할 수 없다.

예를 들어 세 인수(Integer 둘, String 하나)를 받는 상품의 생성자가 있다고 가정할 수 있다. 세 인수를 지원하려면 TriFunction이라는 특별한 함수형 인터페이스를 만들어야 한다. 결국 다음 코드처럼 Map의 시그니처가 복잡해진다.

public interface TriFunction<T, U, V, R> {
  R apply(T t, U u, V v);
}
Map<String, TriFunction<Integer, Integer, String, Product>> map
  = new HashMap<>();

3. Detail

None

4. Reference

  • “모던 자바 인 액션” (저자: 라울-게이브리얼 우르마, 마리오 푸스코, 앨런 마이크로프트)

태그:

카테고리:

업데이트:

댓글남기기