생성자에 선택적 매개변수가 많다면 어떻게 해결할까?


1) 점층적 생성자 패턴(telescoping constructor pattern)


피자를 만든다고 생각해보자. (= Pizza Class가 있다.)

피자를 만드는데 사이즈와 조각 수는 필수이다. (=size와 piece는 필수 매개변수이다. )

피자를 만드는데 햄, 치즈, 고기, 소스는 선택사항이다. (=ham, cheese, beef, sauce 는 선택적 매개변수다.)


이 클래스에서 우리는 생성자를 다음과 같이 구현 할 수 있다.


‘필수 매개변수만 받는 생성자’                  - (size, piece )

‘필수 매개변수와 선택 매개변수 1개를 받는 생성자’     -  (size, piece, ham)

‘필수 매개변수와 선택 매개변수 2개를 받는 생성자’     - (size, piece, ham, cheese)

…….

‘필수 매개변수와 선택 매개변수 전체를 받는 생성자’    -(size, piece, ham, cheese, beef, sauce)


이런식으로 선택 매개변수가 없는 생성자부터 선택 매개변수를 전부 받는 생성자까지 늘려나가는 방식을  ‘점층적 생성자 패턴’ 이라고 한다.




만약 ( Size : 10, Piece : 4, Beef : 20 )인 Pizza 객체를 만들어야 한다면 다음과 같이 객체를 생성할 수 있다.

<Ex 1.1>




< 장점 >

( 10, 5 , 햄 없음, 치즈 없음, 고기 없음, 소스 없음 ) 과 같은 Pizza 객체를 생성할 때

new Pizza(10,5) 으로 쉽게 생성할 수 있다.


< 단점 >

<Ex1.1> 객체를 만들기위해 ham과 cheese에 0을 넣어줘야하는 불편함이 생긴다.

현재는 beef 앞에 사용하지 않는 선택 매개변수가 햄과 치즈 2개 밖에 없지만, 프로그램을 짜다보면 그 수는 훨 씬 늘어난다.

따라서 점층적 생성자 패턴을 쓸 수는 있지만, 매개변수 개수가 많아지면 클라이언트 코드를 작성하거나 읽기 어렵다. 코드를 읽을 때 각 값의 의미가 무엇인지 헷갈릴 수 있고, 매개변수가 몇 개 인지도 주의해서 세어 보아야 할 것이다.







2) 자바빈즈 패턴 (JavaBeans pattern)

자바빈즈 패턴은 매개변수가 없는 생성자로 객체를 만든 후, 세터 메서드를 호출해 원하는 매개변수의 값을 설정하는 방식이다.


점층적 생성자 패턴 구현했던 피자 클래스를 자바빈즈 패턴으로 변환한 아래의 코드를 보자.

점층적 생성자 패턴에서 봤던 피자 클래스가 위와 같이 바뀌었다.

매개변수가 없는 생성자 1개와 Setter 6개로 구현된 클래스이다.


사용 :



점층적 생성자 패턴의 단점은 사라지고, 인스턴스를 만들기 쉽고, 더 읽기 쉬운 코드가 되었다.

하지만 위의 자바빈즈 패턴은 심각한 단점이 있다. 뭘까?


 자바빈즈 패턴의 단점.

(1). 객체 하나를 만드려면 메서드를 여러개 호출해야한다.

  • 위의 ‘사용’ 예에서 Setter 메서드가 6개나 호출된 것을 볼 수 있다.

  • 따라서 1회의 호출로 객체 생성이 끝나지 않으므로 객체의 일관성(Consistency)이 깨진다.


(2) 일관성이 무너지는 문제 때문에 클래스를 불변으로 만들 수 없으며, 스레드 안전성을 얻으려면 프로그래머가 추가 작업을 해줘야만 한다.









3) 빌더 패턴

  • 점층적 생성자 패턴의 안전성과 자바빈즈 패턴의 가독성을 겸비한 빌더 패턴

  • 클라이언트는 필요한 객체를 직접 만드는 대신, 필수 매개변수만으로 생성자를 호출해 빌더 객체를 얻는다.

그런 다음 빌더 객체가 제공하는 일종의 세터 메서드들로 원하는 선택 매개변수들을 설정한다.

마지막으로 매개변수가 없는 build 메서드를 호출해 드디어 우리에게 필요한 객체를 얻는다.

→ 빌더는 생성할 클래스 안에 정적 멤버 클래스로 만들어두는 게 보통이다.



