서론
class를 변경하려고 할 때 public API가 적합한지에 대해 생각해 본 적이 있습니까? LomBok을 사용할 때, 특히 몇가지 놓치기 쉬운 것들이 있습니다. AllArgsConstructor 어노테이션이 많은 문제를 일으킬 것입니다.
문제가 무엇인가?
AllArgsConstructor를 사용한 간단한 클래스에 대해 보겠습니다.
@Data
@AllArgsConstructor
public class Person {
private final String firstName;
private final String lastName;
private Integer age;
}
이제, 테스트에서 만들어진 생성자를 이용할 수 있습니다.
def 'use generated allArgsConstructor'() {
when:
Person p = new Person('John', 'Smith', 30)
then:
with(p) {
firstName == 'John'
lastName == 'Smith'
age == 30
}
}
테스트는 통과했습니다!
Person class에 새로운 필드를 추가해보도록 하겠습니다. - email
@Data
@AllArgsConstructor
public class Person {
private final String firstName;
private final String lastName;
private Integer age;
private String email;
}
새로운 필드를 추가하는 것은 적합한 변경으로 허용할 수 있습니다. 그러나 테스트는 실패합니다.
groovy.lang.GroovyRuntimeException: Could not find matching constructor for: com.github.alien11689.allargsconstructor.Person(java.lang.String, java.lang.String, java.lang.Integer)
어떻게 이 문제를 해결 할 수 있을까요?
1. 필드를 추가한 이후에 이전의 생성자를 추가할 것
아직도 AllArgsConstructor를 사용하고 있다면, 이전에 작성한 생성자에 내용을 추가함으로써 적합성을 확인해야만 합니다. 즉, null이라는 부분을 따로 추가해야 합니다.
@Data
@AllArgsConstructor
public class Person {
private final String firstName;
private final String lastName;
private Integer age;
private String email;
public Person(String firstName, String lastName, Integer age) {
this(firstName, lastName, age, null);
}
}
이제, 우리 코드는 다시 테스트를 통과합니다.
2. lombok.Data 어노테이션만 사용
단지 Data 어노테이션만 사용한다면, final 필드를 가진 생성자만이 생성 될 것입니다. 왜냐하면, Data는
RequiredArgsConstructor를 포함하기 때문입니다.
@Data
public class Person {
private final String firstName;
private final String lastName;
private Integer age;
}
class PersonTest extends Specification {
def 'use generated allArgsConstructor'() {
when:
Person p = new Person('John', 'Smith')
p.age = 30
then:
with(p) {
firstName == 'John'
lastName == 'Smith'
age == 30
}
}
}
email이라는 새로운 필드를 추가하더라도 이제 테스트는 통과합니다.
3. Builder 어노테이션을 사용
Builder 어노테이션은 PersonBuilder 클래스를 만드는데, 새로운 Person 객체를 만들도록 돕습니다.
@Data
@Builder
public class Person {
private final String firstName;
private final String lastName;
private Integer age;
}
class PersonTest extends Specification {
def 'use generated allArgsConstructor'() {
when:
Person p = Person.builder()
.firstName('John')
.lastName('Smith')
.age(30).build()
then:
with(p) {
firstName == 'John'
lastName == 'Smith'
age == 30
}
}
}
email 필드를 추가하고나서도 테스트는 통과합니다.
결론적으로, 3번을 추천합니다. 또한, 가급적이면 @Builder를 이용하여 직접 만든 생성자를 사용하는 것이 좋습니다.
Builder() 생성자는 변수의 이름으로 매개변수를 설정할 수 있기 때문에 순서에 구애받지 않습니다.
특히, 변수가 선언된 순서대로 lombok은 생성자를 만들기 때문에,
만약 개발자 입장에서 임의적으로 변수의 순서를 바꾼다면 모든 생성자의 매개변수 위치가 꼬일 수 있습니다.
참조
dzone.com/articles/do-not-use-allargsconstructor-in-your-public-api
'학습 > Java' 카테고리의 다른 글
JJWT (1) | 2021.03.03 |
---|---|
Could not autowire. No beans of 'Mapper' type found (0) | 2021.03.02 |
Optional을 제대로 사용하는 26가지 방법 ( 2 ) (0) | 2021.02.18 |
Optional을 제대로 사용하는 26가지 방법 ( 1 ) (0) | 2021.02.18 |
orElse() vs orElseGet() (0) | 2021.02.18 |