Josh Bloch 아저씨
최근에 포스팅했던 GoF의 빌더 패턴과는 관점이 다른 접근방법이다. GoF의 빌더패턴은 오브젝트의 생성과 조립 과정을 분리하는 과정에 중점을 두었다면,이펙티브 자바의 빌더 패턴은 생성자(Constructor)가 많을 경우 또는 오브젝트 생성 후 변경 불가능한 불변 오브젝트가 필요한 경우, 불변 오브젝트를 생성하여 오브젝트의 일관성(Consistency),변경 불가능(immutable)을 실현하여 코드의 가독성과 불변성,일관성을 유지하도록 하는것에 중점을 둔다.오늘은 이펙티브 자바에서 소개한 빌더패턴을 다룬다.
Person
public class Person { // 필수 맴버변수 private String gender; private String name; private int age; // 선택적 맴버변수 private String lang; private double height; // 필수 맴버변수 생성자 Person(String name,String gender,int age){ this.name = name; this.gender = gender; this.age = age; } // 모든 맴버 변수를 설정하는 생성자 Person(String name,String gender,int age,String lang,double height){ this.name = name; this.age = age; this.gender = gender; this.lang = lang; this.height = height; } // 선택적 맴버변수 생성자(lang) Person(String name,String gender,int age,String lang){ this.name = name; this.gender = gender; this.age = age; this.lang = lang; } // 선택적 맴버변수 생성자(height) Person(String name,String gender,int age,double height){ this.name = name; this.gender = gender; this.age = age; this.height = height; } }
Main
public class Main { public static void main(String[] args) { // 필수 맴버변수 생성 Person person = new Person("KIM","M",10); // 선택적 맴버변수(lang) 생성 Person person1 = new Person("LEE","F",20,"java"); // 선택적 맴버변수(height) 생성 Person person2 = new Person("PARK","M",22,170); // 모든 맴버변수 생성 Person person3 = new Person("CHOI","F",33,"C",166); } }
점층적 생성자 패턴은 오브젝트의 일관성(Consistency)와 불변성(immutable)이 유지되지만 코드에서 보다시피 Person의 맴버 변수가 하나라도 늘어나는 순간 코드 작업량이 많아진다. 또한 Main 클래스에서 보다시피 오브젝트 생성시 가독성도 좋지않다. 그래서 나온것이 바로
Person
public class Person { private String name; private String gender; private int age; private String lang; private double height; public void setName(String name) { this.name = name; } public void setGender(String gender) { this.gender = gender; } public void setAge(int age) { this.age = age; } public void setLang(String lang) { this.lang = lang; } public void setHeight(double height) { this.height = height; } }
Main
public class Main { public static void main(String[] args) { Person person = new Person(); person.setName("KIM"); person.setGender("M"); person.setAge(22); person.setLang("Java"); person.setHeight(170); } }
코드의 작업량도 줄어들고 가독성도 확실히 좋아졌다. setter 메소드에서 어떤 해당 파라미터가 어떤 변수인지 메소드를 통하여 확인이 가능하다. 하지만 최초 생성 이후 변경이 불가한 오브젝트로 만들수는 없다 언제든지 setter 메소드를 사용하여 변수 값을 바꿀 수 있기 떄문. 다시말해 setter로 떡칠을 해야하니 일관성, 변경 불가능 원칙이 깨져버렸다. 이런 경우에는 후에 스레드 안정성을 위해 추가 작업이 필요하기도 하다.
Person
public class Person { private final String name; private final String gender; private final int age; public static class Builder{ private final String name; private final String gender; private final int age; private String lang; private double height; // 필수 맴버변수 public Builder(String name,String gender,int age){ this.name = name; this. gender = gender; this.age = age; } public Builder lang(String lang){ this.lang = lang; return this; } public Builder height(double height){ this.height = height; return this; } public Person build(){ return new Person(this); } } private Person(Builder builder){ this.name = builder.name; this.gender = builder.gender; this.age = builder.age; } }
Main
public class Main { public static void main(String[] args) { Person person = new Person.Builder("KIM","M",22) .lang("M") .height(170) .build(); } }
Person 내부에 Builder 라는 static class를 만들어 두고 내부 setter에서 맴버 변수를 저장 후 build 메소드를 통해 Person 오브젝트를 생성후 리턴한다.Main 메소드에서 각 데이터가 무엇인지 한번에 알아볼 수 있어 코드의 가독성도 좋아지고 외부에서 맴버 변수값을 변경할 수 없어 일관성과 변경 불가능 원칙 두마리 토끼를 다 잡았다. 하지만 코드량이 많아지는 문제가 있고 성능에 민감한 상황에서는 조심해서 써야한다는 단점 또한 존재한다. 처리해야할 맴버변수가 많거나 변경 불가능을 적용해야 할 경우에는 빌더패턴을 선택하는 편이 좋을듯.
출처: Effective Java