앞의 예제를 빌더 패턴으로 구현한 코드를 예로 보도록 하자.


Pizza 클래스 내에 정적 멤버 클래스로 Builder 클래스가 있는 구조이다.

Builder 클래스는 필수 매개변수를 가진 생성자가 있다.

그리고 Builder의 세터 메서드들은 빌더 자신을 반환하기 때문에 연쇄적으로 호출할 수 있다.


사용 예를 보자.

 클라이언트는 위와 같이 두 가지 방법으로 사용할 수 있다.

이 클라이언트 코드는 쓰기 쉽고, 읽기 쉽다. 빌더 패턴은 명명된 선택적 매개변수를 흉내낸 것이다.(파이썬과 스칼라에 존재하는)






☞ 빌더 패턴은 계층적으로 설계된 클래스와 함께 쓰기에 좋다.


  • 각 계층의 클래스에 관련 빌더를 멤버로 정의한다.

  • 추상 클래스는 추상 빌더를, 구체 클래스(concrete class)는 구체 빌더를 갖게 한다.

나는 클래스를 계층적으로 설계해본 경험이 부족해서 책에 있는 예제를 보여주고 설명하겠다.


<abstract class Pizza>


< 뉴욕피자 >

Pizza 클래스의 하위 클래스인 뉴욕피자 클래스다.

뉴욕피자는 크기를 필수 매개변수로 받는다.



<칼초네 피자>

Pizza 클래스의 하위 클래스인 칼초네(Calzone) 클래스다.

Pizza 클래스는 소스를 넣을지 않넣을지 선택하는 매개변수를 필수로 받는다.



각 하위 클래스의 빌더가 정의한 build 메서드는 해당하는 구체 하위 클래스를 반환하도록 한다. ( NyPizza.Builder는 NyPizza,  Calzone,Builder는 Calzone를 반환)

이렇게 하위 클래스의 메서드가 상위 클래스의 메서드가 정의한 반환타입이 아닌, 그 하위 타입을 반환하는 기능을 ‘공변환 반환 타이핑’이라 한다. 이 기능을 사용하면 클라이언트가 형변환에 신경쓰지 않고도 빌더를 사용할 수 있다.



사용 예)



빌더 패턴의 단점

  • 객체를 만드려면 빌더부터 만들어야한다.

(빌더 생성 비용이 크지는 않지만 민감한 상황에서는 문제가 될 수 있음)

  • 점층적 생성자 패턴보다는 코드가 장황해서 매개변수가 4개 이상은 되어야 값어치를 한다.

→ 하지만 API는 시간이 지날수록 매개변수가 많아지는 경향이 있음.



정리.

생성자나 정적 팩터리가 처리해야 할 매개변수가 많다면 빌더 패턴을 선택하는 게 더 낫다.

특히 매개변수 중 다수가 필수가 아니거나 같은 타입인 경우에.

빌더는 점층적 생성자보다 클라이언트 코드를 읽고 쓰기가 훨씬 간결하고, 자바빈즈보다 훨씬 안전하다.



'Java' 카테고리의 다른 글

정적 펙터리 메서드 (static factory method)  (0) 2019.03.27
Annotation (어노테이션)  (0) 2018.11.13

 이번 포스팅에서는 브랜치들 간 차이점을 비교하는 방법에 대해 소개하고자 합니다.

먼저 현재 상태부터 보여드리고, 실습을 통해 명령어들에 대해 알아가보도록 하겠습니다.


 현재 상태 확인.

현재 상태는 branch가 master, example1, example2가 있으며 master에서 log는 위와 같습니다.

그리고 example1 브랜치에서 상태는 다음과 같다.

위와 같이 master 브랜치에서 git log 명령어를 하면 example1 브랜치에서 commit했던 'add bbbbb.txt' log를 볼 수 없다.

그럼 어떻게하면 한 브랜치에서 다른 모든 브랜치들의 log나, 다른 한 브랜치와 비교하거나, 코드를 비교할 수 있을까?

이제부터 위에 대한 방법을 하나씩 알아가보도록 하자.


1. 모든 Branch의 commit 정보 확인하기 : git log --branches

git log --branches  명령어를 사용하면  현재 어느 브랜치에 있던 모든 브랜치들의 commit 상태를 확인할 수 있다.

(현재 master에 있지만 example1 브랜치가 commit한 'add bbbbb.txt'가 뜨는 것을 볼 수 있다.)

여기에서 다음과 같이 oneline 옵션을 통해 조금 더 간략히 볼 수 있다. 



