오늘 포스팅 할 내용은 Builder 패턴이다.

Class 를 설계하다 보면,

필수로 받아야 할 인자들이 있고,

선택적으로 받아야 할 인자들이 있는 경우가 있다.

필수적으로 받아야 할 인자를 받기 위해

생성자의 매개변수를 넣는다.

또한 선택적 인자를 받기 위해 추가적인

생성자를 만든다.


public class PersonInfo{

  private String name;         //필수적으로 받야할 정보

  private int age;                //선택적으로 받아도 되는 정보

  private int phonNumber;   //선택적으로 받아도 되는 정보

  public PersonInfo(String name){

     this.name = name;

  }

  public PersonInfo(String name, int age){

    this.name = name;

    this.age = age;

  }

  public PersonInfo(String name, int age, int phonNumber){

    this.name = name;

    this.age = age;

    this.phonNumber = phonNumber;

  }

}

이러한 상황을 Effective Java 2/E 에선 점층적 생성자 패턴이라 말한다.

하지만, 이러한 패턴은 잘 동작하지만, 몇가지 단점들이 존재한다.

첫번째로, 인자들이 많아 질 수록 생성자의 숫자 역시 많아진다.

두번째로, 매개변수의 이름으로 원하는 정보를 설명하지만,

같은 자료형은 실수로 바꿔 넣는 실수가 생길 수 있다.

예를 들어, 위의 예시에서 age와 phonNumber를 서로 바꿔 넣는 경우이다.

세번째로, 읽기 어려운 코드가 된다.

PersonInfo personInfo = new PersonInfo("Mommoo", 12, 119);

위의 코드는 매개변수의 정보를 설명할 수 없으므로,

personInfo 객체가 어떤 인자들이 들어갔는지 알기 위해선

PersonInfo 클래스의 매개변수 이름이나, 주석을 참고해야 한다.


이러한 단점들을 보완하기 위해, 자바 빈즈(beans) 패턴이 대안점으로 나온다.

해당 패턴은 웹프로그래밍을 할 때, 자주 쓰는 패턴이다.

public class PersonInfo{

  private String name;         //필수적으로 받야할 정보

  private int age;                //선택적으로 받아도 되는 정보

  private int phonNumber;   //선택적으로 받아도 되는 정보

  public PersonInfo(){

  }

  public void setName(String name){

    this.name = name;

  }

  public void setAge(int age){

    this.age = age;

  }

  public void setPhonNumber(int phonNumber){

    this.phonNumber = phonNumber;

  }

}

생성자의 단점으로 꼽혔던 가독성이 해당 패턴을 사용하는 경우, 어느정도 해결이 된다.

PersonInfo personInfo = new PersonInfo( );

personInfo.setName("Mommoo");         // 이름을 넣는 메서드

personInfo.setAge(12);                       // 나이를 넣는 메서드

personInfo.setPhonNumber(119);          // 전화번호를 넣는 메서드

자바빈즈 패턴은 코드량이 늘어나는 단점이 존재한다. 

하지만 그것보다 더욱 문제가 되는 것은 객체 일관성이 깨진다.

객체 일관성이 깨진다는 것이란, 한번 객체를 생성할때, 그 객체가 변할 여지가 존재한다는 뜻이다.

즉, set메서드는 언제 어디서든 사용 할 수 있다는 장점이 있지만, 객체의 불변성이 깨진다.

스레드 작업에 큰 단점이 될 수 있을 뿐더러, 컴파일러 오류는 아니지만, 우리가 원하지 않는

결과물이 만들어 질 수 있는것이다.


점층적 생성자패턴과 자바빈즈 패턴의 장점을 섞은것이 빌더패턴이다.

정보들은 자바 빈즈 패턴처럼 받되, 데이터 일관성을 위해 정보들을 다 받은후에 객체생성을 해준다.


public class PersonInfo{

  private String name;         //필수적으로 받야할 정보

  private int age;                //선택적으로 받아도 되는 정보

  private int phonNumber;   //선택적으로 받아도 되는 정보

  private PersonInfo(){

  }

  public static class Builder{

    private String name;

    private int age;

    private int phonNumber;

   

   public Builder(String name){

      this.name = name;

    }

   public Builder setAge(int age){

 this.age = age;

 return this;

   }

   public Builder setPhonNumber(int phonNumber){

 this.phonNumber = phonNumber;

 return this;

   }

