프로그래머의 삶 Programmer's Life/Java!!

Static 클래스를 유연하게 사용하기~ ( Builder 패턴 사용 )

Oliver's World 2009. 12. 11. 12:20
728x90


public class NutritionFacts {
 private final int servingSize;
 private final int servings;
 private final int calories;
 private final int fat;
 private final int sodium;
 private final int carbohydrate;
 
 public NutritionFacts (){}
...
...
..
 public NutritionFacts (int servingSize, int servings, int calories, int fat, int sodium, int carbohydrate){
 this. 어쩌고 = 저쩌고;
 this. 어쩌고 = 저쩌고;
 ...
...
..
}

위의 방법은 텔리스코핑 생성자 패턴 방식으로서 신축성이 좋지 않다....너무 당연한가?
.....생성할때 몇번째 인자가 뭔지 어떻게 아는가?-_-; 파라미터가 길어질수록 점점 더 난감해진다...또한 보면 알겠지만 가독성이 떨어진다.... 그러므로 나온게

자바빈즈 패턴....
이것은 다 알 것이다. 게터 세터 패턴~ 이클립스에서 자동으로 생성할 수 있는 간략한 패턴이다.
그러므로 코드는 생략!!
 자바빈즈 패턴의 단점은 여러 번의 메소드 호출로 나누어져 인스턴스가 생성되므로 생성과정동안 객체가 일관된 상태를 유지 못할 수 있다. 그러므로 자바빈즈 패턴은 불변 클래스를 만들 수 있는 가능성을 배제하므로 스레드에서 안전성을 유지하려면 프로그래머의 추가적인 노력이 필요하다는 단점이 있다.
  객체 생성이 완전히 끝났을 때 그 객체를 동결하고 완전하게 되기 전까지는 사용할 수 없도록 함으로써 단점을 줄일 수 있다. 그러나 이 방법은 매우 어렵다고 한다-_-;;;;(해볼 생각도 없었다...) 더욱이 런타임 에러를 유발할 수도 있다. 동결시키는 메소드를 객체 사용 전에 프로그래머가 호출 했는지 컴파일러가 확인할 수 없기 때문이다...

따라서 나온게 빌더 패턴이다.
 빌더 패턴은 원하는 객체를 바로 생성하는 대신 클라이언트는 모든 필수 매개변수를 갖는 생성자를 호출하여 빌더 객체를 얻는다. 그 다음 빌더 객체의 세터 메소드를 호출하여 필요한 선택 매개변수들의 값을 설정한다. 그리고 클라이언트는 매개변수가 없는  build() 메소드를 호출하여 불변 객체를 생성하는 것이다. 말 그대로 불변 객체이므로 생성 후 상태 변화가 없다.

아래는 빌더패턴을 활용한 코드의 예 이다.

public class NutritionFacts {
 private final int servingSize;
 private final int servings;
 private final int calories;
 private final int fat;
 private final int sodium;
 private final int carbohydrate;

 public static class Builder {
  private final int servingSize;
  private final int servings;

  private int calories = 0;
  private int fat = 0;
  private int sodium = 0;
  private int carbohydrate = 0;

  public Builder(int servingSize, int servings) {
   this.servingSize = servingSize;
   this.servings = servings;
  }

  public Builder calories(int val) {
   calories = val;
   return this;
  }

  public Builder fat(int val) {
   fat = val;
   return this;
  }

  public Builder carbohydreate(int val) {
   carbohydrate = val;
   return this;
  }

  public Builder sodium(int val) {
   sodium = val;
   return this;
  }

  public NutritionFacts build() {
   return new NutritionFacts(this);
  }

 }

 private NutritionFacts(Builder builder) {
  servingSize = builder.servingSize;
  servings = builder.servings;
  calories = builder.calories;
  fat = builder.fat;
  sodium = builder.sodium;
  carbohydrate = builder.carbohydrate;
 }

 public int getServingSize() {
  return servingSize;
 }

 public int getServings() {
  return servings;
 }

 public int getCalories() {
  return calories;
 }

 public int getFat() {
  return fat;
 }

 public int getSodium() {
  return sodium;
 }

 public int getCarbohydrate() {
  return carbohydrate;
 }

}

빌더는 자신이 생성하는 객체의 클래스에 포함된 static 멤버 클래스이다. 위의 클래스는 불변 클래스이며, 모든 매개변수의 디폴트 값이 한군데 모아져 있다. 빌더의 세터 메소드들은 연속적으로 여러번 호출 될 수 있도록 빌더 자신의 객체를 반환한다.

NutritionFacts cocaCola=new NutritionFacts.Builder(240, 0).calories(100).sodium(35).carbohydreate(27).build();

이렇게 생성을 한다. 보면 쉽게 코드를 작성할 수 있고 가독성 또한 좋다. 생성자처럼 빌더는 자신의 매개변수에 불변 규칙을 적용할 수 있고 build 메소드는 그런 불변 규칙을 검사할 수 있다. 
  중요한 것은 매개변수들의 값이 빌더로부터 객체에 복사된 후 빌더의 필드가 아닌 객체의 필드에 대해 불변 규칙 검사가 수행되는 것이다. 만약 위배 될 경우에는 build 메소드에서 IllegalStateException 예외를 발생시킨다.

 빌더 패턴은 유연성이 좋다. 하나의 빌더는 여러 개의 객체를 생성하는데 사용될 수 있으며 이런 과정 중 빌더 매개변수는 다양하게 조정될 수 있다. 빌더를 사용하면 일부 필드의 값을 자동으로 설정할 수 있다. 예를들어 하나의 객체가 생성될 때마다 자동적으로 증가하는 일련번호? ㄷㄷㄷ;
 단점이라면 어떤 객체를 생성하기 위해서는 우선 그것의 빌더를 생성해야 한다는 것이다. 이는 빌더 객체의 생성 비용이 눈에 띄게 클 정도는 아니지만 성능이 매우 중요한 상황에서는 문제가 될 수도 있다.

728x90