2. 두 브랜치간 비교하기 : git log '비교할 브랜치1' '비교할 브랜치2'

< git log master example1 >

위의 명령어를 통해서 master과 example1 브랜치간 차이점을 확인 할 수 있다.

그럼 두 브랜치간 코드비교는 어떻게 할까?


3. 두 브랜치간 코드 비교하기 : git diff '비교할 브랜치1' '비교할 브랜치2'

위와 같이 git diff 로 브랜치간 코드를 비교할 수 있는데. 'bbbbb.txt' 파일이 새로 생성되고 내용은 초록색 글씨로 된

'bbbbb'인 것을 확인 할 수 있다.


 이번 포스팅은 Git의 최대 장점이라고 들리는(?) Branch를 알아보고 실습을 해보도록 하겠습니다.



 1. Branch란?

 개발을 하다 보면 코드를 여러 개로 복사해야 하는 일이 자주 생긴다. 코드를 통째로 복사하고 나서 원래 코드와는 상관없이 독립적으로 개발을 진행할 수 있는데, 이렇게 독립적으로 개발하는 것이 브랜치다.

 실습을 해보면서 어떻게 사용하는 것인지 보도록 하겠습니다.



 2. 실습해보기.


1) 초기 상태.

branch를 추가하지 않은 초기의 모습을 먼저 보겠습니다.

git branch를 입력하게되면 branch의 목록이 나오고, 현재 어느 branch인지 보여줍니다.

git은 저장소를 만들면 기본적으로 'master'라는 이름의 branch를 만들어줍니다. 따라서 위에 보이는 master 브랜치는 자동으로 생성된 branch입니다.



2) 'git branch 브랜치이름' : branch를 생성하는 명령어

실습을 해보기 위해 'example1'이라는 branch를 생성했습니다.

그리고 git branch 를 입력해서 보면 현재 branch가 'example1', 'master' 이렇게 2개가 된 것을 확인할 수 있습니다.



3) 'git branch -d 브랜치이름' : 브랜치이름에 해당하는 branch를 삭제하는 명령어

  'example1','master' 브랜치가 있는 상태에서 'example1' 브랜치를 삭제해보았습니다.

삭제 후 'git branch' 명령어를 통해 브랜치를 보면 'master'브랜치만 남은 것이 보입니다.



4) 'git checkout 전환하려는 브랜치 이름' : 브랜치를 전환하는 명령어

'master' 브랜치에서 'example1'으로 브랜치를 전환해보았습니다.

master에 있던 *가  example1으로 이동한 것을 볼 수 있습니다. 이것은 현재 브랜치가 'example1'이라는 것을 의미합니다.



5) 'git checkout -b 생성하고 변환하려는 브랜치 이름' : branch를 생성함과 동시에 전환하는 명령어

git checkout에 -b 옵션을 주게되면 브랜치를 생성하고 전환까지 하게 됩니다.

즉, git branch 브랜치명 → git checkout 브랜치명  : 이 과정은 'git checkout -b 브랜치명'  하는 것과 같습니다.



6) branch 전환해서 작업해보고 무슨 차이가 있는지 알아보기

 <master 브랜치에서 상태 >

현재 branch는 master, example1, example2 가 있고, aaaaa.txt라는 파일이 있습니다.

git log도 위와같이 볼 수 있습니다.

 이제 example1 브랜치로 전환해서 bbbbb.txt라는 파일을 생성하고, commit까지 해보겠습니다.

 < example1 브랜치에서의 상태 >

example1 브랜치로 전환해서 bbbbb.txt 파일을 생성하고 commit까지 한 상태입니다.

여기서 브랜치를 다시 master로 전환하면 example1 브랜치에서 생성했던 bbbbb.txt파일과 log는 어떻게 바뀔까요?

결론을 먼저 말씀드리자면, master브랜치로 다시 전환하면 bbbbb.txt파일은 없어지고, 'add bbbbb.txt'라는 commit 로그도 없어집니다.


 < example1 -> master branch로 전환 후 모습 >

위의 화면을 보시면 브랜치를  example1 -> master로 변경하고, 현재 디렉토리의 파일, git log를 볼 수 있습니다.

다시 master -> example1 으로 브랜치를 변경하고 파일의 상태와, log를 보고 포스팅을 마무리 짓겠습니다.

< master -> example1 으로 branch 다시 전환 후 모습 >

이것을 통해 branch는 각각 독립된 저장소라고 생각 할 수있습니다. 




+ Recent posts