[디자인패턴] 14. the Bridge and Builder Patterns

StandingAsh·2024년 12월 6일

참고: Head First Design Patterns

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

the Bridge Pattern


위와 같은 구조의 리모컨 프로그램을 개발중이라고 가정해보자. 각 리모컨은 동일한 추상화된 로직을 바탕으로 구현한다. 따라서, 위와 같이 추상 클래스(혹은 인터페이스)를 만들고 이를 구현하도록 하였다.

여기서 문제는, 추상화 덕에 각 구현체들의 세부 구현에 변주를 줄 순 있으나 추상화 자체의 다양화는 어떻게 할 수 있을까?

위 처럼 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(); 

the Builder Pattern


테마파크 측에서 고객들의 일정표 클래스를 개발중이다. 그러나, 고객마다 일정이 너무 다르다. 가령, 호텔을 예약할 수도 있고 안 할수도 있으며 당일치기 고객과 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); 
}

빌더 패턴을 이용하면 복잡한 객체를 생성하는 과정을 캡슐화 할 수 있으며, 여러 단계에 걸쳐 생성되도록 할 수 있다. 따라서 복합적인 구조에 자주 사용되지만 팩토리에 비해 클라이언트에 대한 도메인 지식을 더 많이 요구한다.

profile
우당탕탕 백엔드 생존기

0개의 댓글