빌더 패턴

반응형
반응형

오랜만에 작성하는 것 같다. 프록시 이후로 9일만에 작성하는데.... 

아무튼 빌더 패턴은 객체의 생성을 맡는다. 

객체의 생성하면 가장 먼저 떠오르는 패턴은 팩토리 패턴이지만... 

팩토리 패턴과는 또 다른 매력으로 다가온다.

 

사실 빌더 패턴이 만들어진 이유는 정확히 뭔지는 모르지만

하나 확실한것은 

     간결함과 뚜렷함을 얻을 수 있다. 물론 간결함 같은 경우는 클래스가 굉장히 가벼우면 더 간결하다고 느낄 수 있지만 만 말이다. 아무튼 계속 설명하면..

 

 첫 번째 이유는 

public class Test {
	
	private String name;
	private int age;
	private String content;
	private String subject;
	
	public Test(String name, int age, String content, String subject) {
		// TODO Auto-generated constructor stub
	}
	
	public Test(String name, int age, String content) {
		// TODO Auto-generated constructor stub
	}
	
	
	public Test(String name, String content, String subject) {
		// TODO Auto-generated constructor stub
	}
	
	public Test(int age, String content, String subject) {
		// TODO Auto-generated constructor stub
	}
	
	public Test(int age) {
		// TODO Auto-generated constructor stub
	}
	
	
}

이런 클래스가 있다고 가정하자. 

총 맴버 변수가 4개인데

4개를 전부 사용할 경우,  = 1가지 (1,2,3,4)

3개만 사용할 경우  = 3가지 (1,2,3) (1,2,4) (1,3,4) (2,3,4) , 

2개만 사용할 경우  = 6가지 (1,2) (1,3) (1,4) (2,3) (2,4) (3,4)

1개만 사용할 경우 = 4가지

이것 만 계산한다고 가정했을때 15가지의 경우의 수가 나온다. 이 경우같은 경우는 맴버 변수가 4개 밖에 없기 때문에 

그리 크다고 느껴지지 않을 수 있지만 맴버 변수가 10개이며 이 수치는 어마어마하게 증가하게 된다. 

하지만 실무에서는 첫 번째 이유때문에 빌더를 사용하지는 않을 것 같다. 왜냐하면 최대한 클래스를 줄이면 되기 때문이다.

 

두 번째 이유는 뚜렷함이다. 자바의 고질적인 문제점 중 하나가 생성자에 무엇을 넣었는지 알 수 없다. 이게 무슨 말이냐면  

 

Test(1,2,3,4)

이런 클래스에 이런 값을 추가한다고 가정하자. 근데 1이 뭔지 2는 또 뭔지 3은 또 뭔지

정확히 알 수가 없다. 지금은 ide(인텔리제이)가 이에 대한 해결방법을 주긴 하지만, 그것도 메소드에 마우스를 대는등 확인 동작이 있어야만 어떤 값이 넣는지 확인할 수 있다. 그 외방법은 불가능하다. ( 타 언어에서는 생성자에서 어떤 값이 들어갔는지 눈으로 확인 할 수 있는 방법을 제공한다)

 

그렇다면 어떻게 하면 뚜렸하게 값을 확인 할 수 있을까? 바로 빌더 패턴을 이용하면 된다.

new Test().setAge(1)
          .setContent("hello")
          .build();

(위 코드와 실제 코드는 다를 수 있습니다.)

 

 이런식으로 입력할 수 있으면 1이 뭔지 hello가 뭔지 단범에 알 수 있다. 심지어 특별한 확인 작업이 필요없이 두 눈으로 어디에 뭐가 들어갔는지 알 수 있다.

아마 실무에서는 이러한 이유때문인지는 모르겠지만, 하나 확실한 것은 이 방법을 사용하면 1이 뭔지 content에 뭐가 들어갔는지 확실하게 알 수 있다.

 

그러면 빌더 패턴이라는 걸 어떻게 만들까?

 

가장 간단하게 만드는 방법은 롬복의 @Builder을 이용하는 방법이 있다.

 

하지만 그전에 어떻게 해서 되는지 이해를 해야할 것 같다,

다시 위 코드를 가져오자.

new Test().setAge(1)
          .setContent("hello")
          .build();

보면 체이닝으로 연결이 되어있다. 체이닝이 되있다는 소리는 서로 연관이 되있다는 이야기다.

다시 말해 동등 관계가 아니면 하위관계라는 뜻이다. 

그러면 동등일까? 하위일까? 바로 동등이다. 만약 하위로 만들게 되면 끊임없이 하위로 내려가는데 이렇게 만들면 무한으로 동작하지 않는다. 뭐 무한으로 가지는 않겠지만, 언젠가는 끝이 보이고 말것이다.

 

