A A
[Design Pattern] Builder Pattern - ๋นŒ๋” ํŒจํ„ด

Builder Pattern

Builder ํŒจํ„ด์€ ๊ฐ์ฒด ์ƒ์„ฑ ํŒจํ„ด ์ค‘ ํ•˜๋‚˜๋กœ, ๋ณต์žกํ•œ ๊ฐ์ฒด๋ฅผ ๋‹จ๊ณ„์ ์œผ๋กœ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ํŒจํ„ด์ž…๋‹ˆ๋‹ค.
  • Object์˜ ์ƒ์„ฑ๊ณผ์ •์ด ๋ณต์žกํ•  ๋•Œ ์ด๋ฅผ ๊ฐ„๋‹จํ•˜๊ฒŒ ๋งŒ๋“ค์–ด์ค๋‹ˆ๋‹ค.
  • ์ƒ์„ฑ๊ณผ์ •์ด ๋ณต์žกํ•  ์ˆ˜ ์žˆ๋Š” ์—ฌ๋Ÿฌ ๊ฒฝ์šฐ๋Š” ๋งค์šฐ ๋‹ค์–‘ํ•ฉ๋‹ˆ๋‹ค.
  • ์˜ˆ๋ฅผ ๋“ค์–ด ํ•˜๋‚˜์˜ Object๋ฅผ ์ƒ์„ฑ์‹œ, ์—ฌ๋Ÿฌ๊ฐœ์˜ argument๊ฐ€ ํ•„์š”ํ•  ๋•Œ Builder Pattern์ด ์‰ฝ๊ฒŒ ๋งŒ๋“ค์–ด ์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ•ต์‹ฌ ๊ฐœ๋…

  1. Builder ์ธํ„ฐํŽ˜์ด์Šค: ๊ฐ์ฒด๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ๊ฐ ๋‹จ๊ณ„๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.
  2. Concrete Builder ํด๋ž˜์Šค: Builder ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜์—ฌ, ๊ฐ ๋‹จ๊ณ„๋ฅผ ๊ตฌ์ฒด์ ์œผ๋กœ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.
  3. Director: Builder ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ์ฒด ์ƒ์„ฑ์˜ ์ˆœ์„œ๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. ์ƒ์„ฑ ๊ณผ์ •์˜ ์ œ์–ด๋ฅผ ๋‹ด๋‹นํ•ฉ๋‹ˆ๋‹ค.
  4. Product: ์ตœ์ข…์ ์œผ๋กœ ์ƒ์„ฑ๋  ๋ณต์žกํ•œ ๊ฐ์ฒด๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.

 

์žฅ์ 

  • ๋ณต์žกํ•œ ๊ฐ์ฒด ์ƒ์„ฑ์˜ ๋ถ„๋ฆฌ: ๊ฐ์ฒด ์ƒ์„ฑ ์ฝ”๋“œ๋ฅผ ์ œํ’ˆ์˜ ํ‘œํ˜„ ์ฝ”๋“œ์™€ ๋ถ„๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์œ ์—ฐํ•œ ๊ฐ์ฒด ์ƒ์„ฑ: ๋‹ค์–‘ํ•œ ๊ตฌ์„ฑ ์˜ต์…˜์„ ํ†ตํ•ด ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๊ฐ์ฒด ์ƒ์„ฑ์˜ ๋‹จ๊ณ„์  ์ ‘๊ทผ: ๊ฐ์ฒด๋ฅผ ๋‹จ๊ณ„๋ณ„๋กœ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์–ด, ์ƒ์„ฑ ๊ณผ์ •์˜ ์œ ์—ฐ์„ฑ์ด ๋†’์•„์ง‘๋‹ˆ๋‹ค.

 

๋‹จ์ 

  • ๋ณต์žก์„ฑ ์ฆ๊ฐ€: ํŒจํ„ด์„ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐ ํ•„์š”ํ•œ ํด๋ž˜์Šค๊ฐ€ ๋งŽ์•„์ ธ ์ฝ”๋“œ๊ฐ€ ๋ณต์žกํ•ด์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์ผ๊ด€๋œ ์ธํ„ฐํŽ˜์ด์Šค ์š”๊ตฌ: ๋ชจ๋“  Builder๊ฐ€ ๋™์ผํ•œ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•ด์•ผ ํ•˜๋ฏ€๋กœ, ์ƒˆ๋กœ์šด Builder๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ์ž‘์—…์ด ๋ฒˆ๊ฑฐ๋กœ์šธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 


Example.1 (๊ณ ์–‘์ด ์ƒ์„ฑ)

ํ•œ๋ฒˆ ๊ณ ์–‘์ด์˜ ์ƒ์„ฑ ๊ณผ์ •์ด ๋งค์šฐ ๋ณต์žกํ•˜๋‹ค๊ณ  ๊ฐ€์ •ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.
class Cat:
    def _init_ (self,height,weight,color):
        self.height = height
        self.weight = weight
        self.color = color

    def print(self):
        return print(f"Height: {self.height}, Weight: {self.weight}, Color: {self.color}")

# API
class catBuilder:
    def _init_(self):
        self.height = None
        self.weight = None
        self.color = None

    def set_height(self, height):
        self.height = height
        return self

    def set_weight(self, weight):
        self.weight = weight
        return self

    def set_color(self, color):
        self.color = color
        return self

    def build(self):
        cat = Cat(self.height, self.weight, self.color)
        return cat

# Concrete
class WhiteCatBuilder(catBuilder):
    def _init_(self):
        self.color = "white"

class BlackCatBuilder(catBuilder):
    def _init_(self):
        self.color = "black"

cat_builder = Cat()
cat_builder.set_height(30)
cat_builder.set_weight(7)
cat_builder.set_color("Black")
cat = cat_builder.build()
cat.print()

