팩토리 패턴이란 객체를 사용하는 코드에서 객체 생성 부분을 떼어내 추상화한 패턴이자 상속 관계에 있는 두 클래스에서 상위 클래스가 중요한 뼈대를 결정하고, 하위 클래스에서 객체 생성에 대한 구체적인 내용을 결정하는 패턴이다.
OOP의 기본 원칙중 SOLID 에는 OCP(Open Closed Principle) 이라고 하여 프로그램이 확장에는 열려있고, 수정에는 닫혀있어야 한다는 원칙이 있다.
팩토리 패턴은 기능의 확장, 변화에 따라 쉽게 변화할 수 있는 구현체(하위 클래스)의 생성은 구현체가 담당하고, 그 구현체를 주입하는 역할을 팩토리 객체게 위임함으로써 기능의 변화, 확장에 따라 수정해야하는 코드량을 최소화할 수 있다는 장점이 있다.
(1) 팩토리 패턴을 이용하면 상위 클래스와 하위 클래스가 분리되므로 결합도가 낮아진다.
(2) 상위 클래스에서는 하위 클래스에 대한 정보를 전혀 알 필요가 없으므로 유연성을 갖게된다.
(3) 객체 생성 로직이 따로 떼어져 있기 때문에 코드를 리팩터링하더라도 한 곳만 신경쓰면 된다.
(1) 팩토리 패턴을 구현하기 위해서는 많은 서브 클래스가 만들어지므로 코드가 복잡해질 수 있다.
Product 라는 상위 클래스가 구현체의 뼈대를 잡고, 구현체(하위 클래스)를 각각 정의한다. 그리고 팩토리 객체는 상위 클래스를 반환하도록 함으로써, Product에 의존하는 클래스에 손쉽게 의존성을 주입해줄 수 있다.
public class Main {
public static void main(String[] args) {
System.out.println(ProductFactory.getProduct(ProductName.RANDOM, 1000));
System.out.println(ProductFactory.getProduct(ProductName.MILK, 2000));
System.out.println(ProductFactory.getProduct(ProductName.APPLE, 4000));
}
}
enum ProductName {
MILK, APPLE, RANDOM
}
abstract class Product {
public abstract int getPrice();
@Override
public String toString() {
return "Hi this product is " + this.getPrice();
}
}
class ProductFactory {
public static Product getProduct(ProductName type, int price) {
String hello;
switch (type) {
case MILK :
return new MILK(price);
case APPLE:
return new APPLE(price);
default:
return new DefaultProduct(price);
}
}
}
class DefaultProduct extends Product {
private final int price;
public DefaultProduct(int price) {
this.price = price;
}
@Override
public int getPrice() {
return this.price;
}
}
class MILK extends Product {
private final int price;
public MILK(int price) {
this.price = price;
}
@Override
public int getPrice() {
return this.price;
}
}
class APPLE extends Product {
private final int price;
public APPLE(int price) {
this.price = price;
}
@Override
public int getPrice() {
return this.price;
}
}
kotlin 을 사용하면 정말 코드량이 적어진다.
interface Currency {
val code: String
}
class Euro(override val code: String = "EUR") : Currency
class UnitedStatesDollar(override val code: String = "USD") : Currency
enum class Country {
UnitedStates, Spain, UK, Greece
}
class CurrencyFactory {
fun currencyForCountry(country: Country): Currency? {
return when (country) {
Country.Spain, Country.Greece -> Euro()
Country.UnitedStates -> UnitedStatesDollar()
else -> null
}
}
}
class Product1 {
constructor() {
this.name = "Product1";
};
}
class Product2 {
constructor() {
this.name = "Product2";
};
}
class Factory1 {
static createProduct() {
return new Product1();
};
}
class Factory2 {
static createProduct() {
return new Product2();
};
}
const factories = { Factory1, Factory2 }
class Factory {
static createProduct(type) {
const factory = factories[type];
return factory.createProduct();
};
}
const product = Factory.createProduct("Factory1")
console.log(product.name)