드러니까 동등관계다. new Test 나 setAge나 setContent나 build나 전부 같은 클래스를 리턴해준다. 그렇지 않으면 체이닝으로 연결할 수 없다. 근데 더 생각해야 되는 문제가 있다. 바로 build다 다른 부분은 파라미터가 존재하는데 (test제외) 이 부분은 파라미터가 들어 있지않는다. 분명 같은 클래스를 리턴해주는건 분명한데. build를 작성하자마자 build가 종료되었다. 분명 무슨 조치가 있는게 분명하다.

 

자 이제 본격적으로 빌더 패턴을 만들어 보자.

일단 set으로 도배했다.

public class Test {
	
	private String name;
	private int age;
	private String content;
	private String subject;
	
	
	public void setName(String name) {
		this.name = name;
	}
	
	public void setAge(int age) {
		this.age = age;
	}
	
	public void setContent(String content) {
		this.content = content;
	}
	
	public void setSubject(String subject) {
		this.subject = subject;
	}
	
	
}

그 다음에는 같은 클래스를 리턴 시켜줘야 하기 때문에 

package builder;

public class Test {
	
	private String name;
	private int age;
	private String content;
	private String subject;
	
	
	public Test setName(String name) {
		this.name = name;
		return this;
	}
	
	public Test setAge(int age) {
		this.age = age;
		return this;
	}
	
	public Test setContent(String content) {
		this.content = content;
		return this;
	}
	
	public Test setSubject(String subject) {
		this.subject = subject;
		return this;
	}
	
}

return이 this이므로 이 무조건 이 클래스를 리턴 된다. 근데 왜 this일까?

그건 바로 Test로 리턴 시켜줘도 무방하긴 하지만 그렇게 되면 Test클래스를 재 생성해줘야 하기 때문에 이를 방지하고자 this를 추가해주었다. this를 추가해주면 이 클래스 전체를 뜻한다.

근데 아직도 build가 없다. 이는 어떻게 만들까?

	public Test bulid() {
		return this;
	}

 이러면 되는걸까? 한 번 실행해보자. 

정상적으로 나오는것을 확인 했다. 하지만 문제가 발생했다. 

build의 빌더 패턴의 끝인데 내가 만든건 build뒤에도 값이 추가됨을 확인했다. 

build뒤에는 작성할 수 없다고 만들어야 하는데 무슨 뾰족한 방법이 없을까?

 

Builder이라는 inner클래스를 만들어 해결해봅시다.

static class Builder {
		
	private String name;
	private int age;
	private String content;
	private String subject;
		
	public Builder setName(String name) {
		this.name = name;
		return this;
	}
	
	public Builder setAge(int age) {
		this.age = age;
		return this;
	}
	
	public Builder setContent(String content) {
		this.content = content;
		return this;
	}
	
	public Builder setSubject(String subject) {
		this.subject = subject;
		return this;
	}
	

	
}

Test에서 Builder로 바로 사용하기 위해서는 Test안에 있어야 하므로 이너 클래스를 활용했고, 메모리상에 바로 접근하기 위해 static으로 만들었습니다. 직접적으로 사용할리가 없기 때문에 static으로 설정했습니다.

 

근데 이렇게 하면 결과를 도출할수가 없습니다. 이제 빨간줄이 뜹니다. 위처럼 build가 필요할듯 싶습니다.

public Test bulid() {
	return new Test(this);
}

이런식으로 작성되었는데 이는 이 클래스를 넣어서 Test클래스로 생성한다는 뜻입니다.

근데 문제가 있습니다. 이렇게 하면 Builder에는 값이 존재하지만 Test에는 값이 존재하지 않습니다. 이를 추가 해주면 빌더 패턴이 완성입니다.

public class Test {
	
	private String name;
	private int age;
	private String content;
	private String subject;
	
	public Test(Builder builder) {
		name = builder.name;
		age = builder.age;
		content = builder.content;
		subject = builder.subject;
	}

이렇게 하면 builder안에 있는 name,age,content,subject가 들어옵니다. 물론 없다면 들어오지 않겠죠?

자 이제 결과를 봅시다.

public class Main {
	public static void main(String[] args) {
		Test t = new Test.Builder().setAge(2).bulid();
	}
}

자 이제 age가 뭔지 단번에 알수 있게 되었습니다.2군요.

또 2는 age네여ㅎㅎ 물론 setAge가 아닌 age를 써도 무방합니다. fdsfsfd로 작성해도 됩니다. 알아볼수 있다면...

 

또, build뒤에는 아무것도 추가할수 없네여...ㅎㅎ

 

반응형

'디자인패턴' 카테고리의 다른 글

싱글톤 패턴  (0) 2021.11.03
탬플릿 패턴  (0) 2020.06.05
프록시 패턴  (0) 2020.05.18
싱글톤 패턴  (0) 2020.05.12
팩토리 패턴  (0) 2020.05.06

댓글

Designed by JB FACTORY