본문 바로가기

소프트웨어/Android/Java/Ndk

JavaEffective] Build Pattern

소스참고 : 인사이트 출판사. Java Effective 2/E. Joshua Bloch.


Java Effective의 규칙2. Build Pattern


------------------------------------------------------------

https://github.com/ManSung-Kim/mantdu/blob/master/javaEfeective/Je2BuildPettern.java


 보통 개발을 할 때 Class를 만들어놓고 final 지시자로 최초 생성자에서 한번만 값을 지정하게 만드는 경우가 더러 있었다. 그래서 원하는 생성 타입에 따라 여러 생성자를 생성해서 생성자 Overloading을 수행하였다. 한 두개의 final 변수라면 뭐 그리 문제가 되지 않지만, 그 수가 많아지면 많아질수록 계속해서 새로운 생성자 패턴을 만들어줘야 하고 버그의 위험도 높아진다.

 그런데 Build Pattern에서는 이런한 단점을 보완할 수 있는 방법을 설명하고 있다.




소스

package com.example.javaeffective;

public class Je2BuildPettern {
	private final int fCntSodium;
	private final int fCntSugar;
	private final int fCntMiwon;
	private final int fCntRice;
	
	private Je2BuildPettern (Builder builder) {
		this.fCntSodium = builder.fCntSodium;
		this.fCntSugar = builder.fCntSugar;
		this.fCntMiwon = builder.fCntMiwon;
		this.fCntRice = builder.fCntRice;
	}
	
	public String getInfo() {
		String info = "";
		if(fCntSodium!=0) {
			info += "Sodium : "+fCntSodium+"\n";
		}
		if(fCntSugar!=0) {
			info += "Sugar : "+fCntSugar+"\n";
		}
		if(fCntMiwon!=0) {
			info += "Miwon : "+fCntMiwon+"\n";
		}
		if(fCntRice!=0) {
			info += "Rice : "+fCntRice+"\n";
		}
		return info;
	}
	
	public static class Builder {
		// 필수 인자
		private final int fCntSodium;
		private final int fCntSugar;
		
		// 선택 인자
		private int fCntMiwon = 0;
		private int fCntRice = 0;
		
		/**
		 * 생성자. 필소요소인 sodium과 sugar를 초기화
		 * @param sodium
		 * @param sugar
		 */
		public Builder(int sodium, int sugar) {
			this.fCntSodium = sodium;
			this.fCntSugar = sugar;
		}
		
		/**
		 * 미원을 세팅하는 선택적인 메서드
		 * @param val
		 * @return
		 */
		public Builder miwon(int val) {
			this.fCntMiwon = val;
			return this;
		}
		
		/**
		 * 쌀을 세팅하는 선택적인 매서드
		 * @param val
		 * @return
		 */
		public Builder rice(int val) {
			this.fCntRice = val;
			return this;
		}
		
		/**
		 * 최종적으로 build()를 통해 immutable(변경불가능) 클래스 생성
		 * @return
		 */
		public Je2BuildPettern build() {
			return new Je2BuildPettern(this);
		}
	}
}

소스끝


이용하고자 하는 Class의 생성자를 private로 줘서 밖에서는 생성을 할 수 없고 오직 내부에 선언된 Builder Class의 build()를 이용해야만 인스턴스화를 시킬 수 있다. 


소스

// 필수 인자
		private final int fCntSodium;
		private final int fCntSugar;
		
		// 선택 인자
		private int fCntMiwon = 0;
		private int fCntRice = 0;
		
/**
		 * 생성자. 필소요소인 sodium과 sugar를 초기화
		 * @param sodium
		 * @param sugar
		 */
		public Builder(int sodium, int sugar) {
			this.fCntSodium = sodium;
			this.fCntSugar = sugar;
		}

소스끝



Builder Class의 생성자필수로 들어가야 하는 멤버변수들만 인자로 받아들이고, 선택적인 인자는 builder 내부에서 0으로 초기화 하고 있다. 그래서 개발자가 원하는 옵션들만 값을 초기화 할 수 있어서 무식하게 생성자를 늘리지 않고도 final 변수의 초기화가 가능해졌다. 와우




mText = (TextView)findViewById(R.id.textBoard);
		
		Je2BuildPettern food1 = new Je2BuildPettern.Builder(100, 99).build();
		mText.setText("food1 info\n"+food1.getInfo());
		Je2BuildPettern food2 = new Je2BuildPettern.Builder(100, 99)
										.miwon(50)
										.rice(35)
										.build();
		mText.append("\n\nfood2 info\n"+food2.getInfo());


위의 소스는 위에서 만든 Je2BuildPattern을 이용해서 메뉴 정보를 출력하는 소스의 일부이다.




책에 따르면 빌드패턴은 "인자가 많은 생성자나 정적 팩토리가 필요한 클레스, 인자가 선택적인 상황" 에서 유리하다고 하고있다. 프로그램의 확장성을 고려한다면 처음부터 빌드패턴을 사용하는것도 좋다고 한다. 



참고. 책에서 설명하는 비슷한 기능을 하는 생성자 패턴의 종류

점증적 패턴 - 무식하게 생성자 늘리기

자바빈 패턴 - 생성자는 별로 수정하지 않고 요소에 따라 get/set 매서드를 이용

빌더 패턴 - stastic build class를 이용해서 개발자 선택에 따라 final 변수 컨트롤