참고: Head First Design Patterns
본 교재의 메인 챕터에서 미처 다루지 못한 디자인 패턴을 소개하는 부록이다.

위와 같은 구조의 리모컨 프로그램을 개발중이라고 가정해보자. 각 리모컨은 동일한 추상화된 로직을 바탕으로 구현한다. 따라서, 위와 같이 추상 클래스(혹은 인터페이스)를 만들고 이를 구현하도록 하였다.
여기서 문제는, 추상화 덕에 각 구현체들의 세부 구현에 변주를 줄 순 있으나 추상화 자체의 다양화는 어떻게 할 수 있을까?

위 처럼 RemoteControl의 추상화와 TV의 구체화(구현)을 분리할 수 있다. 이 둘을 연결하는 HAS-A 관계를 다리(Bridge)에 비유한 패턴이다.
이렇게 함으로써 둘을 독립적으로 확장시킬 수 있다. 따라서, 여러 플랫폼에서 동작하는 그래픽 혹은 화면 시스템에서 유용하게 사용된다. 다만, 객체와 클래스가 많아지면서 복잡도 역시 증가할 수 있다는 단점이 있다.
원을 그려주는 프로그램을 만들어보자.
public interface DrawAPI {
public void drawCircle(int radius, int x, int y);
}
public class RedCircle implements DrawAPI {
@Override
public void drawCircle(int radius, int x, int y) {
// 빨간색 원 그리기
}
}
public class GreenCircle implements DrawAPI {
@Override
public void drawCircle(int radius, int x, int y) {
// 초록색 원 그리기
}
}
public abstract class Shape {
protected DrawAPI drawAPI;
protected Shape(DrawAPI drawAPI){
this.drawAPI = drawAPI;
}
public abstract void draw();
}
public class Circle extends Shape {
private int x, y, radius;
public Circle(int x, int y, int radius, DrawAPI drawAPI) {
super(drawAPI);
this.x = x;
this.y = y;
this.radius = radius;
}
public void draw() {
drawAPI.drawCircle(radius,x,y);
}
}
위 코드를 통해 아래 다이어그램과 같이 Circle의 구체화와 draw() 기능의 세부 구현을 독립적으로 추상화하였다.

이제 클라이언트는 아래와 같이 호출하게 된다.
Shape redCircle = new Circle(100,100, 10, new RedCircle());
Shape greenCircle = new Circle(100,100, 10, new GreenCircle());
redCircle.draw();
greenCircle.draw();
테마파크 측에서 고객들의 일정표 클래스를 개발중이다. 그러나, 고객마다 일정이 너무 다르다. 가령, 호텔을 예약할 수도 있고 안 할수도 있으며 당일치기 고객과 2박3일 고객, 식사를 할 고객과 하지 않을 고객 등 변수가 너무 많다. 이러한 변수들은 생성자 오버로딩 만으로는 해결이 안된다.
따라서 우리에게 필요한 것은 이 복잡한 객체를 단계적으로 생성할 수 있도록 해주는 디자인 패턴이다. 예제 코드를 통해 살펴보자.
class Vacation {
private List<Person> persons = new ArrayList<Person>();
private Hotel hotel;
private Reservation reservation;
private List<Activity> activities = new ArrayList<Activity>();
public void addPerson(Person person) { this.persons.add(person); }
public void setHotel(Hotel hotel) { this.hotel = hotel; }
public void setReservation(Reservation reservation) { this.reservation = reservation; }
public void addActivity(Activity activity) { this.activities.add(activity); }
public String show() {
String result = "";
result += persons;
result += hotel;
result += reservation;
result += activities;
return result;
}
}
우선 일정표 클래스이다. 인원 목록, 호텔, 예약 일수, 활동 목록 필드와 setter 메소드들을 갖는다.
enum Activity {
RUNNING, RELAXING, SWIMMING
}
class Hotel {
private String name;
public Hotel(String name) { setName(name); }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
}
class Person {
private String lastName;
private String firstName;
private Date dateOfBirth;
public Person(String lastName,String firstName,Date dateOfBirth) {
this.lastName = lastName;
this.firstName = firstName;
this.dateOfBirth = dateOfBirth;
}
}
class Reservation {
private Date startDate;
private Date endDate;
public Reservation(Date in, Date out) {
this.startDate = in;
this.endDate = out;
}
}
위는 Vacation의 각 필드들의 구현체이다. 이제 Vacation 클래스의 빌더(Builder) 클래스를 구현해보자.
class VacationBuilder {
private static VacationBuilder builder = new VacationBuilder();
private VacationBuilder() {}
public static VacationBuilder getInstance() { return builder; }
private Vacation vacation = new Vacation();
public void addPerson(String firstName, String lastName) {
Person p = new Person(lastName, firstName, new Date());
this.vacation.addPerson(p);
}
public void setHotel(String name) { this.vacation.setHotel(new Hotel(name)); }
public void addActivity(Activity activity) { this.vacation.addActivity(activity); }
public void setReservation(String in, String uit) {
Date inDate = new Date();
Date outDate = new Date(new Date().getTime() + 10000);
Reservation reservation = new Reservation(inDate, outDate);
this.vacation.setReservation(reservation);
}
// 완성된 객체 반환
public Vacation getVacation() { return this.vacation; }
}
싱글톤(Singleton) 객체로 구현되어있다. Vacation 객체를 가지며, 클라이언트는 아래와 같이 해당 객체를 직접 생성하지 않고 빌더에게 정보를 전달하고, 최종적으로 getter를 통해 완성된 객체를 반환받는다.
public static void main(String[] args) {
VacationBuilder builder = VacationBuilder.getInstance();
builder.addActivity(Activity.RUNNING);
builder.addPerson("Smith", "John");
builder.setHotel("ACME Hotel");
builder.setReservation("1-2-2015", "1-8-2015");
Vacation vacation = builder.getVacation();
String show = vacation.show();
System.out.println(show);
}
빌더 패턴을 이용하면 복잡한 객체를 생성하는 과정을 캡슐화 할 수 있으며, 여러 단계에 걸쳐 생성되도록 할 수 있다. 따라서 복합적인 구조에 자주 사용되지만 팩토리에 비해 클라이언트에 대한 도메인 지식을 더 많이 요구한다.