๐ก ํฉํ ๋ฆฌ ๋ฉ์๋ ํจํด (Factory Method Pattern)
๊ฐ์ฒด๋ฅผ ์์ฑํ๊ธฐ์ํ ์ธํฐํ์ด์ค๋ฅผ ์ ์ํ๋๋ฐ, ์ด๋ค ํด๋์ค์ ์ธ์คํด์ค๋ฅผ ๋ง๋ค์ด์ง๋ ์๋ธํด๋์ค์์ ์ ํ๊ฒ ๋ง๋ ๋ค. ์ฆ, ํฉํ ๋ฆฌ ๋ฉ์๋ ํจํด์ ์ด์ฉํ๋ฉด ํด๋์ค์ ์ธ์คํด์ค๋ฅผ ๋ง๋๋ ์ผ์ ์๋ธํด๋์ค์๊ฒ ๋งก๊ธฐ๋ ๊ฒ
๐ก ์ถ์ ํฉํ ๋ฆฌ ํจํด (Abstract Factory Pattern)
์ธํฐํ์ด์ค๋ฅผ ์ด์ฉํ์ฌ ์๋ก ์ฐ๊ด๋, ๋๋ ์์กดํ๋ ๊ฐ์ฒด๋ฅผ ๊ตฌ์ ํด๋์ค๋ฅผ ์ง์ ํ์ง ์๊ณ ๋ ์์ฑ
1. ๊ฐ๋จํ ํฉํ ๋ฆฌ (Simple Factory)
1) ๋ฌธ์ ์ธ์
๋ค์๊ณผ ๊ฐ์ด new ํค์๋๋ฅผ ์ด์ฉํ๋ฉด ์ธํฐํ์ด์ค๊ฐ ์๋ ํน์ ๊ตฌํ์ ์ฌ์ฉํ๊ฒ ๋๋ค.
Pizza pizza = new CheesePizza();
// Pizza pizza : ์ธํฐํ์ด์ค๋ฅผ ์จ์ ์ ์ฐํ๊ฒ ๋ง๋๋ ค๊ณ ํ๋ค.
// new CheesePizza(); : ๊ทธ๋ผ์๋ ๋ถ๊ตฌํ๊ณ ๊ตฌ์ํด๋์ค์ ์ธ์คํด์ค๋ฅผ ๋ง๋ค์ด์ผ ํ๋ค.
์ฌ๋ฌ๊ฐ์ ๋ค์ํ ๊ตฌ์ ํด๋์ค๊ฐ ์กด์ฌํ๋ฉด ๋ค์๊ณผ ๊ฐ์ ์ฝ๋๋ฅผ ๋ง๋ค์ด์ผ ํ๋ ๊ฒฝ์ฐ๋ ์๊ธด๋ค.
public Pizza orderPizza (String type) { // ์ด๋ค ํผ์๋ฅผ ์กฐ๋ฆฌํ ์ง type ์ ์ด์ฉํ์ฌ
Pizza pizza;
if(type.equals("cheese")) { // ๊ฐ ํผ์์ ํด๋นํ๋ ๊ฐ์ฒด ์์ฑ
pizza = new CheesePizza();
} else if (type.equals("hawaiian")) {
pizza = new HwaiianPizza();
} else if (type.equals("pepperoni")) {
pizza = new PepperoniPizza();
}
// ํผ์ ์กฐ๋ฆฌ ๋ฉ์๋๋ค.. ์๋ต
return pizza;
}
์ด ๊ฒฝ์ฐ ๋ณ๊ฒฝ ๋๋ ํ์ฅ์ ํ ๋ ์ค๋ฅ๊ฐ ๋ฐ์ํ ๊ฐ๋ฅ์ฑ์ด ์ปค์ง๋ ๋ฌธ์ ๊ฐ ์๋ค. ์ฝ๋ ์์ ์ด ํ์ฐ์ฝ ๋ฐ์ํ๊ธฐ ๋๋ฌธ์ด๋ค.
์ฆ, "๋ณํ" ๋๋ ๊ฒ ๋๋ฌธ์ new ์ ์ฌ์ฉ์ ์ฃผ์ํด์ผ ํ ํ์๊ฐ ์๋ค.
์ด ๋๋ฌธ์ ์ธํฐํ์ด์ค์ ๋ง์ถฐ์ ์ฝ๋ฉํ๋ผ๋ ๋ง์ ํ๊ณ ๋ ํ๋ค. ์ธํฐํ์ด์ค๋ฅผ ๋ฐํ์ผ๋ก ์ฝ๋๋ฅผ ๋ง๋ค๋ฉด, ๋คํ์ฑ ๋๋ฌธ์ ์ด๋ค ํด๋์ค๋ ํน์ ์ธํฐํ์ด์ค๋ง ๊ตฌํํ๋ฉด ์ฌ์ฉํ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค. ๋ฐ๋๋ก, ๊ตฌ์ ํด๋์ค๋ฅผ ๋ง์ด ์ฌ์ฉํ๋ฉด ์๋ก์ด ๊ตฌ์ ํด๋์ค๊ฐ ์ถ๊ฐ๋ ๋๋ง๋ค ์ฝ๋๋ฅผ ๊ณ ์ณ์ผ ํ๊ธฐ ๋๋ฌธ์ ์ด ๊ณผ์ ์์ ๋ง์ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์๋ค.
๋ค์๋งํ๋ฉด ์ธํฐํ์ด์ค์ ๋ง์ถฐ ์ฝ๋ฉํ๋ฉด ๋ณํ์ ๋ํด ๋ซํ ์๋ ์ฝ๋๊ฐ ๋๋ค. OCP ์์น์ ์ค์ํ๋ค๋ ์๋ฏธ์ด๋ค.
๐ ๋์์ธ ์์น
OCP (Open - Closed Principle) ์์น , ๊ฐ๋ฐฉ ํ์ ์์น
ํ์ฅ์ ๋ํด ์ด๋ ค ์์ด์ผ ํ๊ณ , ์์ ์ ๋ํด์๋ ๋ซํ ์์ด์ผ ํ๋ค๋ ์์น
ํ์ฅ์ ๋ํด์๋ Open , ๋ณ๊ฒฝ์ ๋ํด์๋ Closed ํ๋ผ๋ ์์น
์์ pizza ํด๋์ค ์์ฑ๊ณผ์ ์ ํตํด ๊ตฌ์ ํด๋์ค๋ฅผ ๋ฐํ์ผ๋ก ์ฝ๋ฉ์ ํ๋ฉด ๋์ค์ ์ฝ๋๋ฅผ ์์ ํด์ผํ ๊ฐ๋ฅ์ฑ์ด ๋์์ง๊ณ , ์ ์ฐ์ฑ์ด ๋จ์ด์ง๋ค๋ ๊ฒ์ ํ์ธํ๋ค. ๊ทธ๋ ๋ค๋ฉด ํผํ ์ ์๋ ๋ฐฉ๋ฒ์ ์์๊น?
" ๋ฐ๋ ์ ์๋ ๋ถ๋ถ์ ์ฐพ์๋ด์ ๋ฐ๋์ง ์๋ ๋ถ๋ถํ๊ณ ๋ถ๋ฆฌ์์ผ์ผ ํ๋ค "
2) ๋ฌธ์ ๋ฅผ ํด๊ฒฐํด๋ณด๊ธฐ
๊ฐ์ฒด ์ธ์คํด์ค๋ฅผ ์์ฑํ๋ (new ํค์๋๋ฅผ ์ฌ์ฉํ๋) ๋ถ๋ถ์ ๋ณ๋์ ์ธํฐํ์ด์ค๋ก ๋ถ๋ฆฌํ์.
(1) PizzaStore
public class PizzaStore {
SimplePizzaFactory factory;
public PizzaStore(SimplePizzaFactory factory) {
this.factory = factory;
}
public Pizza orderPizza(String type) {
Pizza pizza;
// ์ด๋ค ํผ์ ๋ง๋ค์ง ๊ณต์ฅ์์ (Factory) ์์ฑ
pizza = factory.createPizza(type);
// ํผ์ ์กฐ๋ฆฌ
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}โ
(2) ํฉํ ๋ฆฌ (Factory) ์์ฑ : SimplePizzaFactory
public class SimplePizzaFactory {
public Pizza createPizza(String type) {
Pizza pizza = null;
// ๊ฐ์ฒด์์ฑ์ ์บก์ํํ๋ค. ๊ฒฐํฉ๋๋ฅผ ๋ฎ์ถ๋ ๋ฐฉ์์ด๋ค.
if (type.equals(PizzaType.CHEESE)) {
pizza = new CheesePizza();
} else if (type.equals(PizzaType.HAWAIIAN)) {
pizza = new HawaiianPizza();
} else if (type.equals(PizzaType.PEPPERONI)) {
pizza = new PepperoniPizza();
} else if (type.equals(PizzaType.VEGGIE)) {
pizza = new VeggiePizza();
}
return pizza;
}
}
public class PizzaType {
public static final String CHEESE = "cheese";
public static final String HAWAIIAN = "hawaiian";
public static final String PEPPERONI = "pepperoni";
public static final String VEGGIE = "veggie";
}
(3) Pizza ์ถ์ ํด๋์ค
public abstract class Pizza {
String name; // ํผ์์ด๋ฆ
String dough; // ๋์ฐ
String sauce; // ์์ค
List<String> toppings = new ArrayList<>(); // ํ ํ
public String getName() {
return name;
}
public void prepare() {
System.out.println("preparing... " + name);
}
public void bake() {
System.out.println("baking... " + name);
}
public void cut() {
System.out.println("cutting... " + name);
}
public void box() {
System.out.println("boxing... " + name);
}
@Override
public String toString() {
// ํผ์ ์ ๋ณด ํ์
StringBuilder sb = new StringBuilder();
sb.append("===== " + name + " ํผ์ ์ ๋ณด =====\n");
sb.append("dough = " + dough + "\n");
sb.append("sauce = " + sauce + "\n");
for (String topping : toppings) {
sb.append("toppings = " + topping + "\n");
}
return sb.toString();
}
}
(4) Pizza ์ถ์ ํด๋์ค๋ฅผ ๊ตฌํ
// ํํ๋ก๋ ํผ์
public class PepperoniPizza extends Pizza {
public PepperoniPizza() {
name = "Pepperoni Pizza";
dough = "Crust";
sauce = "Marinara sauce";
toppings.add("Sliced Pepperoni");
toppings.add("Sliced Onion");
toppings.add("Grated parmesan cheese");
}
}
// ๊ทธ์ธ ํผ์๋ค..
(5) ์คํ
@Test
void simplePizza() {
SimplePizzaFactory factory = new SimplePizzaFactory();
PizzaStore pizzaStore = new PizzaStore(factory);
pizza = pizzaStore.orderPizza(PizzaType.PEPPERONI);
System.out.println(pizza.getName() + " ๋ฅผ ์ฃผ๋ฌธํฉ๋๋ค.");
System.out.println(pizza.toString());
}
(6) ๊ฒฐ๊ณผ ํ์ธ
preparing... Pepperoni Pizza
baking... Pepperoni Pizza
cutting... Pepperoni Pizza
boxing... Pepperoni Pizza
Pepperoni Pizza ๋ฅผ ์ฃผ๋ฌธํฉ๋๋ค.
===== Pepperoni Pizza ํผ์ ์ ๋ณด =====
dough = Crust
sauce = Marinara sauce
toppings = Sliced Pepperoni
toppings = Sliced Onion
toppings = Grated parmesan cheese
๊ฐ๋จํ ํฉํ ๋ฆฌ (Simple Factory) ๋ ์ด๋ ๊ฒ ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ ์์ ์ ๋ถ๋ฆฌํด์ ์ด์ฉํ๋ ๊ฒ์ผ๋ก ๋์์ธ ํจํด์ด๋ผ๊ณ ํ ์๋ ์๋ค. ํ๋ก๊ทธ๋๋ฐ์ ํ๋๋ฐ ์์ด์ ์์ฃผ ์ฐ์ด๋ ๊ด์ฉ๊ตฌ์ ๊ฐ๊น๋ค๊ณ ํ ์ ์๋ค.
3) ๋ฌธ์ ๊ฐ ์๊ฒผ๋ค.
์ด ํผ์๊ฐ๊ฒ๊ฐ ์ธ๊ธฐ๊ฐ ์๊ฒจ ํ๋์ฐจ์ด์ฆํ ๋์ด ๋ค๋ฅธ ์ง์ญ์๋ ์ง์ ์ ๋ด๊ฒ๋๊ณ , ๊ฐ ์ง์ญ๋ณ๋ก ๊ณ ์ ์คํ์ผ์ ํผ์๋ฅผ ๋ง๋ค์ด ํ๊ฒ ๋์๋ค. ๋ชจ๋ ๋ถ์ ์์ PizzaStore ์ฝ๋๋ฅผ ์ฌ์ฉํ๋ค.
๊ฐ๋จํ ํฉํ ๋ฆฌ (Simple Factory) ๋ฅผ ์ด์ฉํด์ ๋ง๋ค์ด๋ณด์.
// SimplePizzaFactory๋ฅผ ๋นผ๊ณ ๊ฐ๊ฐ์ ์ง์ ๋ณ ํฉํ ๋ฆฌ๋ฅผ ๋ง๋ค์๋ค.
PizzaStore seoulStore = new PizzaStore(new SeoulPizzaFactory());
seoulStore.orderPizza("cheese");
PizzaStore busanStore = new PizzaStore(new BusanPizzaFactory());
busanStore.orderPizza("cheese");
PizzaStore suwonStore = new PizzaStore(new SuwonPizzaFactory());
suwonStore.orderPizza("cheese");
์์ ๊ฐ์ ๊ตฌํ์ผ๋ก ๊ฐ ํฉํ ๋ฆฌ๋ฅผ ๊ฐ์ง ๋ถ์ ๋ค์ด ๊ฐ์์ ๋ฐฉ์์ผ๋ก ํผ์๋ฅผ ๋ง๋ค์ด ํ ์ ์๊ฒ ๋์๋ค. ์๋ฅผ๋ค์ด ํผ์๋ฅผ ๊ตฝ๋ ๋ฐฉ์์ด๋ ์ปทํ ๋ฐฉ์์ ์ฐจ์ด ๋ฑ์ด๋ค. ๊ทธ๋ฌ๋ ์ด๋ฐ ๋ฐฉ์์ผ๋ก๋ ์ง์ ๋ง๋ค ํฉํ ๋ฆฌ๋ฅผ ๋ง๋ค์ด์ผ ํ๋ค. ์ง์ ์ด ๋์ด๋ ์๋ก ์ฝ๋๊ฐ ๋ฐฉ๋ํด์ ธ๋ฒ๋ฆฌ๋ ๋น์ฐํ ์ข์ ๋ฐฉ๋ฒ์ด ๋ ์ ์๋ค.
ํผ์ ์ ์ํ๋์ ๋ชจ๋ PizzaStore ํด๋์ค์ ์์ํ๋ฉด์๋ ๊ณ ์ ์ ์คํ์ผ์ ์ด๋ฆด ์ ์๋๋ก ํผ์๊ฐ๊ฒ (PizzaStore) ์ ์ ์๊ณผ์ ( prepare() → bake() → cut() → box() ) ์ ํ๋๋ก ๋ฌถ์ด์ฃผ๋ ํ๋ ์์ํฌ๋ฅผ ๋ง๋ค ํ์๊ฐ ์๊ฒผ๋ค.
2. ํฉํ ๋ฆฌ ๋ฉ์๋ ํจํด
(1) ํผ์๊ฐ๊ฒ PizzaStore ์ถ์ ํด๋์ค : ํผ์ ์ ์ํ๋์ ๋ชจ๋ ์์ํ๋ฉด์๋ ๊ณ ์ ์คํ์ผ์ ํฉํ ๋ฆฌ์์ ํด๊ฒฐํ๋๋กํ๋ค.
public abstract class PizzaStore {
// Pizza ์ธ์คํด์ค๋ฅผ ๋ง๋๋ ์ผ์ ํฉํ ๋ฆฌ ์ญํ ์ ํ๋ ์ธ์คํด์ค์์
public abstract Pizza createPizza(String item);
public Pizza orderPizza(String type) { // ํฉํ ๋ฆฌ ๋ฉ์๋์์ํด ์์ฐ๋ ์ ํ๊ฐ์ง๊ณ ์์
์ฒ๋ฆฌ
Pizza pizza = createPizza(type); // ์ค์ ํฉํ ๋ฆฌ ๋ฉ์๋ ๊ตฌํ์ ์ ํ ๋ง๋๋ ์๋ธํด๋์ค์์
System.out.println("==== " + pizza.getName() + " ์ ์ ====");
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
(2) PizzaStore ๋ฅผ ์์ํด์ ๊ฐ ์ง์ ํผ์๊ฐ๊ฒ ์๋ธ ํด๋์ค๋ฅผ ๋ง๋ค์ด์ฃผ๊ณ ์ง์ ์์ ์คํ์ผ์ ๊ฒฐ์ ํ๋๋ก ํ๋ค.
// ์์ธ ํผ์ ์ง์
public class SeoulPizzaStore extends PizzaStore {
@Override
public Pizza createPizza(String type) {
if (type.equals(PizzaType.CHEESE)) {
return new SeoulStyleCheesePizza();
} else if (type.equals(PizzaType.HAWAIIAN)) {
return new SeoulStyleHawaiianPizza();
} else if (type.equals(PizzaType.PEPPERONI)) {
return new SeoulStylePepperoniPizza();
} else if (type.equals(PizzaType.VEGGIE)) {
return new SeoulStyleVeggiePizza();
} else {
return null;
}
}
}
// ๋ถ์ฐ์ง์
public class BusanPizzaStore extends PizzaStore {
@Override
public Pizza createPizza(String type) {
if (type.equals(PizzaType.CHEESE)) {
return new BusanStyleCheesePizza();
} else if (type.equals(PizzaType.HAWAIIAN)) {
return new BusanStyleHawaiianPizza();
} else if (type.equals(PizzaType.PEPPERONI)) {
return new BusanStylePepperoniPizza();
} else if (type.equals(PizzaType.VEGGIE)) {
return new BusanStyleVeggiePizza();
} else {
return null;
}
}
}
๊ทธ๋ฆผ์ผ๋ก ํํํ๋ฉด ๋ค์๊ณผ ๊ฐ์ ๊ตฌ์กฐ๋ฅผ ๊ฐ๋๋ค.
(3) Pizza ํด๋์ค
public abstract class Pizza {
String name; // ํผ์์ด๋ฆ
String dough; // ๋์ฐ
String sauce; // ์์ค
List<String> toppings = new ArrayList<>(); // ํ ํ
public void prepare() {
System.out.println("preparing... " + name);
System.out.println("tossing dough...");
System.out.println("adding sauce...");
System.out.println("adding toppings...");
for (String topping : toppings) {
System.out.println(" " + topping);
}
}
public void bake() {
System.out.println("baking... 350๋์์ 25๋ถ์ ๋ ๊ตฌ์๋๋ค.");
}
public void cut() {
System.out.println("cutting... ํผ์๋ฅผ ์ปคํ
ํฉ๋๋ค.");
}
public void box() {
System.out.println("boxing... ๋ฐ์ค์ ๋ฃ์ต๋๋ค.");
}
public String getName() { return name; }
@Override
public String toString() { // ํผ์ ์ ๋ณด ํ์
StringBuilder sb = new StringBuilder();
sb.append("===== " + name + " =====\n");
sb.append("dough = " + dough + "\n");
sb.append("sauce = " + sauce + "\n");
for (String topping : toppings) {
sb.append("toppings = " + topping + "\n");
}
return sb.toString();
}
}
(4) Pizza ํด๋์ค๋ฅผ ์์๋ฐ์ ๊ฐ ์ง์ ๋ณ ๊ณ ์ ์คํ์ผ ํผ์ ๊ตฌํ
// ์์ธ ์คํ์ผ ํ์์ด์ ํผ์
public class SeoulStyleHawaiianPizza extends Pizza {
public SeoulStyleHawaiianPizza() {
name = "Seoul Style Hawaiian Pizza";
dough = "Thin Crust";
sauce = "Marinara Sauce";
toppings.add("Pineapple");
toppings.add("Pepperoni");
toppings.add("Italian Sausage");
}
}
// ๋ถ์ฐ ์คํ์ผ ํํ๋ก๋ ํผ์
public class BusanStylePepperoniPizza extends Pizza {
public BusanStylePepperoniPizza() {
name = "Busan Style Pepperoni Pizza";
dough = "Crust";
sauce = "Marinara sauce";
toppings.add("Sliced Double Pepperoni");
toppings.add("Grated parmesan cheese");
}
@Override
public void cut() { // ์ ์๊ณผ์ ๋ ๊ฐ ์คํ์ผ์ ๋ง๊ฒ ๊ตฌํ๊ฐ๋ฅ
System.out.println("cutting.. square cut!");
}
}
(5) ์คํ
@Test
void simpleExtend() {
// ๊ฐ์ฒด ์์ฑ์ ์บก์ํ
PizzaStore seoulStore = new SeoulPizzaStore();
PizzaStore busanStore = new BusanPizzaStore();
System.out.println("์์ธ์ง์ 1๋ฒ ์๋ ์ฃผ๋ฌธ");
Pizza fistPizza = seoulStore.orderPizza(PizzaType.HAWAIIAN);
System.out.println("๋ถ์ฐ์ง์ 2๋ฒ ์๋ ์ฃผ๋ฌธ");
Pizza secondPizza = busanStore.orderPizza(PizzaType.PEPPERONI);
}
(6) ์ถ๋ ฅ๊ฒฐ๊ณผ ํ์ธ
์์ธ์ง์ 1๋ฒ ์๋ ์ฃผ๋ฌธ
==== Seoul Style Hawaiian Pizza ์ ์ ====
preparing... Seoul Style Hawaiian Pizza
tossing dough...
adding sauce...
adding toppings...
Pineapple
Pepperoni
Italian Sausage
baking... 350๋์์ 25๋ถ์ ๋ ๊ตฌ์๋๋ค.
cutting... ํผ์๋ฅผ ์ปคํ
ํฉ๋๋ค.
boxing... ๋ฐ์ค์ ๋ฃ์ต๋๋ค.
๋ถ์ฐ์ง์ 2๋ฒ ์๋ ์ฃผ๋ฌธ
==== Busan Style Pepperoni Pizza ์ ์ ====
preparing... Busan Style Pepperoni Pizza
tossing dough...
adding sauce...
adding toppings...
Sliced Double Pepperoni
Grated parmesan cheese
baking... 350๋์์ 25๋ถ์ ๋ ๊ตฌ์๋๋ค.
cutting.. square cut!
boxing... ๋ฐ์ค์ ๋ฃ์ต๋๋ค.
๊ฐ ์ง์ ์คํ์ผ์ ๋ง๊ฒ ์ ์๋๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
โ ์์ฐ์ ํด๋์ค PizzaStore : ๊ฐ์ฒด๋ฅผ ์์ฐ
โก ์ ํ ํด๋์ค Pizza : ์ ํ์ ์์ฐ
์์ฐ์ ๋ด๋นํ๋ PizzaStore ์ถ์ ํด๋์ค์์ ๊ฐ์ฒด๋ฅผ ๋ง๋ค๊ธฐ์ํ ๋ฉ์๋ createPizza() ๋ฅผ ์ ๊ณตํด์ค๋ค. ์ฆ, ํฉํ ๋ฆฌ ๋ฉ์๋๋ฅผ ์ํ ์ธํฐํ์ด์ค๋ฅผ ์ ๊ณตํ๋ค.
PizzaStore ์ ๊ตฌํ๋์ด ์๋ ๋ค๋ฅธ ๋ฉ์๋ orderPizza() ์์๋ ํฉํ ๋ฆฌ ๋ฉ์๋์ ์ํด ์์ฐ๋ ์ ํ์ ๊ฐ์ง๊ณ ํ์ํ ์์ ์ ์ฒ๋ฆฌํ๋ค. ํ์ง๋ง ์ค์ ํฉํ ๋ฆฌ ๋ฉ์๋๋ฅผ ๊ตฌํํ๊ณ ์ ํ (๊ฐ์ฒด ์ธ์คํด์ค) ์ ๋ง๋ค์ด ๋ด๋ ์ผ์ ์๋ธํด๋์ค์์๋ง ํ ์ ์๋ค.
→ concrete class ์ ๋ํ ์์กด์ฑ์ ์ค์ด๋ ๊ฒ์ด ์ข๋ค.
โ ์์กด์ฑ ๋ค์ง๊ธฐ ์์น
๐ ๋์์ธ ์์น
์์กด์ฑ ๋ค์ง๊ธฐ ์์น (Dependency Inversion Principle)
์ถ์ํ๋ ๊ฒ์ ์์กดํ๋๋ก ๋ง๋ค์ด๋ผ. ๊ตฌ์ ํด๋์ค (concrete class) ์ ์์กดํ๋๋ก ๋ง๋ค์ง ์๋๋ก ํ๋ค.
- PizzaStore : ์ถ์ ํด๋์ค (abstract class)
Pizza ํด๋์ค์๋ง ์์กดํ๋ค. - Pizza : ์ถ์ ํด๋์ค (abstract class)
- XxxStyleXxxPizza : ๊ตฌ์ ํด๋์ค (Concrete class)
๊ฐ ๊ตฌ์ ํด๋์ค ์ญ์ Pizza ์ถ์ ํด๋์ค์ ์์กดํ๋ค.
Pizza ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ๊ณ ์๊ธฐ ๋๋ฌธ์ด๋ค.
์ '์์กด์ฑ ๋ค์ง๊ธฐ ์์น'์ธ๊ฐ?
ใ์ ์ฉ ์ ใ | ใ์ ์ฉ ํใ |
PizzaStore → SeoulStyleCheesePizza PizzaStore → BusanStylePepperoniPizza |
PizzaStore → Pizza Pizza ← SeoulStyleCheesePizza Pizza ← BusanStylePepperoniPizza |
์์ ๊ฐ์ด ์์กด๊ด๊ณ๊ฐ ๋ค์ง์ด์ง๊ธฐ ๋๋ฌธ์ ์์กด์ฑ ๋ค์ง๊ธฐ ์์น์ด๋ผ ํ๋ค.
ํฉํ ๋ฆฌ ๋ฉ์๋ ํจํด์ ์ ์ฉํ๊ณ ๋๋ฉด ๊ณ ์์ค ๊ตฌ์ฑ์์ (PizzaStore) ์ ์ ์์ค ๊ตฌ์ฑ์์(SeoulStyleCheesePizza, BusanStylePepperoniPizza, ...) ๋ค์ด ๋ชจ๋ ์ถ์ํด๋์ค Pizza ์ ์์กดํ๊ฒ ๋๋ค. (๊ณ ์์ค ๋ชจ๋๊ณผ ์ ์์ค ๋ชจ๋์ด ๋ชจ๋ ํ๋์ ์ถ์ ํด๋์ค์ ์์กดํ๋ค.)
์ฐธ๊ณ ๋ก ํฉํ ๋ฆฌ ๋ฉ์๋ ํจํด์ด ์์กด์ฑ ๋ค์ง๊ธฐ ์์น์ ์ค์ํ๊ธฐ ์ํด ์ฐ๋ ์ ์ผํ ๊ธฐ๋ฒ์ ์๋๋ค.
๐ ์์กด์ฑ ๋ค์ง๊ธฐ ์์น์ ์๋ฐฐ๋๋ ๊ฐ์ฒด์งํฅ ๋์์ธ์ ํผํ๋๋ฐ ๋์์ด ๋๋ ๊ฐ์ด๋
1. ์ด๋ค ๋ณ์์๋ ๊ตฌ์ ํด๋์ค์ ๋ํ ๋ ํผ๋ฐ์ค๋ฅผ ์ง์ ํ์ง ์๋๋ค.
new ๋ฅผ ์ฌ์ฉํ๋ฉด ๋ ํผ๋ฐ์ค๋ฅผ ์ฌ์ฉํ๊ฒ ๋๋ ๊ฒ์ด๋ค.
2. ๊ตฌ์ ํด๋์ค์์ ์ ๋๋ ํด๋์ค๋ฅผ ๋ง๋ค์ง ์๋๋ค.
๊ตฌ์ ํด๋์ค์์ ์ ๋๋ ํด๋์ค๋ฅผ ๋ง๋ค๋ฉด ํน์ ๊ตฌ์ ํด๋์ค์ ์์กดํ๊ฒ ๋๋ค.
์ถ์ํ ๋ ๊ฒ์ ์ฌ์ฉํ๋๋ก ํด์ผ ํ๋ค.
3. ๋ฒ ์ด์ค ํด๋์ค์ ์ด๋ฏธ ๊ตฌํ๋์ด ์๋ ๋ฉ์๋๋ฅผ ์ค๋ฒ๋ผ์ด๋ ํ์ง ์๋๋ค.
์ด๋ฏธ ๊ตฌํ๋ ๋ฉ์๋๋ฅผ ์ค๋ฒ๋ผ์ด๋ ํ๋ค๋ ๊ฒ์ ์ ์ด๋ถํฐ ๋ฒ ์ด์ค ํด๋์ค๊ฐ ์ ๋๋ก ์ถ์ํ๋์ง ์์ ๊ฒ์ด๋ค.
๋ฒ ์ด์ค ํด๋์ค์์ ๋ฉ์๋๋ฅผ ์ ์ํ ๋๋ ๋ชจ๋ ์๋ธ ํด๋์ค์์ ๊ณต์ ํ ์ ์๋ ๊ฒ๋ง ์ ์ํด์ผ ํ๋ค.
์ ๋ฆฌํ๋ฉด ๋ง๋ค๊ณ ์๋ ํด๋์ค๊ฐ ๋ฐ๋ ๊ฐ๋ฅ์ฑ์ด ์๋ค๋ฉด ํฉํ ๋ฆฌ ๋ฉ์๋ ํจํด ๊ฐ์ ๊ธฐ๋ฒ์ ์จ์ ๋ณ๊ฒฝ๋ ์ ์๋ ๋ถ๋ถ์ ์บก์ํ ํ์ฌ์ผ ํ๋ค.
3. ์ถ์ ํฉํ ๋ฆฌ ํจํด
๋์์ธ ์์น๋ ์ง์ผ๊ฐ๋ฉด์ ์ ์ฐํ ํ๋ ์์ํฌ๋ฅผ ๋ง๋ค์๋ค. ๊ฐ ์ง์ ๋ค์ด ์ ์ด์๋๋ ๊ฒ ๊ฐ์๋ค. ๊ทธ๋ฐ๋ฐ, ๋ช๋ช ์ง์ ๋ค์ด ์ฌ๋ฃ๋ฅผ ๋ ์ผ ๊ฒ์ผ๋ก ๋ฐ๊ฟ์ ์๊ฐ๋ฅผ ์ ๊ฐํด ๋ง์ง์ ๋จ๊ธฐ๊ณ ์์๋ค. ๊ทธ๋์ ์์ฌ๋ฃ์ ํ์ง๊น์ง ๊ด๋ฆฌํ๋ ๋ฐฉ๋ฒ์ด ํ์ํด์ก๋ค.
์์ฌ๋ฃ ๊ตฐ์ ๋ง๋ค์ด ํ์ ํด๋ณด์. ์ ํ์ ๋ค์ด๊ฐ๋ ์ฌ๋ฃ๊ตฐ (๋ฐ์ฃฝ, ์์ค, ์น์ฆ, ์ผ์ฑ, ๊ณ ๊ธฐ ๋ฑ) ์ ๊ฐ์ง๋ง, ์ง์ญ๋ง๋ค ์ฌ๋ฃ์ ๊ตฌ์ฒด์ ์ธ ํน์ง์ด ์กฐ๊ธ์ฉ ๋ฌ๋๋ค.
(1) ์์ฌ๋ฃ
๋ํ์ ์ผ๋ก Cheese, Dough, Sauce, Pepperoni, Fruits, Veggies ๋ก ๊ตฌ๋ถํ๊ณ ๊ฐ ์์ธ ํด๋์ค๋ฅผ ๋ง๋ค์ด์ค๋ค.
// Cheese ์ ๊ฒฝ์ฐ
public interface Cheese {
String toString();
}
public class MozzarellaCheese implements Cheese { // ๊ฐ Cheese ๊ตฌํ
public String toString() { return "mozzarella cheese"; }
}
// Dough ์ ๊ฒฝ์ฐ
public interface Dough {
String toString();
}
public class ThinCrustDough implements Dough { // ๊ฐ Dough ๊ตฌํ
public String toString() { return "thin crust dough"; }
}
// Sauce, Veggies, Pepperoni, Fruits .. ์ดํ์๋ต
(2) ์์ฌ๋ฃ ๊ณต์ฅ์ ๋ง๋ค์.
โ ์ง์ญ๋ณ๋ก ๊ณต์ฅ (factory) ์ ๋ง๋ค์ด ๊ฐ ์์ฑ ๋ฉ์๋๋ฅผ ๊ตฌํํ๋ PizzaIngredientFactory ํด๋์ค๋ฅผ ๋ง๋ค์.
public interface PizzaIngredientFactory {
public Dough createDough();
public Cheese createCheese();
public Fruits createFruits();
public Pepperoni createPepperoni();
public Veggies[] createVeggies();
public Sauce createSauce();
}
โก ๊ฐ ์ง์ ์ ์์ฌ๋ฃ ๊ณต์ฅ (factory) ๋ฅผ ๊ตฌํ
// ์์ธ ์ง์ ์์ฌ๋ฃ ๊ณต์ฅ
public class SeoulIngredientFactory implements PizzaIngredientFactory {
@Override
public Dough createDough() {
return new ThickCrustDough();
}
@Override
public Cheese createCheese() {
return new ParmesanCheese();
}
@Override
public Fruits createFruits() {
return new Pineapple();
}
@Override
public Pepperoni createPepperoni() {
return new SlicedPepperoni();
}
@Override
public Veggies[] createVeggies() {
Veggies[] veggies = {new Garlic(), new Spinach(), new BlackOlives()};
return veggies;
}
@Override
public Sauce createSauce() {
return new TomatoSauce();
}
}
// ๋ถ์ฐ ์ง์ ์์ฌ๋ฃ ๊ณต์ฅ
public class BusanIngredientFactory implements PizzaIngredientFactory {
@Override
public Dough createDough() {
return new ThinCrustDough();
}
@Override
public Cheese createCheese() {
return new ReggianoCheese();
}
@Override
public Fruits createFruits() {
return new Pineapple();
}
@Override
public Pepperoni createPepperoni() {
return new SlicedPepperoni();
}
@Override
public Veggies[] createVeggies() {
Veggies[] veggies = {new Onion(), new Spinach(), new Mushroom()};
return veggies;
}
@Override
public Sauce createSauce() {
return new MarinaraSauce();
}
}
(3) ํผ์ ์ถ์ ํด๋์ค, ๊ฐ ํผ์ ๊ตฌํ
// ํผ์ ์ถ์ ํด๋์ค
public abstract class Pizza {
public String name; // ํผ์์ด๋ฆ
public Dough dough; // ๋์ฐ
public Cheese cheese; // ์น์ฆ
public Fruits fruits; // ๊ณผ์ผ
public Pepperoni pepperoni; // ํํ๋ก๋
public Sauce sauce; // ์์ค
public Veggies[] veggies; // ์ผ์ฑ๋ค
public abstract void prepare(); // point : ์ถ์๋ฉ์๋๋ก ๋ณ๊ฒฝ๋จ!!
public void bake() {
System.out.println("baking... 350 ๋์์ 25๋ถ");
}
public void box() {
System.out.println("boxing... ๋ฐ์ค์ ํฌ์ฅ");
}
public void cut() {
System.out.println("cutting... ์ปคํ
");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("===== " + name + " =====\n");
if (dough != null) {
sb.append(dough);
sb.append("\n");
}
if (sauce != null) {
sb.append(sauce);
sb.append("\n");
}
if (cheese != null) {
sb.append(cheese);
sb.append("\n");
}
if (pepperoni != null) {
sb.append(pepperoni);
sb.append("\n");
}
if (fruits != null) {
sb.append(fruits);
sb.append("\n");
}
if (veggies != null) {
for (Veggies veg : veggies) {
sb.append(veg);
sb.append(", ");
}
sb.append("\n");
}
return sb.toString();
}
}
// ๊ฐ ํผ์ ๊ตฌํ
public class PepperoniPizza extends Pizza {
PizzaIngredientFactory ingredientFactory;
public PepperoniPizza(PizzaIngredientFactory ingredientFactory) {
this.ingredientFactory = ingredientFactory;
}
@Override
public void prepare() {
System.out.println("preparing... " + name);
dough = ingredientFactory.createDough();
pepperoni = ingredientFactory.createPepperoni();
cheese = ingredientFactory.createCheese();
sauce = ingredientFactory.createSauce();
veggies = ingredientFactory.createVeggies();
}
}
// ๋ถ์ฐ์คํ์ผ ํํ๋ก๋ ํผ์ ๊ตฌํ
public class BusanStylePepperoniPizza extends Pizza {
PizzaIngredientFactory ingredientFactory;
public BusanStylePepperoniPizza(PizzaIngredientFactory ingredientFactory) {
this.ingredientFactory = ingredientFactory;
}
@Override
public void prepare() {
System.out.println("preparing... " + name);
dough = ingredientFactory.createDough();
pepperoni = ingredientFactory.createPepperoni();
cheese = ingredientFactory.createCheese();
sauce = ingredientFactory.createSauce();
veggies = ingredientFactory.createVeggies();
}
@Override
public void box() {
System.out.println("๋ถ์ฐ ์ด๋ฒคํธ ๋ฐ์ค์ ๋ฃ์ต๋๋ค.");
}
}
(4) ๋ง๋ ์์ฌ๋ฃ ๊ณต์ฅ์ PizzaStore ์์ ์ฌ์ฉํ๋๋ก ํจ์ผ๋ก์จ ํ๋๋ก ๋ฌถ์
// ์์ธ์ง์
public class SeoulPizzaStore extends PizzaStore {
@Override
protected Pizza createPizza(String item) {
Pizza pizza = null;
// ์์ฌ๋ฃ ๊ณต์ฅ
PizzaIngredientFactory ingredientFactory = new SeoulIngredientFactory();
if (item.equals(PizzaType.PEPPERONI)) {
pizza = new PepperoniPizza(ingredientFactory);
pizza.setName("์์ธ ์คํ์ผ ํํ๋ก๋ ํผ์");
} else if (item.equals(PizzaType.HAWAIIAN)) {
pizza = new HawaiianPizza(ingredientFactory);
pizza.setName("์์ธ ์คํ์ผ ํ์์ด์ ํผ์");
} else if (item.equals(PizzaType.VEGGIE)) {
pizza = new VeggiePizza(ingredientFactory);
pizza.setName("์์ธ ์คํ์ผ ๋ฒ ์งํ
๋ฆฌ์ ํผ์");
}
return pizza;
}
}
// ๋ถ์ฐ์ง์
public class BusanPizzaStore extends PizzaStore {
@Override
protected Pizza createPizza(String item) {
Pizza pizza = null;
PizzaIngredientFactory ingredientFactory = new BusanIngredientFactory();
if (item.equals(PizzaType.PEPPERONI)) {
pizza = new BusanStylePepperoniPizza(ingredientFactory);
pizza.setName("๋ถ์ฐ ์คํ์ผ ํํ๋ก๋ ํผ์");
} else if (item.equals(PizzaType.HAWAIIAN)) {
pizza = new HawaiianPizza(ingredientFactory);
pizza.setName("๋ถ์ฐ ์คํ์ผ ํ์์ด์ ํผ์");
} else if (item.equals(PizzaType.VEGGIE)) {
pizza = new VeggiePizza(ingredientFactory);
pizza.setName("๋ถ์ฐ ์คํ์ผ ๋ฒ ์งํ
๋ฆฌ์ ํผ์");
}
return pizza;
}
}
(5) ์คํ
@Test
void abstractFactoryTest() {
PizzaStore seoulStore = new SeoulPizzaStore(); // ์์ธ์ง์ ์์ฑ
PizzaStore busanStore = new BusanPizzaStore(); // ๋ถ์ฐ์ง์ ์์ฑ
System.out.println("์์ธ ํผ์ ์ฃผ๋ฌธ 1");
Pizza seoul1 = seoulStore.orderPizza(PizzaType.PEPPERONI); // ์์ธ์ง์ ์ฃผ๋ฌธ1
System.out.println("์์ธ ํผ์ ์ฃผ๋ฌธ 2");
Pizza seoul2 = seoulStore.orderPizza(PizzaType.VEGGIE); // ์์ธ์ง์ ์ฃผ๋ฌธ2
System.out.println("๋ถ์ฐ ํผ์ ์ฃผ๋ฌธ 1");
Pizza busan1 = busanStore.orderPizza(PizzaType.PEPPERONI); // ๋ถ์ฐ์ง์ ์ฃผ๋ฌธ1
System.out.println("๋ถ์ฐ ํผ์ ์ฃผ๋ฌธ 2");
Pizza busan2 = busanStore.orderPizza(PizzaType.VEGGIE); // ๋ถ์ฐ์ง์ ์ฃผ๋ฌธ2
}
(6) ๊ฒฐ๊ณผํ์ธ
์์ธ ํผ์ ์ฃผ๋ฌธ 1
===== ์์ธ ์คํ์ผ ํํ๋ก๋ ํผ์ ์ ์ =====
preparing... ์์ธ ์คํ์ผ ํํ๋ก๋ ํผ์
baking... 350 ๋์์ 25๋ถ
cutting... ์ปคํ
boxing... ๋ฐ์ค์ ํฌ์ฅ
์์ธ ํผ์ ์ฃผ๋ฌธ 2
===== ์์ธ ์คํ์ผ ๋ฒ ์งํ
๋ฆฌ์ ํผ์ ์ ์ =====
preparing... ์์ธ ์คํ์ผ ๋ฒ ์งํ
๋ฆฌ์ ํผ์
baking... 350 ๋์์ 25๋ถ
cutting... ์ปคํ
boxing... ๋ฐ์ค์ ํฌ์ฅ
๋ถ์ฐ ํผ์ ์ฃผ๋ฌธ 1
===== ๋ถ์ฐ ์คํ์ผ ํํ๋ก๋ ํผ์ ์ ์ =====
preparing... ๋ถ์ฐ ์คํ์ผ ํํ๋ก๋ ํผ์
baking... 350 ๋์์ 25๋ถ
cutting... ์ปคํ
๋ถ์ฐ ์ด๋ฒคํธ ๋ฐ์ค์ ๋ฃ์ต๋๋ค.
๋ถ์ฐ ํผ์ ์ฃผ๋ฌธ 2
===== ๋ถ์ฐ ์คํ์ผ ๋ฒ ์งํ
๋ฆฌ์ ํผ์ ์ ์ =====
preparing... ๋ถ์ฐ ์คํ์ผ ๋ฒ ์งํ
๋ฆฌ์ ํผ์
baking... 350 ๋์์ 25๋ถ
cutting... ์ปคํ
boxing... ๋ฐ์ค์ ํฌ์ฅ
4. ์ ๋ฆฌํ๋ฉด
๐ ์ถ์ ํฉํ ๋ฆฌ ํจํด
์ ํ๊ตฐ์ ์์ฑํ๊ธฐ ์ํ ์ธํฐํ์ด์ค๋ฅผ ์์ฑ ๊ทธ ์ธํฐํ์ด์ค๋ฅผ ๊ตฌ์ฑํ์ฌ ์ฌ์ฉํ ์ ์๊ฒ๋ ํ๋๊ฒ
๐ ์ถ์ ๋ฉ์๋ ํจํด
ํ๋์ ์ถ์ํด๋์ค์์ ์ถ์ ๋ฉ์๋๋ฅผ ๋ง๋ค๊ณ ์๋ธํด๋์ค๋ค์ด ๊ทธ ์ถ์๋ฉ์๋๋ฅผ ๊ตฌํ ํ์ฌ ์ธ์คํด์ค๋ฅผ ๋ง๋ค๊ฒ๋ ํ๋๊ฒ
์ฐธ๊ณ
ํค๋ ํผ์คํธ ๋์์ธ ํจํด (Head First Design Patterns) - ์๋ฆญ ํ๋ฆฌ๋จผ,์๋ฆฌ์๋ฒ ์ค ๋กญ์จ,์ผ์ด์ ์์๋ผ,๋ฒํธ ๋ฒ ์ด์ธ