계속지나가기
코딩스뮤
계속지나가기
전체 방문자
오늘
어제
  • 코딩스뮤:)
    • Algorithm
      • 백준 문제풀이
      • 프로그래머스 문제풀이
      • 알고리즘 이론
      • 자료구조
      • SW Expert Academy
    • 인공지능(AI)
      • LLMs
      • 자연어처리(NLP)
      • 컴퓨터비전(CV)
      • 딥러닝(DL)
      • 머신러닝(ML)
      • 인공지능기초수학
      • 선형대수학
    • 컴퓨터 세팅
    • Computer Science
      • 유닉스프로그래밍
      • 프로그래밍언어론
      • 디자인패턴
      • 클린코드
      • SW 영어
      • 리눅스
      • 논리회로
    • Server
      • Docker

블로그 메뉴

  • 홈
  • Who Am I(CV)
  • 태그

공지사항

인기 글

태그

  • 손실함수
  • 디지털이미지처리
  • 에지검출
  • 지도학습
  • 컴퓨터비전
  • NLP
  • MaximumFlow
  • 언어모델
  • 최대유량
  • 기계학습
  • f1-score
  • DigitalImageProcessing
  • 파이썬 클린코드
  • 비지도학습
  • networkflow
  • 비용함수
  • 머신러닝
  • 선형회귀
  • 패턴인식
  • SIFT
  • 경사하강법
  • DIP
  • 네트워크플로우
  • 군집화
  • 결정경계
  • LM
  • ML
  • 알고리즘
  • machinelearning
  • ComputerVision

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
계속지나가기

코딩스뮤

[Head First Design Patterns] 04 팩토리 패턴
Computer Science/디자인패턴

[Head First Design Patterns] 04 팩토리 패턴

2020. 10. 20. 19:33
반응형

(본 강의 노트는 한빛 미디어의 [Head First Design Patterns]책을 기반으로 하고 있습니다)

04 팩토리 패턴

Factory Method Pattern

목적

  • Defining an interface for creating an object
  • 객체 생성용 인터페이스 정의
  • 단, 서브클래스가 어떤 클래스를 인스턴스화(객체 생성)할 지 결정할 수 있도록 함.
  • 펙토리 메소드는 객체 생성을 서브 클래스에서 하도록 미룰 수 있게 해줌

Abstact Factory Pattern

  • 목적 : 구체적인 클래스를 명시하지 않고 관련된 혹은 의존적인 객체들을 생성할 수 있는 인터페이스 제공

문제

  • 객체를 생성하는 'new' 의 문제
    : new는 인터페이스가 아니라 실제 클래스를 생성
    • OCP에 어긋남 : not closed for modification
      1. 생성할 객체가 늘어나면 코드 수정 필요
      2. 클래스가 많아지거나 변경되면 클라이언트 측 변경이 많아짐

디자인 패턴 요소

요소

설명

이름

팩토리 메소드(Factory Method), 추상 팩토리(Abstract Factory)

문제

실제로 구현되는 클래스의 객체를 생성할 때 객체의 종류가 달라지면 클라이언트 코드를 수정해야 하는 것이 너무 많음

해결방안

생성을 분리해서 캡슐화 시킴

결과

사용할 객체가 많거나 객체를 생성하는 방법이 변경되어도 연쇄적인 수정이 적어짐

사례1 - 피자 가게

  • 피자 가게를 운영하고 있다고 가정
  • 피자 주문을 위해 아래 코드를 작성
void prepareToBoxing(Pizza pizza) {
  pizza.prepare();
  pizza.bake();
  pizza.cut();
  pizza.box();
}

Pizza orderPizza() {
  Pizza pizza = new Pizza();

  prepareToBoxing(pizza);
  return pizza;
}
  • 문제점 : 피자 종류가 여러가지 있으면 코드를 수정해야 함
