01. Builder Pattern
Motivation - 빌더 패턴을 사용하는 이유
- Some objects are simple and can be created in single initializer call
- Other objects require a lot of ceremony to create
- Having an object with 10 initializer arguments is not productive
- Instead, opt for piecewise construction
-
Builder provides an API for constructing an object step-by-step
- 일부 객체는 간단하며 한 번의 이니셜라이저 호출로 생성할 수 있습니다.
- 다른 객체는 생성하는 데 많은 의식이 필요합니다.
- 이니셜라이저 인자가 10개인 객체를 갖는 것은 생산적이지 않습니다.
- 대신, 조각별로 구성하는 방법을 선택하세요.
- 빌더는 단계별로 객체를 구성하기 위한 API를 제공합니다.
빌더의 공식적인 정의
When piecewise object construction is complicated, provide an API for doing it succinctly
text = "hello"
parts = ["<p>", text, "</p>"]
print("".join(parts))
words = ["hello", "world"]
parts = ["<ul>"]
for w in words:
parts.append(f"<li>{w}</l;i")
parts.append("</ul>")
print('\n'.join(parts))
class HtmlElement:
indent_size = 2
def __init__(self, name="", text=""):
self.name = name
self.text = text
self.elements = []
def __str(self, indent):
lines = []
i = " " * (indent * self.indent_size)
lines.append(f"{i}<{self.name}>")
if self.text:
i1 = " " * ((indent + 1) * self.indent_size)
lines.append(f"{i1}{self.text}")
for e in self.elements:
lines.append(e.__str(indent + 1))
lines.append(f"{i}</{self.name}>")
return "\n".join(lines)
def __str__(self):
return self.__str(0)
@staticmethod
def create(name):
return HtmlBuilder(name)
class HtmlBuilder:
def __init__(self, root_name):
self.root_name = root_name
self.__root = HtmlElement(name=root_name)
def add_child(self, child_name, child_text):
self.__root.elements.append(HtmlElement(child_name, child_text))
def add_child_fluent(self, child_name, child_text):
"""Method 체이닝을 할 수 있게 됨"""
self.__root.elements.append(HtmlElement(child_name, child_text))
return self
def __str__(self):
return str(self.__root)
builder = HtmlElement.create("ul")
# builder = HtmlBuilder('ul')
# builder.add_child('li', 'hello')
# builder.add_child('li', 'world')
builder.add_child_fluent("li", "hello").add_child_fluent("li", "world")
print("Ordinary builder:")
print(builder)
다른 예시
class Person:
def __init__(self):
# address
self.street_address = None
self.postcode = None
self.city = None
# employment
self.company_name = None
self.position = None
self.annual_income = None
def __str__(self):
return (
f"Address: {self.street_address}, {self.postcode}, {self.city}"
+ f"Employed at {self.company_name} as a {self.position} earning {self.annual_income}"
)
class PersonBuilder:
def __init__(self, person=Person()):
self.person = person
@property
def works(self):
return PersonJobBuilder(self.person)
@property
def lives(self):
return PersonAddressBuilder(self.person)
def build(self):
return self.person
class PersonJobBuilder(PersonBuilder):
def __init__(self, person: Person):
super().__init__(person)
def at(self, company_name):
self.person.company_name = company_name
return self
def as_a(self, position):
self.person.position = position
return self
def earning(self, annual_income):
self.person.annual_income = annual_income
return self
class PersonAddressBuilder(PersonBuilder):
def __init__(self, person: Person):
super().__init__(person)
def at(self, street_address):
self.person.street_address = street_address
return self
def with_postcode(self, postcode):
self.person.postcode = postcode
return self
def in_city(self, city):
self.person.city = city
return self
pb = PersonBuilder()
person = (
pb
.lives
.at("123 London Road")
.in_city("London")
.with_postcode("SW12BC")
.works
.at("Fabrikam")
.as_a("Engineer")
.earning(123000)
.build()
)
print(person)
개방형 폐쇄 원칙을 따른 상속을 통해 확장으로 만든 빌더패턴
class Person:
def __init__(self):
self.name = None
self.position = None
self.date_of_birth = None
def __str__(self):
return f"{self.name} born on {self.date_of_birth} " + f"works as {self.position}"
@staticmethod
def new():
return PersonBuilder()
class PersonBuilder:
def __init__(self):
self.person = Person()
def build(self):
return self.person
class PersonInfoBuilder(PersonBuilder):
def called(self, name):
self.person.name = name
return self
class PersonJobBuilder(PersonInfoBuilder):
def works_as_a(self, position):
self.person.position = position
return self
class PersonBirthDateBuilder(PersonJobBuilder):
def born(self, date_of_birth):
self.person.date_of_birth = date_of_birth
return self
pb = PersonBirthDateBuilder()
me = pb\
.called('Dmitri')\
.works_as_a('Quant')\
.born('1/1/1980')\
.build()
print(me)
02. Builder Pattern 자바 예시
public class Computer {
// required parameters
private String HDD;
private String RAM;
// optional parameters
private boolean isGraphicsCardEnabled;
private boolean isBluetoothEnabled;
public String getHDD() {
return HDD;
}
public String getRAM() {
return RAM;
}
public boolean isGraphicsCardEnabled() {
return isGraphicsCardEnabled;
}
public boolean isBluetoothEnabled() {
return isBluetoothEnabled;
}
private Computer(ComputerBuilder builder) {
this.HDD = builder.HDD;
this.RAM = builder.RAM;
this.isGraphicsCardEnabled = builder.isGraphicsCardEnabled;
this.isBluetoothEnabled = builder.isBluetoothEnabled;
}
// Builder Class
public static class ComputerBuilder {
// required parameters
private String HDD;
private String RAM;
// optional parameters
private boolean isGraphicsCardEnabled;
private boolean isBluetoothEnabled;
public ComputerBuilder(String hdd, String ram) {
this.HDD = hdd;
this.RAM = ram;
}
public ComputerBuilder setGraphicsCardEnabled(boolean isGraphicsCardEnabled) {
this.isGraphicsCardEnabled = isGraphicsCardEnabled;
return this;
}
public ComputerBuilder setBluetoothEnabled(boolean isBluetoothEnabled) {
this.isBluetoothEnabled = isBluetoothEnabled;
return this;
}
public Computer build() {
return new Computer(this);
}
}
}
여기서 살펴볼 것은 Computer 클래스가 setter 메소드 없이 getter 메소드만 가진다는 것과 public 생성자가 없다는 것입니다. 그렇기 때문에 Computer 객체를 얻기 위해서는 오직 ComputerBuilder 클래스를 통해서만 가능합니다.
public class TestBuilderPattern {
public static void main(String[] args) {
Computer comp = new Computer.ComputerBuilder("500GB", "2GB")
.setBluetoothEnabled(true)
.setGraphiteEnabled(true)
.build();
}
}
댓글남기기