spring 강의를 들으면서 배운 내용으로는,
실무에서 Set 메서드를 사용하면 무분별하게 값이 변경되기 때문에 지양해야하고
필드의 개수가 늘어남에 따라 선택적으로 필드에 값을 넣기위해 @Builder를 사용한다고 했다.
이 내용에 대해 좀 더 깊이있게 다뤄보려고 한다.
@Builder 사용방법 ( With Lombok )
lombok dependency를 사용한다는 가정하에,
아래의 코드는 Builder 패턴을 구현한 대신에 롬복에서 제공하는 @Builder 를 적용한 예이다.
여기서는 email과 passwd에 대해서만 적용했는데 용도에 따라 매개변수를 추가해서 활용하면 된다.
..
public class MemberDto {
private String email;
private String passwd;
private String address;
private String tel;
private Long age;
@Builder // ★
public MemberDto(String email, String passwd) {
this.email = email;
this.passwd = passwd;
}
}
@Builder 를 적용한 생성자 안에 있는 필드의 데이터에 한하여, 아래와 같이 사용할 수 있다.
public class MemberService {
..
public void toDto(Member member) {
MemberDto memberDto = MemberDto.builder()
.email(member.getEmail())
.passwd(member.getPasswd())
.build();
}
}
Builder Pattern ( without Lombok )
위에서는, 단순히 Lombok 의 @Builder 애노테이션 적용만으로 부가적인 코드 구현이 없었다.
Lombok 없이 Builder 패턴을 구현해보자.
public class MemberDto {
private String email;
private String passwd;
private String address;
private String tel;
private Long age;
public String getEmail() {
return email;
}
public String getPasswd() {
return passwd;
}
public String getAddress() {
return address;
}
public String getTel() {
return tel;
}
public Long getAge() {
return age;
}
public MemberDto(Builder builder) {
this.email = builder.email;
this.passwd = builder.passwd;
this.address = builder.address;
this.tel = builder.tel;
this.age = builder.age;
}
public static Builder builder() {
return new Builder();
}
public static final class Builder{
String email;
String passwd;
String address;
String tel;
Long age;
private Builder(){
}
public Builder email(String email) {
this.email = email;
return this;
}
public Builder passwd(String passwd) {
this.passwd = passwd;
return this;
}
public Builder address(String address) {
this.address = address;
return this;
}
public Builder tel(String tel) {
this.tel = tel;
return this;
}
public Builder age(Long age) {
this.age = age;
return this;
}
public MemberDto build() {
return new MemberDto(this);
}
}
}
위와 같이 Builder Patten을 구현해봄으로서 좀 더 명확하게 동작에 대해 이해할 수 있다.
MemberDto에는 getter 만 있기때문에, 불변객체로 사용이 가능하며 사용방법은 @Builder와 동일하다.
MemberDto memberDto = MemberDto.builder()
.email("test@test.com")
.passwd("1234")
.address("서울")
.tel("010-xxxx-xxxx")
.age(20L)
.build();
Builder 패턴의 이점
정리하자면, Builder 패턴의 이점은 다음과 같다.
1. Builder를 통해 불변 객체로 생성할 수 있다. ( 안정성 ↑ )
2. Builder를 통해 매개변수의 순서에 대한 오류를 예방할 수 있다. ( 가독성 ↑ )
1. Builder를 통해 불변 객체로 생성할 수 있다.
Setter를 사용하게 되면 어디서든 값이 변경이 가능하며, 필수 데이터 말고도 다른 데이터를 변경할 수 있다.
그렇지만 Builder를 사용하면 생성된 이후로 값을 변경할 수 없으며 필수 데이터 외에 다른 데이터에 대해 값을 지정할 수도 없다.
public class MemberService {
..
public void toDto(Member member) {
// 1. setter
MemberDto memberDto1 = new MemberDto();
memberDto1.setEmail(member.getEmail());
memberDto1.setPasswd(member.getPasswd());
// memberDto1.setAge(member.getAge()); -> 가능
// 2. builder
MemberDto memberDto2 = MemberDto.builder()
.email(member.getEmail())
.passwd(member.getPasswd())
// .age(member.getAge()) -> ★불가
.build();
}
}
2. Builder를 통해 매개변수의 순서에 대한 오류를 예방할 수 있다.
아래와 같이 모든 매개변수를 갖는 생성자를 통해 인스턴스를 생성하면 해당 값을 가지는 필드가 뭔지에 대해 명확히 나와있지 않기 때문에 순서에 대한 오류가 발생할 수 있다.
Builder를 사용하면 각 데이터에 대한 필드값이 명확하기 때문에 가독성 또한 좋아진다.
MemberDto memberDto1 = new MemberDto("test@test.com","1234","서울","010-xxxx-xxxx",20L);
/** Builder 적용 **/
MemberDto memberDto2 = MemberDto.builder()
.email("test@test.com")
.passwd("1234")
.address("서울")
.tel("010-xxxx-xxxx")
.age(20L)
.build();
정리
사실 디자인 패턴의 Builder 패턴과 @Builder를 같다고 생각하지 못했었다.
디자인 패턴을 직접 내 코드에 적용해보고 구현해봄으로써 이해도가 높아진 것 같다.
이 외에도 다양한 디자인 패턴에 대해 공부해보자.
참고
https://johngrib.github.io/wiki/pattern/builder/
https://github.com/davidmoten/java-builder-pattern-tricks
'Web' 카테고리의 다른 글
[Spring] 라이브러리 버전 관리하기 ( Gradle ) (0) | 2023.07.06 |
---|---|
[Spring] Exception Handling 에 대해서 (3) | 2023.07.03 |
[Spring] @Email 과 이메일 정규식 표현 (0) | 2023.06.24 |
[Spring] Gradle 과 Maven 에 대해서 (0) | 2023.06.22 |
[Spring] HikariCP 와 Pool Size에 대해서 (0) | 2023.06.18 |