design pattern #3 Creational pattern - Builder
- 앞의 두 factory pattern 은 products 로부터 공통의 interface 를 뽑았다면, builder 는 서로 연관이 없는 product 지만 동일한 construction process 를 사용하는 경우이다.
- configuration option을 chain 형태로 붙이고 붙인 뒤 build() 하면 결과물이 나오는 구조이다
- 여기서 특이한점은, 내가 본 builder 는 class 형태로, build() 할때까지 자기 자신 계속 return 하면서 config 설정하는거였는데 ,여기선 builder 도 interface 로 빠져있다.
- 동일한 기능을 inteface 로 묶고, 각기 다른 product 의 config 값 세팅은 director 에서 한다.
- config 별로 다른 subclass 만들기엔 너무 많은 subclass 가 생기고,
- constructor param 으로 넣기엔 너무 많은 optional field 들 때문에 null,null,null,... 들이 생길 때 유용하다.
- 동일한 기능을 갖는 차와 비행기를 각 build 한다고 하면, 차와 비행기를 만드는데 정말 수많은 속성값들이 들어가는데, 이를 위한 assembly code 를 builder class 로 따로 빼고, 차와 비행기가 공유하는 속성을 interface 로 묶을 것이다.
- 물론 직접 builder 를 사용해서 원하는 속성값을 하나씩 setting 해서 special oder 혹은 한정판 모델처럼 만들 수도 있지만, 재사용성이 높고, 반복될 수 있다면 director 에서 묶어서 관리하는것이 좋다.
공통의 config 값인 engine 과 transmission 을 interface builder 로 묶고,
public interface Builder {
void setEngine(Engine engine);
void setTransmission(Transmission transmission);
}
각각의 구체적인 Builder 를 구현한다.
public class CarBuilder implements Builder {
private Engine engine;
private Transmission transmission;
@Override
public void setEngine(Engine engine) {
this.engine = engine;
}
@Override
public void setTransmission(Transmission transmission) {
this.transmission = transmission;
}
public Car getResult() {
return new Car(engine, transmission);
}
}
public class AirplaneBuilder implements Builder{
private Engine engine;
private Transmission transmission;
@Override
public void setEngine(Engine engine) {
this.engine = engine;
}
@Override
public void setTransmission(Transmission transmission) {
this.transmission = transmission;
}
public Airplane getResult() {
return new Airplane(engine, transmission);
}
}
product class 는 아래처럼 config 들을 가진다.
public class Car {
private final Engine engine;
private final Transmission transmission;
private double fuel = 0;
public Car(Engine engine, Transmission transmission) {
//Constructor
}
//Getter, Setter
}
public class Airplane {
private final Engine engine;
private final Transmission transmission;
public Airplane(Engine engine, Transmission transmission) {
//Constructor
}
}
아래처럼 각각의 config 역시 기능을 가질수도, 단순 enum 일수도 있다.
public class Engine {
private final double volume;
private double mileage;
private boolean started;
public Engine(double volume, double mileage) {
this.volume = volume;
this.mileage = mileage;
}
public void on() {
started = true;
}
public void off() {
started = false;
}
public boolean isStarted() {
return started;
}
public void go(double mileage) {
if (started) {
this.mileage += mileage;
} else {
System.err.println("Cannot go(), you must start engine first!");
}
}
public double getVolume() {
return volume;
}
public double getMileage() {
return mileage;
}
}
public enum Transmission {
SINGLE_SPEED, MANUAL, AUTOMATIC, SEMI_AUTOMATIC
}
실제 builder 에 구체적인 값을 넣는건 director 에서 한다.
director 는 각각의 config 에 대한 설정값만 알뿐, 뭘 build 하는지는 모른다.
물론 director 없이, builder 만을 가지고 직접 원하는 값들만 세팅 할 수도 있다.
public class Director {
public void constructSportsCar(Builder builder) {
builder.setEngine(new Engine(3.0, 0));
builder.setTransmission(Transmission.SEMI_AUTOMATIC);
}
public void constructSUV(Builder builder) {
builder.setEngine(new Engine(2.5, 0));
builder.setTransmission(Transmission.MANUAL);
}
public void constructAirbus(Builder builder) {
builder.setEngine(new Engine(5000, 0));
builder.setTransmission(Transmission.AUTOMATIC);
}
public void constructBoeing(Builder builder) {
builder.setEngine(new Engine(3000, 100));
builder.setTransmission(Transmission.AUTOMATIC);
}
}client 에서는 아래와 같이 쓸 수 있다. director 에, 필요한 구체적 builder 를 넣어주면,director 가 값을 채워준다. 그럼 builder 로부터 객체를 꺼내올 수 있다.
public static void main(String[] args) {
Director director = new Director();
// Director gets the concrete builder object from the client
// (application code). That's because application knows better which
// builder to use to get a specific product.
CarBuilder builder = new CarBuilder();
director.constructSportsCar(builder);
// The final product is often retrieved from a builder object, since
// Director is not aware and not dependent on concrete builders and
// products.
Car tesla = builder.getResult();
System.out.println("Car built:\n" + tesla.getEngine());
AirplaneBuilder airplaneBuilder = new AirplaneBuilder();
// Director may know several building recipes.
director.constructAirbus(airplaneBuilder);
Airplane a380 = airplaneBuilder.getResult();
System.out.println("\nAirbus built:\n" + a380.getTranssmission());director.constructBoeing(airplaneBuilder);
Airplane boeing797 = airplaneBuilder.getResult();
System.out.println("\nBoeing built:\n" + boeing797.getTranssmission());}
Comments
Post a Comment