[Ch9] 9.2.5 팩토리 패턴

kkambbak1·2023년 5월 4일
0

https://github.com/BanditBool2/ReadingRecord/issues/46

채원님 답변

static으로 Map을 관리하는 이유는 생성한 인스턴스를 계속 유지하기 위해서입니다. 만약 static이 아닌 인스턴스 변수로 Map을 관리한다면, 해당 인스턴스가 Garbage Collection될 때 생성한 인스턴스들도 함께 사라집니다. 그러므로 static으로 Map을 관리하여 어디서든지 생성한 인스턴스들을 참조할 수 있도록 하는 것이 좋습니다.

실제로 사용한다면, ProductFactory 클래스를 따로 만들어서 사용할 수도 있지만, 그렇게 하지 않고도 다른 클래스에서도 쉽게 접근해서 사용할 수 있도록 하기 위해서 static으로 관리하는 것입니다. 만약 해당 클래스가 다른 클래스에서 사용되지 않고 독립적으로 사용된다면, 별도의 클래스를 만들 필요 없이 Factory 패턴을 구현한 클래스 내에 Map을 관리하는 것이 더 효율적일 수 있습니다.

Supplier 인터페이스는 .get()메서드의 Lazy Evaluation으로 동일하게 작동하여 불필요한 인스턴스 생성을 방지하고, 필요한 시점에만 인스턴스를 생성하여 메모리를 효율적으로 사용할 수 있습니다. 다시 한번 상기시켜주셔서 감사합니다 👊


public class FactoryMain {

  public static void main(String[] args) {
    Product p1 = ProductFactory.createProduct("loan");
    System.out.printf("p1: %s%n", p1.getClass().getSimpleName());

    Supplier<Product> loanSupplier = Loan::new;
    Product p2 = loanSupplier.get();
    System.out.printf("p2: %s%n", p2.getClass().getSimpleName());

    Product p3 = ProductFactory.createProductLambda("loan");
    System.out.printf("p3: %s%n", p3.getClass().getSimpleName());
  }

  static private 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);
      }
    }

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

  static private interface Product {}
  static private class Loan implements Product {}
  static private class Stock implements Product {}
  static private class Bond implements Product {}

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

}

전체 코드를 보면 뭔가 신기하다. 어떻게 적용시켜야 할지 조금 더 공부해 보아야겠다.


람다로 객체 지향 디자인 패턴 리팩터링

일회성이라면 람다를 사용하는 것도 좋은 방법.

여러 메서드를 정의하는 등 복잡하다면, 람다 표현식보다 기존의 클래스를 구현하는 게 낫다.

전략패턴

한 유형의 알고리즘을 보유한 상태에서 런타임에 적절한 알고리즘을 선택하는 기법.

3부분의 부위로 나뉜다.

  • 알고리즘을 나타내는 인터페이스
  • 인터페이스를 구현한 구현 클래스 (StrategyA, StrategyB …)
  • 전략 객체를 사용하는 클라이언트

람다식으로 new Validator( (String s) → s.matches(”[a-z]+”)); 다양한 전략을 구현하는 클래스 없이 간결하게 표현

템플릿 메서드 패턴

세부 메소드를 abstract로 남겨놓고, 자식클래스에서 Override해서 사용하는 패턴.

세부 메소드를각각 구체화해서 생성한 상속받은 클래스들을 만드는 것.

메인 메소드에서는 Override한 세부메소드를 이용하여 각각의 기능을 실행한다.

예를 들어, 데이터베이스에서 id를 가져오는 것까지는 동일하고(메인메서드에서 처리)
결제 방식이 여러가지가 있다면 이를 세부 메소드에서 처리하게끔 abstract로 남겨놓고, 각각의 클래스로 만들어 처리하는 것.

메인메소드에 파라미터로 Consumer<> 형식을 갖는 인수를 추가하여 이를 람다식으로 구현한다.

그러면 세부 메소드를 따로 클래스로 만들지 않아도 됨.

옵저버 패턴

어떤 이벤트가 발생했을 때 한 객체(주제 Subject)가 다른 객체 리스트(Observer 옵저버)에 자동으로 알림을 보내야하는 상황

버튼을 클릭하면 옵저버에 알림이 전달되고 정해진 동작이 수행됨.

옵저버를 명시적으로 인스턴스화하지 않고 람다식을 직접 전달하여 실행할 동작 지정.

의무 체인

한 객체가 어떤 작업을 처리한 다음에 다른 객체로 전달하고, 다른객체도 해야할 작업을 처리한 다음에 또 다른 객체로 전달하는 식.

UnaryOperator와 andThen 메서드로 함수를 조합해서 체인만들기 가능

팩토리 패턴

인스턴스화한 로직을 클라이언트에 노출하지 않고 객체를 만들 때 사용

profile
윤성

0개의 댓글