디자인 패턴 - 생성 패턴

@Soo · April 10, 2022 · 8 min read

팩토리 패턴

구상 클래스의 인스턴스 생성 부분을 캡슐화하기

Pizza orderPizza(String type){
	Pizza pizz;

	if(type.equlas("cheese"){
		pizza = new CheesePizza();
	} else if(type.equlas("greek"){
		pizza = new GreekPizza();
  } else 	if(type.equlas("veggie"){
		pizza = new VeggiePizza();
	}

	return pizza;
}
  • 피자 인스턴스를 만드는 구상 클래스를 선택하는 부분이 상황이 변하면 코드를 변경해야 한다.

객체 생성 부분 캡슐화 하기

public class SimplePizzaFactory {

	public Pizza createPizza(String type){
		if(type.equlas("cheese"){
			pizza = new CheesePizza();
		} else if(type.equlas("greek"){
			pizza = new GreekPizza();
	  } else 	if(type.equlas("veggie"){
			pizza = new VeggiePizza();
		}
	
		return pizza;
	}
}
  • 피자 인스턴스 생성 부분을 SimplePizzaFactory로 넘겼을 때의 장점은?

    • 팩토리를 사용하지 않으면 피자 인스턴스를 생성하는 부분이 많아 생성하는 작업이 변경되었을 때 생성하는 모든 부분을 고쳐줘야 한다.

클라이언트 코드 수정

public class PizzaStore {

	SimplePizzaFactory factory;

	// 생성자
  ...

	public Pizza orderPizza(String type){
		Pizza pizza;

		pizza = factory.createPizza(type);

		pizza.prepare();
		pizza.bake();
		pizza.cut();
		pizza.box();

		return pizza;
	}
}

팩토리 메서드 패턴

  • 요구사항이 바뀌어 피자를 각 지점마다 그 지역의 특성과 입맛을 반영한 다양한 스타일의 피자를 만들어야 된다면?
  • PizzaStore 클래스를 추상 클래스로 변경
public abstract class PizzaStore {

	public Pizza orderPizza(String type){
		Pizza pizza;

		pizza = createPizza(type);

		pizza.prepare();
		pizza.bake();
		pizza.cut();
		pizza.box();

		return pizza;
	}

abstract Pizza createPizza(String type);

}
  • 각 지점에 맞는 서브클래스를 생성(NYPizzaStore, ChicagoPizzastore, CaliforniaPizzaStore)
public class NYPizzaSotre extends PizzaStore {
	
	public Pizza createPizza(String type){
		if(type.equlas("cheese"){
			pizza = new NYStyleCheesePizza();
		} else if(type.equlas("greek"){
			pizza = new NYStyleGreekPizza();
	  } else 	if(type.equlas("veggie"){
			pizza = new NYStyleVeggiePizza();
		}
	
		return pizza;
	}
}

팩토리 메서드 패턴의 정의

  • 팩토리 메서드 패턴에서는 객체를 생성할 때 필요한 인터페이스를 만든다.
  • 어떤 클래스의 인서턴스를 만들지는 서브클래스에서 결정한다.
  • 팩토리 메서드 패턴을 사용하면 클래스 인스턴스 만드는 일을 서브 클래스에게 맡기게 된다.

Untitled

  • Product - 생성될 객체의 인터페이스
  • ConcreteProduct - Product 인터페이스를 구현한 구상 클래스.
  • Creator - 객체를 생성하는 팩토리 메소드를 가진 인터페이스
  • ConcreteCreator - Creator 인터페이스를 구현한 구상 클래스.

의존성 역전 원칙

  • 고수준 구성 요소가 저수준 구성 요소에 의존하면 안되며, 항상 추상화에 의존하게 만들어야 한다.

    • 변수에 구상 클래스의 레퍼런스를 저장하지 않는다.
    • new 연산자는 구상 클래스를 사용하게 되므로 팩토리를 써서 방지
    • 구상 클래스에서 유도된 클래스를 만들지 맙시다.
    • 인터페이스나 추상 클래스처럼 추상화된 것으로부터 클래스를 만들어야 된다.
    • 베이스 클래스에 이미 구현되어 있는 메소드를 오버라이드하지 않는다.
    • 이미 구현되어 있는 메서드를 오버라이드 한다면 베이스 클래스가 제대로 추상화되지 않았다.
    • 베이스 클래스에서 메서드를 정의할 때는 모든 서브클래스에서 공유할 수 있는 것만 정의해야한다.

추상 팩토리 패턴의 정의

  • 추상 팩토리 패턴은 구상 클래스에 의존하지 않고도 서로 연관되거나 의존적인 객체로 이루어진 제품군을 생산하는 인터페이스를 제공합니다. 구상 클래스는 서브 클래스에서 만듭니다.

Untitled
Untitled

팩토리 메서드 패턴과 추상 팩토리 패턴의 차이

  • 팩터리 메서드 패턴은 하나의 메서드가 여러 객체를 생성한다
  • 추상 팩토리 패턴은 팩터리 자체가 인터페이고 서브 클래스가 여러 객체를 생성한다.
  • 팩터리 메서드 패턴은 클라이언트와 구상 클래스를 분리시켜야할 때 사용한다.
  • 추상 팩터리 패턴은 하나의 제품군을 생성할 때 사용한다.

    • 뉴욕지점의 제품군(Dough, Sauce, Cheese, Vaggies 등)
    • 제품군을 합치는 부분은 클라이언트의 책임

싱글톤 패턴

  • 싱글턴 패턴은 클래스 인스턴스를 하나만 만들고, 그 인스턴스로의 전역 접근을 제공

고전적인 구현 방식

public class Singleton {
	private static Singleton instance;

	private Singleton() {}

	public static Singleton getInstance() {
		if(instance == null)
			instance = new Singleton();

		return instance;
	}
}
  • 멀티스레드 환경에서 두 스레드가 동시에 getInstance를 호출할 때 인스턴스가 2개 만들어질 가능성이 있다.

동기화를 이용한 구현

public class Singleton {
	private static Singleton instance;

	private Singleton() {}

	public static synchronized Singleton getInstance() {
		if(instance == null)
			instance = new Singleton();

		return instance;
	}
}
  • synchronized 키워드를 추가하면 한 스레드가 메소드 사용을 끝내기 전까지 다른 스레드는 기다리게 된다.
  • 하지만 synchronized 키워드를 사용하면 성능이 저하되고, 객체가 생성할 시점에만 동기화가 필요한데 자원을 낭비하게 된다.

static initializer 를 이용한 구현

public class Singleton {
	private static Singleton instance = new Singleton;

	private Singleton() {}

	public static Singleton getInstance() {
		return instance;
	}
}
  • 클래스가 로딩될 때 JVM에서 Singleton이 하나뿐인 인서턴스를 생성해준다.
  • 하지만 생성시점을 JVM에서 관리하기 때문에 lazy loading 처럼 생성시점을 컨트롤할 수가 없다.

DCL을 이용한 구현

  • DCL(Double-Checked Locking)을 사용하면 인스턴스가 생성되어 있는지 확인 한 다음 생성되어 있지 않을 때만 동기화할 수 있다.
public class Singleton {
	private volaile static Singleton instance;

	private Singleton() {}

	public static Singleton getInstance() {
		if(instance == null){
				synchronized (Singleton.class) {
					if (instance == null) {
						instance = new Singleton();
					}
				}
		}
		return instance;
	}
}

enum 을 사용한 구현

public enum Singleton {
	UNIQUE_INSTACE;
}

public class SinlegonClient {
		public static void main(String[] args){
				Singleton singleton = Singleton.UNIQUE.INSTANCE;
		}
}
@Soo
RDBMS, NoSQL, 분산 처리에 관심이 많은 백엔드 엔지니어입니다.