빌더 패턴이란?
프로그램의 기능과 구현을 분리하여 둘의 구조가 독립적으로 변화될 수 있도록 한 패턴
복합체의 구축 과정에서 그 형식을 분리 하여 동일한 구축방식으로 다양한 종류의 복합체를 만들 수 있도록 한 형태
→ 한 객체의 내부 구축과정을 외부의 존재가 하도록 함.
DI, 의존성 주입도 이용한다.
1. 프로그램 실행시 특정한 implementation이 선택되도록 할 때
2. Abstraction과 implementation이 별도의 계층구조를 가질 때
3. implementation부분을 수정하여도 client코드에는 영향이 없을 때
의존성 주입에 해당되는 형태가 적용된다.
의존성 주입
→ 참조변수를 설정하기 위해서 외부에서 생성된 인스턴스를 생성자나 setter에 전달하는것
TextBuilder tb = new TextBuilder();
Director director = new Director(tb);
Director와 Builder클래스는 이름과 역할이 동일하다.
TextBuilder와 HTMLBuilder의 경우 ConcreateBuilder의 역할이다.
Main은 Client의 역할을 수행한다.
import java.io.*;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
abstract class Builder {
public abstract void makeTitle(String title);
public abstract void makeString(String str);
public abstract void makeItems(String[] items);
public abstract void close();
}
class Director {
private Builder builder;
public Director(Builder builder) {
this.builder = builder;
}
public void construct() {
builder.makeTitle("Greeting");
builder.makeString("일반적인 인사");
builder.makeItems(new String[]{"How are you?", "Hello.", "Hi.",});
builder.makeString("시간대별 인사");
builder.makeItems(new String[]{"Good morning.", "Good afternoon.", "Good evening.",});
builder.close();
}
}
class TextBuilder extends Builder {
private StringBuilder sb = new StringBuilder();
@Override
public void makeTitle(String title) {
sb.append("==============================\n");
sb.append("[");
sb.append(title);
sb.append("]\n\n");
}
@Override
public void makeString(String str) {
sb.append(" ■ ");
sb.append(str);
sb.append("\n\n");
}
@Override
public void makeItems(String[] items) {
for (String s : items) {
sb.append(" - ");
sb.append(s);
sb.append("\n");
}
sb.append("\n");
}
@Override
public void close() {
sb.append("==============================\n");
}
public String getTextResult() {
return sb.toString();
}
}
class HTMLBuilder extends Builder {
private String filename = "untitled.html";
private StringBuilder sb = new StringBuilder();
@Override
public void makeTitle(String title) {
filename = title + ".html";
sb.append("<!DOCTYPE html>\n");
sb.append("<html>\n");
sb.append("<head><title>");
sb.append(title);
sb.append("</title></head>\n");
sb.append("<body>\n");
sb.append("<h1>");
sb.append(title);
sb.append("</h1>\n\n");
}
@Override
public void makeString(String str) {
sb.append("<p>");
sb.append(str);
sb.append("</p>\n\n");
}
@Override
public void makeItems(String[] items) {
sb.append("<ul>\n");
for (String s : items) {
sb.append("<li>");
sb.append(s);
sb.append("</li>\n");
}
sb.append("</ul>\n\n");
}
@Override
public void close() {
sb.append("</body>");
sb.append("</html>\n");
try {
Writer writer = new FileWriter(filename);
writer.write(sb.toString());
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public String getHTMLResult() {
return filename;
}
}
public class Main {
public static void main(String[] args) {
if (args.length != 1) {
usage();
System.exit(0);
}
//arg에 어떤 text가 있는지에 따라 다른 객체를 생성하는 방식의 main문
if (args[0].equals("text")) {
TextBuilder textbuilder = new TextBuilder();
System.out.println(textbuilder.getTextResult());
Director director = new Director(textbuilder);
director.construct();
String result = textbuilder.getTextResult();
System.out.println(result);
} else if (args[0].equals("html")) {
HTMLBuilder htmlbuilder = new HTMLBuilder();
Director director = new Director(htmlbuilder);
director.construct();
String filename = htmlbuilder.getHTMLResult();
System.out.println("HTML파일 " + filename + "이 작성되었습니다.");
} else {
//만약 args[]에 아무 데이터가 없을 경우 안내 문구를 출력
usage();
System.exit(0);
}
}
// 사용 방법을 표시한다
public static void usage() {
System.out.println("Usage: java Main text 텍스트로 문서 작성");
System.out.println("Usage: java Main html HTML 파일로 문서 작성");
}
}
import Foundation
protocol Builder {
func makeTitle(_ title: String)
func makeString(_ str: String)
func makeItems(_ items: [String])
func close()
}
class Director {
var builder: Builder
init(_ builder: Builder) {
self.builder = builder
}
func construct() {
builder.makeTitle("Greeting")
builder.makeString("일반적인 인사")
builder.makeItems(["How are you?", "Hello.", "Hi."])
builder.makeString("시간대별 인사")
builder.makeItems(["Good morning.", "Good afternoon.", "Good evening."])
builder.close()
}
}
class TextBuilder : Builder{
//swift에서는 StringBuilder를 사용안해도 일반 String이 java의 StringBuilder처럼 동작함
private var sb: String = ""
init(){
}
func makeTitle(_ title: String) {
sb.append("=======================\n")
sb.append("[ \(title) ]\n\n")
}
func makeString(_ str: String) {
sb.append(" ■ \(str)\n\n")
}
func makeItems(_ items: [String]) {
for s in items {
sb.append(" - ");
sb.append(s);
sb.append("\n");
}
sb.append("\n");
}
func close() {
sb.append("==============================\n")
}
func getTextResult() -> String {
return sb
}
}
class HTMLBuilder: Builder {
private var filename: String = "untitled.html"
private var sb: String = ""
func makeTitle(_ title: String) {
filename = title + ".html"
sb.append("<!DOCTYPE html>\n")
sb.append("<html>\n")
sb.append("<head><title>")
sb.append(title)
sb.append("</title></head>\n")
sb.append("<body>\n")
sb.append("<h1>")
sb.append(title)
sb.append("</h1>\n\n")
}
func makeString(_ str: String) {
sb.append("<p>")
sb.append(str)
sb.append("</p>\n\n")
}
func makeItems(_ items: [String]) {
sb.append("<ul>\n")
for s in items {
sb.append("<li>")
sb.append(s)
sb.append("</li>\n")
}
sb.append("</ul>\n\n")
}
func close() {
sb.append("</body>")
sb.append("</html>\n")
//파일 출력
// 파일 이름과 경로 지정 (여기서는 Document 디렉토리에 저장)
if let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
let fileURL = documentDirectory.appendingPathComponent(filename)
do {
// 문자열을 데이터로 변환
try sb.write(to: fileURL, atomically: true, encoding: .utf8)
print("File successfully written to: \(fileURL.path)")
} catch {
print("Failed to write to file: \(error.localizedDescription)")
}
}
}
func getHTMLResult() -> String {
return filename
}
}
@main
struct Main {
static func main() {
print("출력하고자 하는 형태를 입력하세요(text or html): ")
let input = readLine() ?? ""
print(input)
if (input == "text") {
let textbuilder: TextBuilder = TextBuilder();
print(textbuilder.getTextResult());
let director: Director = Director(textbuilder);
director.construct();
let result: String = textbuilder.getTextResult();
print(result);
} else if (input == "html") {
let htmlbuilder: HTMLBuilder = HTMLBuilder();
let director: Director = Director(htmlbuilder);
director.construct();
let filename: String = htmlbuilder.getHTMLResult();
print("HTML파일 \(filename)이 작성되었습니다.");
} else {
//만약 args[]에 아무 데이터가 없을 경우 안내 문구를 출력
usage();
}
}
static func usage() {
print("Usage: java Main text 텍스트로 문서 작성");
print("Usage: java Main html HTML 파일로 문서 작성");
}
}
프로그램의 기능과 구현을 분리하여 둘의 구조가 독립적으로 변화될 수 있도록 한 패턴이다.
기능 계층과 구현 계층을 분리하여 각각을 독립적으로 확장할 수 있다
기능의 추가는 기능 계층에서만 이루어 지고 구현계층에는 영향을 미치지 않는다.