   public PersonInfo build(){

   PersonInfo personInfo = new PersonInfo( );

   personInfo.name = name;

   personInfo.age = age;

   personInfo.phonNumber = phonNumber;

   return personInfo;

 }

}

이러한 빌더 패턴은 아래와 같이 사용한다.

PersonInfo personInfo = new Builder("Mommoo").setAge(12).setPhonNumber(119).build( );

setter에 리턴자료형을 Builder 객체를 지정함으로써, 메서드 체이닝 기법을 적용했고,

정보를 다 넣은 경우 build( ) 메서드로 객체생성을 한다.

build( ) 메서드를 쓴 이후 에는 PersonInfo 클래스의 멤버변수를 변경 할 수 있는 방법은

리플렉션 기법빼곤 존재하지 않는다. 따라서, 데이터 일관성, 객체불변성 등을 만족시킨다.

또한 코드 가독성 역시 올라간다.


하지만 이러한 빌드 패턴의 단점은 많은 코드량이 필요할 뿐더러,

Builder라는 객체를 하나더 만드는 것이므로, 때에 따라선 성능이 낮아질 수 있다.


Class 설계할때, 받아야할 인자들이 많거나, 선택적 인자들이 많거나 또한, 추가될 인자들이 많은 경우

에 Builder 패턴을 고려한다면, 좀 더 나은 설계를 할 수 있을 것이다.


포스팅이 도움 되셨다면, 커피 한잔 후원해주세요!
더 좋은 포스팅 작성에 큰 힘이 됩니다.

Buy me a coffeeBuy me a coffee

오늘 포스팅 할 내용은 정적 팩토리 메서드에 관한 내용이다.


내용 의 상당수는 (Effective Javaa 2/E - 2판)의 도움을 받았고,


개인적인 의견을 덧 붙여 해당 내용을 풀어 설명할 것 이다.


인스턴스를 생성하기 위해서는 Class 원칙에 따라, 생성자를 호출해야 한다.


Person p = new Person( );


Class의 이름은 캡슐화를 위해 Class의 내용을 아우를 수 있는 이름을 짓기 마련이다.


따라서, 생성자의 이름만을 봐서는 감이 안올때가 있다.


또한, 여러가지 매개변수를 받는 생성자를 만들 때도, 생성자의 매개변수를


설명할 방법이 딱히 없다. 하지만, 정적 팩토리 메서드의 활용 여부에 따라 


코드의 가독성을 높일 수 있다. (여기서 정적은 static을 가리킨다.)


그로인해 아래와 같은 장점들이 존재한다.


정적 팩토리 메서드는 해당 클래스 인스턴스에 이름을 지어줄 수 있다.


public class Person{

  private String name;

  private Person( ){

  }

  

  public static Person newInstance(){

    return new Person( );

  }

  public static Person newInstance(String name){

    this.name = name;

    return new Person( );

  }


  public static void main(String[] args){

    Person p  = Person.newInstance(); // 평범한 인스턴스 생성

    Person p2 = Person.newInstanceByName("Mommoo"); // name속성을 가지는 인스턴스 생성 

  }

}


인스턴스의 갯수를 제어 할 수 있다. 이를 응용한 것이 싱글톤 패턴 이다.


public class Person{

  private final Person p = new Person( );


  private Person( ){

  }


  public static Person getInstance(){

    return p;

  }


  public static void main(String[] args){

    Person p = Person.getInstance( ); // 이미 존재하고 있는 객체 리턴

  }

}


생성자엔 반환값이 없지만, 필요시에 적절한 반환값을 만들어 줄 수 있다.


public class Person{

  private Person( ){ 

  }


  public static class Doctor{

    private Doctor(){

    }

  }


  public static Doctor getDoctor( ){

    return new Doctor( );

  }


  public static void main(String[] args){

    Doctor doctor = Person.getDoctor( );

  }

}


위의 예시는 반환값을 특정 Class로 했지만, 설계에 따라 어떠한 반환값이든 가능하다.


생성자를 감싸는 구조이므로, 유지보수와 성능 개선에 좋다.


위의 예시중 getDoctor 메서드가 new Doctor( )란 새로운 인스턴스를 리턴하지만,


개발자가 싱글톤으로 리턴하는 코드로 변경한다고 가정해보자.


public class Client{


  public static void main(String[] args){

    /** 클라이언트는 변경사항이 없다. 

      *  getDoctor( ) 메서드가 내부적으로 어떻게 변경되는지, 어떻게 쓰이는지 알 필요조차 없다.

      *  클라이언트는 단순히 Doctor라는 객체를 쓰는데만 집중한다. 

      */

    Doctor doctor = Person.getDoctor( );  

  }

}


public class Person{

  private Person( ){ 

  }


  public static class Doctor{

    

    private final Doctor d = new Doctor( );


    private Doctor(){

    }

  }


  public static Doctor getDoctor( ){

    return Doctor.d;   //new Doctor( ) 에서 Doctor.d 로 변경되었다.

  }


  public static void main(String[] args){

    Doctor doctor = Person.getDoctor( );

  }

}


이러한 장점들이 있는 반면, 단점들은 무엇이 있을까? 


먼저, 정적 팩토리 메서드의 사용을 권하기 위해서 private 제어자를 사용해 생성자를 막는다.


그로인해, 해당 클래스를 상속 할 수 없다. 왜냐하면 상속받을 생성자가 없기 때문이다.


또한, 다른 정적 팩토리 메서드는 다른 메서드와 크게 구분이 가지 않는다. 


만약 생성자를 이용한다고 가정해보자.


클래스를 설계 할때는 클래스 이름과 동일하고 반환값 자리가 아예 존재 하지 않는


메서드라는 특정 표식이 있고, 클래스를 쓸때도, new 생성자( ); 처럼


new 연산자를 통해 생성하므로, 생성자의 느낌을 물씬 주지만


정적 팩토리 메서드는 다른 정적 메서들과 차이점이 존재하지 않는다.


Class를 설계한 개발자 조차도 주석을 달아 놓지 않으면, 후에 다시 자신이 설계한 클래스를


봤을 때, 잊어먹 을 수 있다. 


결국 정적 팩토리 메서드를 활용한 클래스를 사용할때는, API나 사용법을 들어야한다.


(물론 생성자를 사용할 때도 어느정도 API나 사용법등을 참고해야할 때가 있다.)




'Java [Effective Java 2_E]' 카테고리의 다른 글

JAVA - 열거자료형 Enum 사용하기(1)  (0) 2016.11.11
Java - Builder 패턴 적용하기.  (3) 2016.10.08

포스팅이 도움 되셨다면, 커피 한잔 후원해주세요!
더 좋은 포스팅 작성에 큰 힘이 됩니다.

Buy me a coffeeBuy me a coffee

+ Recent posts