제 블로그의 모든 글은 IMHO로 쓴 것입니다. 잘못된 부분이 있으면 덧글을 통해서 소통을 하면 더 좋은 글로 발전이 될 수 있을 것 같습니다. 그렇지만 소통을 할 때 서로의 감정을 존중하는 선에서 해주셨으면 좋겠습니다. 감사합니다:) — Disadvantages of the Builder pattern include:
Requires creating a separate ConcreteBuilder for each different type of product. Requires the builder classes to be mutable. Data members of class aren’t guaranteed to be initialized. Dependency injection may be less supported.
잘못했던 생각 : jpa에서 set을 마구 사용하는 것과 builder로 하는 것이 비슷하다고 생각했었다.
그래서 set은 객체의 값을 바꿀 수 있고, 개발자의 바꾸면 안되는 것을 바꿀 수 있기 때문에 무분별하게 생성하는 것은 좋지 않다.(라고 생각했다.
라고 생각했따.
그러나 가장 잘못된 생각은!
빌더패턴은 객체를 생성할 때 사용하는 패턴이다. 객체 내용을 바꿀 때 보다는!
builder는 그저 한번 객체를 생성하고 setter 메소드가 없어 변경 불가능한 객체를 만들 수 있고
한번의 객체를 생성함으로 객체 일관성이 깨지지 않는다.
또한 build()함수가 잘못된 값이 입력되었는지 검증도 된다.
*setter의 단점
객체 일관성(consistency)이 깨진다. 1회의 호출로 객체 생성이 끝나지 않았다. 즉 한 번에 생성하지 않고 생성한 객체에 값을 떡칠하고 있다. setter 메서드가 있으므로 변경 불가능(immutable)클래스를 만들 수가 없다. 스레드 안전성을 확보하려면 점층적 생성자 패턴보다 많은 일을 해야 한다. set method는 의도를 갖기가 힘들다.
*setter대안책
updateMyAccount 1 2 3 4 5 6 7 8 9 10 11
public Account updateMyAccount(long id, AccountDto.MyAccountReq dto) { final Account account = findById(id); account.updateMyAccount(dto); return account; } // Account 도메인 클래스 public void updateMyAccount(AccountDto.MyAccountReq dto) { this.address = dto.getAddress(); this.fistName = dto.getFistName(); this.lastName = dto.getLastName(); }
위의 예제와 같은 예제 코드입니다. findById 메소드를 통해서 영속성을 가진 객체를 가져오고 도메인에 작성된 updateMyAccount를 통해서 업데이트를 진행하고 있습니다.
repository.save() 메소드를 사용하지 않았습니다. 다시 말해 메소드들은 객체 그 자신을 통해서 데이터베이스 변경작업을 진행하고, create 메서드에 대해서만 repository.save()를 사용합니다
create 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
// 전체 코드를 보시는 것을 추천드립니다. public static class SignUpReq {
private com.cheese.springjpa.Account.model.Email email; private Address address;
@Builder public SignUpReq(Email email, String fistName, String lastName, String password, Address address) { this.email = email; this.address = address; }
public Account toEntity() { return Account.builder() .email(this.email) .address(this.address) .build(); } }
public Account create(AccountDto.SignUpReq dto) { return accountRepository.save(dto.toEntity()); }
setter 메소드 없이 create 하는 예제입니다. SignUpReq 클래스는 Request DTO 클래스를 통해서 사용자에게 필요한 값을 입력받고 그 값을 toEntity 메소드를 통해서 Account 객체를 생성하게 됩니다. 이 때 빌더 패턴을 이용해서 객체를 생성했습니다. 도메인 객체를 생성할 때 빌더패턴을 적극 추천해 드립니다. 빌더 패턴에 대해서는 여기서는 별도로 다루지 않겠습니다.
save 메소드에는 도메인 객체 타입이 들어가야 합니다. 이때 toEntity 메소드를 통해서 해당 객체로 새롭게 도메인 객체가 생성되고 save 메소드를 통해서 데이터베이스에 insert 됩니다.
이정도 되면… setter의 장점이 궁금해지게 된다.
setter의 역할은 getter와 setter를 분리해서 정보은닉을 하는 것 뿐인가?
그렇다면 builder에서 정보은닉을 사용한다면 setter는 더이상 필요하지 않은 것 아닌가?
setter는 정말 그냥 안티패턴인가?
그냥 안티패턴이라면 사람들이 많이 안썼지 않았을까?
builder의 역할: 처음에 객체를 생성하고 셋팅 하는 용도이다.
빌더는 빌더패턴으로 만들어진 것인데
빌더패턴은 객체를 생성할 때 흔하게 사용하는 패턴이다.
내가 지금 쓰는 lombok 빌더는 effective java에 나온 빌더패턴과 비슷한 방식으로 짜져 있는데.
장점 각 인자가 어떤 의미인지 알기 쉽다. setter 메소드가 없으므로 변경 불가능 객체를 만들 수 있다. 한 번에 객체를 생성하므로 객체 일관성이 깨지지 않는다. build() 함수가 잘못된 값이 입력되었는지 검증하게 할 수도 있다. 있다. 그럼으로 builder는 써도 굿!
setter의 장점인 정보은닉에 대해서 좀 더 깊게 알아봐야겠다
일단은 풀리퀘 먼저 처리하자 ^^^^^^*
느낌이 새로 생성할 때는 builder를 생성해도 되는 것 같다.
일단 그것부터 시작하자
참고 :
setter 대안책 : https://cheese10yun.github.io/spring-jpa-best-06/
빌더패턴 내용: https://johngrib.github.io/wiki/builder-pattern/
https://blog.vjvj.net/2017/04/effective-java-2-builder.html
builder pattern을 field 내용 바꿀 때 사용해도 되는 것인가? : https://stackoverflow.com/questions/51998929/java-builder-pattern-and-changing-field
https://blog.vjvj.net/2017/04/effective-java-2-builder.html