Etc/2020

[Java] 객체 생성 패턴

메바동 2020. 6. 9. 10:59
728x90

Java 객체 생성 방법

1. 점층적 생성자 패턴

점층적 생성자 패턴을 만드는 방법은 다음과 같다.

  1. 필수 인자를 받는 필수 생성자 를 하나 만든다.
  2. 1개의 선택적 인자를 받는 생성자를 추가한다.
  3. 2개의 선택적 인자를 받는 생성자를 추가한다.
  4. ...
  5. 모든 선택적 인자를 받는 생성자를 추가한다.
// 점층적 생성자 패턴 코드의 예 public class Member { private final String name; // 필수 인자 private final String age; // 선택적 인자 private final String location; // 선택적 인자 // 필수 생성자 public Member(String name) { this(name, "나이 비공개", "지역 비공개"); } // 1개의 선택적 인자를 받는 생성자 public Member(String name, String age) { this(name, age, "지역 비공개"); } // 모든 선택적 인자를 다 받는 생성자 public Member(String name, String age, String location) { this.name = name; this.age = age; this.location = location; } }
  • 장점

    new Member("홍길동", "나이 비공개", "지역 비공개") 와 같은 호출이 빈번하게 일어난다면, new Member("홍길동") 으로 대체할 수 있다.

  • 단점

    • 다른 생성자를 호출하는 생성자가 많으므로, 인자가 추가되는 일이 발생하면 코드를 수정하기 어렵다.
    • 코드 가독성이 떨어진다. 특히, 인자 수가 많을 때 호출 코드만 봐서는 의미를 알기 어렵다.
    • 설정할 필요가 없는 필드에도 인자를 전달해야 하는 경우가 생긴다.

2. 자바빈 패턴

점층적 생성자 패턴의 대안으로 자바빈 패턴(JavaBeans Pattern)이 있다.

public class CafeMenu { private int coffee = 1; // 필수 인자 private int beverage = 1; // 필수 인자 private int dessert = 0; // 선택적 인자 private int bakery = 0; // 선택적 인자 private int drinks = 0; // 선택적 인자 public CafeMenu() {} // setter 메서드 정의 public void setCoffee(int coffee) { this.coffee = coffee; } public void setBeverage(int beverage) { this.beverage = beverage; } public void setDessert(int dessert) { this.dessert = dessert; } public void setBakery(int bakery) { this.bakery = bakery; } public void setDrinks(int drinks) { this.drinks = drinks; } }

자바빈 패턴은 setter 메서드를 사용해서 인자에 말그대로 set 해주는 방법이다. CafeMenu 객체를 만들고 싶다면

public class GetCafeMenu { public static void main(String[] args) { CafeMenu starbucks = new CafeMenu(); starbucks.setCoffee(10); starbucks.setBeverage(30); starbucks.setDessert(10); starbucks.setBakery(20); starbucks.setDrinks(5); } }

위와 같이 setter 메서드를 통해서 객체를 생성할 수 있다.

  • 장점

    • 각 인자의 의미를 파악하기가 쉽다.
    • 복잡하게 여러 개의 생성자를 만들지 않아도 된다.
  • 단점

    • 1회의 호출로 객체 생성이 끝나지 않기 때문에 객체 일관성(consistency)이 깨진다.
    • setter 메서드가 있으므로 변경 불가능(immutable) 클래스를 만들 수가 없다.
      • 스레드 안정성을 확보하려면 점층적 생성자 패턴보다 많은 일을 해야 한다.

3. 빌더 패턴

빌더 패턴(Builder Pattern)은 점층적 생성자 패턴의 안정성과 자바진 패턴의 가독성을 결합한 패턴이다.

public class CafeMenu { private final int coffee; // 필수 인자 private final int beverage; // 필수 인자 private final int dessert; // 선택적 인자 private final int bakery; // 선택적 인자 private final int drinks; // 선택적 인자 public static class Builder { private final int coffee; // 필수 인자 private final int beverage; // 필수 인자 private int dessert; // 선택적 인자 private int bakery; // 선택적 인자 private int drinks; // 선택적 인자 // 필수 인자 생성자 public Builder(int coffee, int beverage) { this.coffee = coffee; this.beverage = beverage; } // 선택적 인자는 Builder 타입의 함수로 만든다. public Builder dessert(int val) { dessert = val; return this; } public Builder bakery(int val) { bakery = val; return this; } public Builder drinks(int val) { drinks = val; return this; } // CafeMenu 타입으로 만들기 함수 public CafeMenu build() { return new CafeMenu(this); } } private CafeMenu(Builder builder) { coffee = builder.coffee; beverage = builder.beverage; dessert = builder.dessert; bakery = builder.bakery; drinks = builder.drinks; } }
  1. CafeMenu 객체의 각 인자(coffee, beverage, dessert, bakery, drinks)가 final로 선언되어있으므로 변경 불가능하다. (immutable)
  2. 모든 인자의 기본값(default value)이 한 곳에 모여있다.
  3. Builder 객체 내에서 필수 인자는 final로 선언함으로써 필수적으로 생성자를 통해 생성한다.
  4. Builder 객체 내에서 선택 인자를 기본값으로 초기화하고 Builder형으로 자기 자신을 반환한다.
  5. Builder 객체 내의 build 메서드로 빌더 객체 자신을 CafeMenu 형으로 반환한다.

빌더 패턴을 이용한 객체 생성은 다음과 같이 할 수 있다.

public class getCafeMenu { public static void main(String[] args) { CafeMenu.Builder builder = new CafeMenu.Builder(20, 10); builder.drinks(5); CafeMenu coffeeBean = builder.build(); // 한 줄로 이어서 작성 가능하다. CafeMenu starbucks = new CafeMenu.Builder(30, 20).bakery(10).dessert(5).drinks(10).build(); } }
  • 장점

    • 각 인자가 어떤 의미인지 알기 쉽다.
    • setter 메서드가 없으므로 변경 불가능 객체를 만들 수 있다.
    • 한 번에 객체를 생성하므로 객체 일관성이 깨지지 않는다.
    • build() 함수가 잘못된 값이 입력되었는지 검증하게 할 수도 있다.
  • 단점

    • 빌더 객체를 생성해야 하기 때문에 코드가 길어질 수 있다.
    • 빌더 객체 때문에 오버헤드가 일어날 가능성은 적지만, 성능이 중요한 상황에서는 잘 고려해서 사용해야 한다.

빌더 패턴은 인자가 많은 생성자정적 팩토리가 필요한 클래스 설계할 때 , 특히 대부분의 인자가 선택적 인자인 상황 에 유용하다.

Lombok @Builder

빌더 패턴은 Lombok의 @Builder 어노테이션으로 쉽게 사용할 수 있다.

@Builder 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 class GetNutritionFacts { public static void main(String[] args) { NutritionFacts.NutritionFactsBuilder builder = NutritionFacts.builder(); builder.calories(230); builder.fat(10); NutritionFacts facts = builder.build(); // 또는 NutritionFacts facts = NutritionFacts.builder().calories(230).fat(10).build(); } }




Reference

728x90