일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- 개발 뭐하지
- 백엔드 개발자 뭐해?
- 스프링부트
- 클론코딩
- BufferedReader
- 깃
- git hub
- 개발자
- 깃허브
- 예외처리
- RESTful웹서비스
- 리눅스 서버시간 변경
- string
- 인텔리제이
- 입력
- 서비스 계층
- 백엔드
- Scanner
- Java
- .ppk만들기
- 스프링 OAuth
- Spring
- springboot
- 스프링
- 프로그래머스
- 인스타그램만들기
- RESTful
- 다리를 지나는 트럭
- Git
- @RestCotroller
- Today
- Total
It's Ward
[Spring] 빌더 패턴(Bulider Pattern)에 대해 알아보자 본문
빌더 패턴은 GoF 디자인 패턴 중 생성 패턴에 해당하고 주로 복잡한 단계를 거쳐야 생성되는 개체의 구현을 서브 클래스에게 넘겨줄 때 많이 사용된다.
빌더의 생성자나 메소드에서 유효성 확인을 할 수 있고, 많은 매개변수들을 혼합해서 확인해야 하는경우, build 메소드에서 호출하는 생성자에서 확인할 수 있다.
또, 실패하면 예외를 발생시켜 어떤 매개변수가 잘못되었는지 확인도 할 수 있는데, 이러한 빌더 패턴에대해서 자세하게 알아보도록 한다.
1. 왜 빌더 패턴(Bulider Pattern)을 사용해야 하는가?
빌더 패턴은 다음과 같은 장점을 가지고 있다.
1) 개발자가 필요한 데이터만 설정할 수 있음.
2) 가독성을 높이고, 유연한 변경이 가능함
1) 필요한 데이터만 설정할 수 있음
예시를 들어보자. 프로그램을 요청한 클라이언트가 멤버가 몇번 들어왔는지 체크를 하고싶은데 아직 윗선의 결제가 나지않아 지금 만들어야할지, 넣어야할지 아니면 다른 필드값이 추가되어야할 지 모른다는 가정이다.
빌더 패턴을 모르고 만들었을 경우 다음과 같이 작성할 것이다.
1. 우선 다 만들어 더미값을 만들어 넣어주는 방법
2. 생성자를 또 만들어 만들어 추가하는 방법
3. 정적 메소드를 이용해 추가하는 방법
//더미값을 이용해 넣어주는 방법
Member member = new Member( "ward", "", 0, "VIP");
public class Member {
private String name;
private String password;
private int count;
private String grade;
//생성자를 만들어 추가하는 방법
public Member (String name, String password, String grade){
this.name = name;
this.password = password;
this.grade = grade;
}
//정적 메소드를 이용해 추가하는 방법
public static User newMember(String name, String password, String grade){
return new Member(name, password, 0 , VIP);
}
}
이런 작업이 별로 없다면 당연히.. 해줄 수 있겟지만 굉장히 불필요한 작업이고, 요구사항이 변할때마다 유지보수 시간은 늘어만 날것이다.
하지만 빌더를 이용한다면, 편하게 사용할 수 있다. (사용하지 않는 값에 null / 0 / false 값을 일일히 주지 않아도 괜찮다.)
Member member = Member.builder()
.name("ward")
.password("")
.grade("VIP")
.build();
2) 가독성을 높이고, 유효성 검증을 할 수 있음.
또다시 예시를 들어보자. Member 클래스에 새로운 나이(age)와, 사용한 금액(countMoney)을 추가해야한다고 하면 다음과 같이 생성자로 인스턴스화 해야할 것이다.
//이전
Member member = new Member( "ward", "", 0, "VIP");
//변경
Member member = new Member( "ward", "", 0, "VIP", 29, 1040510);
갑작스렇게 많아진 매개변수에, 어떤 값을 써야하는지 모르게되고 결국, 추가될수록 코드를 보기 점점 힘들어질 것이다. 최악의 경우 매개변수를 잘못 입력하여 오류가 발생 할 수도 있다.
Member member = Member.builder()
.name("ward")
.grade("VIP")
.password("")
.countMoney(1040510)
.age(29)
.build();
위와 같은 코드를 보면 어떤 데이터가 Member에 추가되어야하는지, 명시적으로 표현하고 있어 직관적으로 보기 쉽고 또한 순서가 변경되어도 잘 작동된다.
유연한 변경은, 새롭게 추가되는 변수때문에 생성자로 만들었을 경우, 기존의 코드를 모두 수정해야하는데 이 또한 변경없이 사용할 수 있다는 장점이 있다.
@Test
public void 테스트1(){
//이전
//Member member = new Member( "ward", "", 0, "VIP");
//변경
Member member = new Member( "ward", "", 0, "VIP", 29, 1040510);
}
...
public void 테스트99(){
//이전
//Member member = new Member( "ward", "", 0, "VIP");
//변경
Member member = new Member( "ward", "", 0, "VIP", 29, 1040510);
}
또한, 동적으로 빌더를 통해 잘못된 데이터가 들어간 경우(검증에 실패한경우) illegalargumentexception 을 통해 에러메세지로 어떠한 매개변수가 잘못되었는 지 확인할 수 있다.
빌더 패턴을 더 잘 사용하려면..
1. 불필요한 @Setter / @Data 의 사용은 NO!
변경 가능성을 최소화 하는 경우에는 Setter를 구현하지 않음으로서 final로 선언한 것과 같은 불변성을 확보할 수 있다. (의존관계의 변경이 필요한 상황이 없는경우, 변경의 가능성을 배제하고 불변성을 보장하자)
2. Class 레벨이 아닌 생성자 레벨에 적용하자
@Builder를 Class에 적용하면, 모든 맴버 필드에 대해 매개변수를 만드는 기본 생성자를 만들기때문에 만약 어떤 변수가 데이터베이스 PK 생성전략에 의존한다면, 그 데이터는 변경되어서는 안되기때문이다. Class 레벨에서는 제한하기가 어렵다.
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String password;
private int count;
private String grade;
@Builder
public Member (String name, String password, int count, String grade){
this.name = name;
this.password = password;
this.count = count;
this.grade = grade;
}
}
다음과 같이 Builder 생성시 파라미터가 없는 경우 객체 생성시 id값을 넘겨받지 못하게 제한할 수 있습니다.
Member member = Member.builder()
.name("ward")
.password("")
.grade("VIP")
.id(2L) // 컴파일 시 에러 발생
.build();
3. 초기값이 필요할 때에는 Builder.Default를 이용하자
Builder는 Wrapper 타입을 사용하여 값을 설정하지 않으면 자동으로 다음과 같은 값을 가진다.
Wrapper / Object : null
int : 0
boolean : false
만약에 기본값을 설정하고싶다면, @Builder.Default를 이용하여 기본값을 설정해 줄 수 있는데, 다음과 같이 설정하면, 기본값이 변경된다.
@Builder.Default
private String grade = "VIP";
마치며..
단순하게 빌드 패턴을 사용만 할줄 알았지, 스프링 스터디를 진행하면서 궁금한 점을 하나씩 발표하기로 하였는데 스터디원님께서 좋은 기회를 만들어 주셔서 조금 더 스프링과 친해지고 관련된 디자인 패턴이라던지, 디버깅 방법이라던지 좋은 정보들을 학습할 수 있는 시간이였다고 생각한다.
끝으로, 빌더를 이용한 테스트 방법에대해 소개한다.
https://cheese10yun.github.io/spring-builder-pattern/
참고 자료 :
https://www.youtube.com/watch?v=OwkXMxCqWHM
https://mangkyu.tistory.com/163
https://mangkyu.tistory.com/125
'Java > Spring' 카테고리의 다른 글
[Spring] RESTful 웹서비스 (0) | 2022.06.23 |
---|---|
[Spring] @Controller와 @RestController 동작 방식 및 차이점 (0) | 2022.06.21 |
[Spring] Spring의 특징과 계층 구조 (0) | 2022.06.20 |
[Spring] 트랜잭션(Transaction)이란 무엇일까? (1) | 2022.06.18 |
[Spring] 스프링 Intellij - 실시간 반영하기(코드 자동 재시작) (0) | 2022.05.29 |