Example.2 (Pizza Case 1: Product)

ํ”ผ์ž๋ฅผ ํŒ๋งคํ•˜๊ณ ์ž ํ•  ๋•Œ, ๋‹ค์–‘ํ•œ ์ข…๋ฅ˜์˜ ํ”ผ์ž๋“ค์„ ํ•œ๋ฒˆ์— ์ƒ์„ฑํ•˜๊ณ ์ž ํ•ฉ๋‹ˆ๋‹ค.
  • 4๊ฐœ์˜ ์„ค์ •์ด ํ•„์š”ํ•œ ๋ณ€์ˆ˜๋“ค
class Pizza:
    def _init_(self):
        self.size = None
        self.cheese = False
        self.pepperoni = False
        self.bacon = False
    
    def pizza_state(self):
        print("size: {}, cheese: {}, pepperoni: {}, bacon: {}".format(self.size, self.cheese, self.pepperoni, self.bacon))
  • API & Concrete Builder
# API
class PizzaBuilder:
    def set_size(self, size):
        pass
    
    def add_cheese(self):
        pass
    
    def add_pepperoni(self):
        pass
    
    def add_bacon(self):
        pass
     
    def build(self):
        pass

# Concrete
class Margheritabuilder(PizzaBuilder):
    def _init_(self):
        self.pizza = Pizza()
    
    def set_size(self, size):
        self.pizza.size = size
    
    def add_cheese(self):
        self.pizza.cheese = True
    
    def add_pepperoni(self):
        pass
    
    def add_bacon(self):
        pass
    
    def build(self):
        return self.pizza

# Concrete
class MeatLoversBuilder(PizzaBuilder):
    def _init_(self):
        self.pizza = Pizza()
    
    def set_size(self, size):
        self.pizza.size = size
    
    def add_cheese(self):
        self.pizza.cheese = True
    
    def add_pepperoni(self):
        self.pizza.pepperoni = True
    
    def add_bacon(self):
        self.pizza.bacon = True
    
    def build(self):
        return self.pizza

# Director
class PizzaDirector:
    def _init_(self, builder):
        self.builder = builder
    
    def construct(self, size="Medium"):
        self.builder.set_size(size)
        self.builder.add_cheese()
        self.builder.add_pepperoni()
        self.builder.add_bacon()
        return self.builder.build()

margherita_builder = Margheritabuilder()
director = PizzaDirector(margherita_builder)
margherita_pizza = director.construct(size="Large")
print("Margherita Pizza", margherita_pizza)

meat_lovers_builder = MeatLoversBuilder()
director = PizzaDirector(meat_lovers_builder)
meat_lovers_pizza = director.construct(size="Large")
print("Meat Lovers Pizza", meat_lovers_pizza)

Example.3 (Pizza Case 2: Product)

ํ”ผ์ž๋ฅผ ํŒ๋งคํ•˜๊ณ ์ž ํ•  ๋•Œ, ๋‹ค์–‘ํ•œ ์ข…๋ฅ˜์˜ ํ”ผ์ž๋“ค์„ ๋ฏธ๋ฆฌ ์„ค์ •๋œ argument๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ƒ์„ฑํ•˜๊ณ ์ž ํ•ฉ๋‹ˆ๋‹ค.
  • 5๊ฐœ์˜ ์„ค์ •์ด ํ•„์š”ํ•œ ๋ณ€์ˆ˜๋“ค
class Pizza:
    def _init_(self):
        self.size = None
        self.cheese = False
        self.pepperoni = False
        self.bacon = False
        self.vegetables = []
    
    def pizza_state(self):
        print("size: {}, cheese: {}, pepperoni: {}, bacon: {}, vegetables: {}".format(self.size, self.cheese, self.pepperoni, self.bacon, self.vegetables))

# ๋ณ„๋„์˜ Builder API๋ฅผ ๋‘์ง€ ์•Š์Œ
class PizzaBuilder:
    def set_size(self, size):
        self.pizza.size = size
        return self
    
    def add_cheese(self):
        self.pizza.cheese = True
        return self
    
    def add_pepperoni(self):
        self.pizza.pepperoni = True
        return self
    
    def add_bacon(self):
        self.pizza.bacon = True
        return self
     
    def add_vegetables(self, vegetables):
        self.pizza.vegetables = vegetables
        return self

    def build(self):
        finished_pizza = self.pizza
        self.reset()
        return finished_pizza

# ์ข…๋ฅ˜๋ณ„ ํ”ผ์ž์— ๋Œ€ํ•œ ์ฝ”๋“œ๋ฅผ ์ง์ ‘ ์ž…๋ ฅํ•ด์•ผํ•จ
class PizzaDirector:
    def _init_(self, builder):
        self.builder = builder
    
    def prepare_marinara(self):
        return self.builder.set_size("Large").add_cheese().build()
    
    def prepare_meat_lovers(self):
        return self.builder.set_size("Large").add_cheese().add_pepperoni().add_bacon().build()
    
    def prepare_vegetarian(self):
        self.builder.reset()
        return self.builder.set_size("Large").add_cheese().add_vegetables(["Bell papers", "Olives", "Onions"]).build()

# ์‚ฌ์šฉ ์˜ˆ์‹œ - ์ข…๋ฅ˜๋ณ„ ํ”ผ์ž์— ๋Œ€ํ•œ ์ฝ”๋“œ๋ฅผ ์ง์ ‘ ์ž…๋ ฅํ•ด์•ผํ•จ
builder = PizzaBuilder()
director = PizzaDirector(builder)

margherita = director.prepare_marinara()
margherita.pizza_state()

meat_lovers = director.prepare_meat_lovers()
meat_lovers.pizza_state()

vegetarian = director.prepare_vegetarian()
vegetarian.pizza_state()