Pizza orderPizza(String type) {
  Pizza pizza;

  if (type.equals("cheese")) {
    pizza = new CheesePizza();
  } else if (type.equals("greek") {
    pizza = new GreekPizza();
  } else if (type.equals("pepperoni") {
    pizza = new PepperoniPizza();
  }

  prepareToBoxing(pizza);
  return pizza;
}
  • 해결 : 객체 생성 부분을 캡슐화
public class SimplePizzaFactory {
  public Pizza createPizza(String type) {
    Pizza pizza = null;
    if (type.equals("cheese")) {
      pizza = new CheesePizza();
    } else if (type.equals("pepperoni") {
      pizza = new PepperoniPizza();
    } else if (type.equals("clam") {
      pizza = new ClamPizza();
    } else if (type.equals("veggie") {
      pizza = new VeggiePizza();
    }  
    return pizza;
  }
}
  • SimplePizzaFactory 사용
public class PizzaStore {
  SimplePizzaFactory factory;
  public PizzaStore(SimplePizzaFactory factory) {
    this.factory = factory;
  }
  public Pizza orderPizza(String type) {
    Pizza pizza = null;
    pizza = factory.createPizza(type);
    prepareToBoxing(pizza); 
    return pizza;
  }
  void prepareToBoxing(Pizza pizza) {
    … // 기존 코드
  }
}

피자 가게 프로그램의 클래스 다이어그램

Simple Factory

: Simple Factory가 어느 객체를 생성할 지 판단하고, 사용자 측에 맞는 객체 반환

  • 일반적으로 if문에서 문자열에 따라 생성할 객체를 결정
  • 사실상 패턴이라고 볼 수는 없음

사례2 - 피자 프랜차이즈 사업

: 프랜차이즈 사업을 하면서 각 지점마다 해당 지역의 특성과 입맛을 반영하여 다른 스타일의 피자를 만들려고 함

  • 어떻게 지역별 차이점을 적용시킬까?

  • SimplePizzaFactory 대신 세 가지 다른 팩토리를 PizzaStore에서 사용하도록 하면 됨
NYPizzaFactory nyFactory = new NYPizzaFactory();
PizzaStore nyStore = new PizzaStore(nyFactory);
nyStore.order("Veggie");

ChicagoPizzaFactory = new ChicagoPizzaFactory();
PizzaStore chicagoStore = new PizzaStore(chicagoFactory);
chicagoStore.order("Veggie");
  • 문제점

    1. PizzaStore가 피자 생성 과정과 분리되어 있어, 유연성은 보장되나, 일괄적인 처리가 어려울 수 있음
    2. 피자 스토어마다 다른 처리 과정이 나타날 수 있음
  • 해결 방법

    1. 피자 가게와 피자 제작 과정 전체를 하나로 묶어주는 프레임워크를 만들기로 함

      • 유연성은 지켜야 함
      • createPizza() 메소드를 PizzaStore에 다시 넣고, 추상 메소드로 선언
    2. 분점마다 달라질 수 있는 것은 피자의 스타일. 주문 시스템은 모든 분점에서 똑같이 진행됨

    3. orderPizza()에서는 어떤 피자가 만들어지는지 알 수 없음

    4. 코드

      public abstract class PizzaStore {
        void prepareToBoxing(Pizza pizza) {
          pizza.prepare();
          pizza.bake();
          pizza.cut();
          pizza.box();
        }
      
        public Pizza orderPizza(String type) {
          Pizza pizza = createPizza(type);
          prepareToBoxing(pizza);
          return pizza;
        }
      
        // 팩토리 메소드
        abstract Pizza createPizza(String type);
      }
      public class NYPizzaStore extends PizzaStore {
        Pizza createPizza(String type) {
          if type.equals("cheese")) {
            pizza = new NYStyleCheesePizza();
          } else if (type.equals("pepperoni")) {
            pizza = new NYStylePepperoniPizza();
          } else if (type.equals("clam")) {
            pizza = new NYStyleClamPizza();
          } else if (type.equals("veggie")) {
            pizza = new NYStyleVeggiePizza();
          }
        }
      }
      public class ChicagoPizzaStore extends PizzaStore {
        Pizza createPizza(String type) {
          if type.equals("cheese")) {
            pizza = new ChicagoStyleCheesePizza();
          } else if (type.equals("pepperoni")) {
            pizza = new ChicagoStylePepperoniPizza();
          } else if (type.equals("clam")) {
            pizza = new ChicagoStyleClamPizza();
          } else if (type.equals("veggie")) {
            pizza = new ChicagoStyleVeggiePizza();
          }
        }
      }
      

클래스 다이어그램

팩토리 메소드

  • 팩토리 메소드는 객체 생성을 처리함
    : 팩토리 메소드 이용 시 객체를 생성하는 작업을 서브 클래스에 캡슐화 시킬 수 있음
  • 슈퍼클래스에 있는 클라이언트 코드와 서브 클래스에 있는 객체 생성 코드를 분리시킬 수 있음
    : abstract Product factoryMethod(String type)
  • 팩토리 메소드는 특정 제품(객체)을 반환
    : 해당 객체는 수퍼클래스에서 정의한 메소드 내에서 사용
  • 팩토리 메소드는 클라이언트에서 실제로 생성되는 실제 객체가 무엇인지 알 수 없게 만드는 역할

피자 클래스 구현

public abstract class Pizza {
  String name;
  String dough;
  String sauce;
  ArrayList toppings = new ArrayList();

  void prepare() {
    System.out.println("Preparing " + name);
    System.out.println("Tossing dough…");
    System.out.println("Adding sauce…");
    System.out.println("Adding toppings: ");
    for (int i = 0; i < toppings.size(); i++) {
      System.out.println("  " + toppings.get(i));
    }
  }
  void bake() {
    System.out.println("Bake for 25 minutes at 350");
  }
  void cut() {
    System.out.println("Cutting the pizza into diagonal slices");
  }
  void box() {
    System.out.println("Place pizza in official PizzaStore box");
  }
  public String getName() {
    return name;
  }
}
public class NYStyleCheesePizza extends Pizza {
  public NYStyleCheesePizza() {
    name = "NY Style Sauce and Cheese Pizza";
    dough = "Thin Crust Dough";
    sauce = "Marinara Sauce";
    toppings.add("Grated Reggiano Cheese");
  }
}

public class ChicagoStyleCheesePizza extends Pizza {
  public ChicagoStyleCheesePizza () {
    name = "Chicago Style Deep Dish Cheese Pizza";
    dough = "Extra Thick Crust Dough";
    sauce = "Plum Tomato Sauce";
    toppings.add("Shredded Mozzarella Cheese");
  }
  void cut() {
    System.out.println("Cutting the pizza into square slices");
  }
}
public class PizzaTestDrive {
  public static void main(String[] args) {
    PizzaStore nyStore = new NYPizzaStore();
    PizzaStore chicagoStore = new ChicagoPizzaStore();
    Pizza pizza = nyStore.orderPizza("cheese");
    System.out.println("Ethan ordered a " 
                        + pizza.getName() + "\n");
    pizza = chicagoStore.orderPizza("cheese");
    System.out.println("Joel ordered a " 
                          + pizza.getName + "\n");
  }
}

병렬 클래스 계층 구조

클래스 다이어그램

사례 3 - 피자 원재료 품질 관리

  • 분점에서 좋은 재료를 사용하도록 관리할 수 있을까?
    1. 원재료를 생산하는 공장을 만들고 분점까지 재료를 제공
    2. 문제는 본점이 떨어져 있고, 지점마다 재료들이 같은 것들도 있지만 일부는 다름
  • 원재료를 생산할 팩토리 인터페이스 정의
public interface PizzaIngredientFactory {
  public Dough createDough();
  public Sauce createSauce();
  public Cheese createCheese();
  public Veggies[] createVeggies();
  public Pepperoni createPepperoni();
  public Clams createClam();
}

- 뉴욕 원재료 공장

public class NYPizzaIngredientFactory implements 
                           PizzaIngredientFactory {
  public Dough createDough() {
    return new ThinCrustDough();
  }
  public Sauce createSauce() {
    return new MarinaraSauce(); 
  }
  public Cheese createCheese() {
    return new ReggianoCheese(); 
  }
  public Veggies[] createVeggies() {
    Veggies veggies[] = { new Garlic(), new Onion(), 
                 new Mushroom(), new RedPepper() };
    return veggies;
  }
  public Pepperoni createPepperoni() {
    return new SlicedPepperoni();
  }
  public Clams createClam() {
    return new FreshClams(); 
  }
}
  • 새로운 피자 클래스
public abstract class Pizza {
  String name;
  Dough dough;
  Sauce sauce;
  Veggies veggies[];
  Cheese cheese;
  Pepperoni pepperoni;
  Clams clam;
  
  abstract void prepare();

  void bake() {
    System.out.println("Bake for 25 minutes at 350");
  }
  void cut() {
    System.out.println("Cutting the pizza into diagonal slices");
  } 
  void cut() {
    System.out.println("Cutting the pizza into diagonal slices");
  }
  void box() {
    System.out.println("Place pizza in official PizzaStore box");
  }
  void setName(String name) {
    this.name = name;
  }
  String getName() {
    return name; 
  }
  public String toString() {
    // 피자 이름 출력
  }
} 
  • 팩토리 메소드 패턴을 이용한 코드에서 NYChessePizza, ChicagoCheesePizza 클래스는 지역별로 다른 재료를 사용한다는 것만 빼면 같음
    • 재료만 다를 뿐 결국 준비 단계는 같음
    • 따라서 피자마다 지역별로 따로 만들 필요가 없음
    • 지역별로 다른 재료들은 원재료 공장에서 만들어줌
  • 치즈 피자 클래스
public class CheesePizza extends Pizza {
  PizzaIngredientFactory ingredientFactory;
  public CheesePizza(PizzaIngredientFactory 
                             ingredientFactory) {
    this.ingredientFactory = ingredientFactory;
  }
  void prepare() {
    System.out.println("Preparing " + name);
    dough = ingredientFactory.createDough();
    sauce = ingredientFactory.createSauce();
    cheese = ingredientFactory.createCheese();
  }
}
  • 뉴욕 피자 스토어 클래스
public class NYPizzaStore extends PizzaStore {
  protected Pizza createPizza(String item) {
    Pizza pizza = null;
    PizzaIngredientFactory ingredientFactory = 
            new NYPizzaIngredientFactory();
    if (item.equals("cheese")) {
      pizza = new CheesePizza(ingredientFactory);
      pizza.setName("New York Style Cheese Pizza");
    } else if (item.equals("veggie")) {
      pizza = new VeggiePizza(ingredientFactory);
      pizza.setName("New York Style Veggie Pizza");
    } else if (item.equals("clam")) {
    …
    }
    return pizza;
  }
}

Abstract Factory Pattern

: 추상 팩토리를 통해서 제품군을 생성하기 위한 인터페이스를 제공할 수 있음

  • 인터페이스를 이용하는 코드를 만들면 코드를 제품을 생산하는 실제 팩토리와 분리시킬 수 있음
  • 이렇게 함으로써 지역, 운영체제, 룩앤필 등 서로 다른 상황별로 적당한 제품을 생산할 수 있는 다양한 팩토리 구현 가능
  • 코드가 실제 제품과 분리되어 있으므로 다른 공장을 사용하면 다른 결과를 얻을 수 있음

클래스 다이어그램

디자인 패턴 요소

요소

설명

이름

팩토리 메소드(Factory Method), 추상 팩토리(Abstract Factory)

문제

실제로 구현되는 클래스의 객체를 생성할 때 객체의 종류가 달라지면 클라이언트 코드를 수정해야 하는 것이 너무 많음

해결방안

생성을 분리해서 캡슐화 시킴

결과

사용할 객체가 많거나 객체를 생성하는 방법이 변경되어도 연쇄적인 수정이 적어짐

반응형

'Computer Science > 디자인패턴' 카테고리의 다른 글

[Head First Design Patterns] 06 커맨드 패턴  (0) 2020.11.03
[Head First Design Patterns] 05 싱글톤 패턴  (0) 2020.10.20
[Head First Design Patterns] 01 디자인 패턴 소개  (0) 2020.10.18
Introduction Advanced OOP  (0) 2020.10.18
[Head First Design Patterns] 03 데코레이터 패턴  (0) 2020.10.07
    'Computer Science/디자인패턴' 카테고리의 다른 글
    • [Head First Design Patterns] 06 커맨드 패턴
    • [Head First Design Patterns] 05 싱글톤 패턴
    • [Head First Design Patterns] 01 디자인 패턴 소개
    • Introduction Advanced OOP
    계속지나가기
    계속지나가기
    NLP Engineer

    티스토리툴바