반응형
해당 글에서는 생성자 패턴에 대해서 이해하고, 어떤 패턴으로 생성자를 구성하는 것이 좋을지에 대해서 공유합니다.
1) 생성자 패턴의 종류
1. 점층적 생성자 패턴 (Telescoping Constructor Pattern)
💡 점층적 생성자 패턴
- 생성자를 매개변수에 개수만큼 구성하는 패턴을 의미합니다.
public class SuccessCode {
private int status;
private String code;
private String message;
// 매개변수 1개
SuccessCode(int status) {
this(status, null, null);
}
// 매개변수 2개
SuccessCode(int status, String code) {
this(status, code, null);
}
// 모든 매개변수를 가짐
SuccessCode(int status, String code, String message) {
this.status = status;
this.code = code;
this.message = message;
}
}
💡 객체를 생성하고자 할때 객체 내에 파라미터 값는 필수적으로 넣어줘야 구성이 가능합니다.
2. 자바 빈즈 패턴(Java Beans Pattern)
💡 자바 빈즈 패턴
- 별도의 생성자를 구성하지 않고 객체를 생성하여 setter를 이용하여서 객체를 구성하는 형태를 의미합니다.
- 아래의 예시는 Lombok을 이용하여서 별도의 Getter/Setter를 구성하지 않은 형태로 예시를 작성하였습니다. 동일하게 Getter Setter로 구성된 형태와 동일합니다.
import lombok.*;
@Getter
@Setter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class SuccessCode {
private int status;
private String code;
private String message;
}
3. Builder Pattern
💡 빌더 패턴(Builder Pattern)
- 복합 객체의 생성 과정과 표현 방법을 분리하여 동일한 생성 절차에서 서로 다른 표현 결과를 만들 수 있게 하는 패턴을 의미합니다.
- 다시 말해, Lombok에서 제공해주는 Annotation으로 별도의 Bulider Pattern을 구성할 필요 없이 생성자에 @Bulider를 선언하면 이를 자동으로 구성하는것을 의미합니다.
import lombok.*;
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class SuccessCode {
private int status;
private String code;
private String message;
@Builder
SuccessCode(int status, String code, String message) {
this.status = status;
this.code = code;
this.message = message;
}
}
// Builder Pattern 사용
SuccessCode successCode = SuccessCode.builder()
.status(200)
.message("성공 되었습니다.")
.code("200")
.build();
[ 더 알아보기 ]
💡 @NoArgsConstructor(access = AccessLevel.PROTECTED)
- 파라미터가 없는 생성자를 생성해주는 어노테이션이며 무분별한 객체 생성에 대해서 한번 더 체크 할 수 있는 수단 입니다.
- 개발자가 실수로 클래스의 필드 중 하나의 필드에 대한 값 설정을 누락하였을 경우 객체는 불완정한 상태가 되어 버립니다.
💡 생성자 패턴에 대해서 세가지를 확인해보았습니다.
💡 결론적으로는 'Builder Pattern'을 사용하는 것을 권장드립니다. 권장에 대한 이유는 이어서 말씀 드립니다.
2) Builder Pattern을 사용해야 하는 이유
💡 사용을 해야 하는 이유에 대해서 가장 큰 이유는 위에서 설명한 점층적인 생성자 패턴, 자바 빈 패턴의 단점을 모두 채울 수 있는 것이 'Builder Pattern'이기에 권장드립니다.
1. 생성자의 입력 값에 대한 순서가 바뀔 경우에 대한 오류를 줄일 수 있다.
💡 점층적인 패턴은 사용하려는 생성자마다 모두 만들어 줘야 하는 문제가 있습니다. 그리고 또한 매개변수의 개수가 동일한 경우 다른 생성자를 구성할 수 없다는 점이 있습니다.
public class SuccessCode {
private int status;
private String code;
private String message;
// 매개변수 1개
SuccessCode(int status) {
this(status, null, null);
}
// 매개변수 2개
SuccessCode(int status, String code) {
this(status, code, null);
}
// 매개변수 2개
SuccessCode(String code, String message) {
this(status, null, message);
}
// 매개변수 2개
SuccessCode(String code, String message) {
// 해당 부분에 오류 발생!
this(null, code, message);
}
// 모든 매개변수를 가짐
SuccessCode(int status, String code, String message) {
this.status = status;
this.code = code;
this.message = message;
}
💡 빌더 패턴의 경우 전체 생성자를 만들어두고 사용하지 않는 값에 대해서 초기화 값으로 채워주기만 하면 수행이 가능합니다.
import lombok.*;
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class SuccessCode {
private int status;
private String code;
private String message;
@Builder
SuccessCode(int status, String code, String message) {
this.status = status;
this.code = code;
this.message = message;
}
}
// Builder Pattern 사용
SuccessCode successCode = SuccessCode.builder()
.status(200)
.message("성공 되었습니다.")
.code("200")
.build();
2. 가독성이 높은 코드를 작성할 수 있다.
💡 자바 빈즈는 같은 경우는 생성한 객체에 setter로 값을 모두 세팅을 해주어야 합니다.
💡 아래의 예시에는 지금은 3개의 변수만 존재하지만 나중에는 가독성이 떨어집니다.
@Setter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class SuccessCode {
private int status;
private String code;
private String message;
}
SuccessCode successCode = new SuccessCode();
successCode.setStatus(200);
successCode.setCode("200");
successCode.setMessage("성공");
💡 빌더 패턴의 경우 하나의 메서드 묶음에 모두 값을 세팅할 수 있기에 가독성이 좋습니다.
import lombok.*;
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class SuccessCode {
private int status;
private String code;
private String message;
@Builder
SuccessCode(int status, String code, String message) {
this.status = status;
this.code = code;
this.message = message;
}
}
// Builder Pattern
SuccessCode successCode = SuccessCode.builder()
.status(0)
.message("성공 되었습니다.")
.code("200")
.build();
3. 변경 가능성을 최소화할 수 있다.
💡 Lombok에서 모든 변수의 생성자를 만들어주는 @AllArgsConstructor 어노테이션을 사용하는 경우에 IDE에서 제공하는 리펙토링이 작동하지 않고 개발자도 인식하지 못하는 사이에 생성자의 파라미터 순서를 필드 선언 순서에 맞춰 변경해버리는 문제가 있습니다. 또한 인스턴스 멤버의 선언 순서에 영향을 받기 때문에 변수의 순서를 바꾸면 생성자의 입력 값 순서도 바뀌게 되어 검출되지 않는 치명적인 오류를 발생시킬 수 있습니다.
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@AllArgsConstructor
public class SuccessCode {
// 성공 코드의 '코드 상태'를 반환한다.
private final int status;
// 성공 코드의 '코드 값'을 반환한다.
private final String code;
// 성공 코드의 '코드 메시지'를 반환한다.
private final String message;
}
/**
* 문제점이 발생하는 구간
* 생성자의 순서가 뒤바뀌더라도 객체 생성이 완료되어 잘못된 데이터가 들어 갈 수 있다.
*/
// 객체 생성 - OK
SuccessCode successCode = new SuccessCode(200, "200", "성공입니다.");
// 객체 생성 - OK
SuccessCode successCode = new SuccessCode(200, "성공입니다.", "200");
4. 선택적으로 객체를 구성할 수 있습니다.
💡 모든 인자를 넣어줘야 하는 생성자 형태에서 파라미터 값을 넣어주지 않으면 오류가 발생합니다
public class SuccessCode {
private final int status;
private final String code;
private final String message;
SuccessCode(int status, String code, String message) {
this.status = status;
this.code = code;
this.message = message;
}
}
SuccessCode(200, ""); // 에러 발생
💡 builder로 생성자를 구성할 때에 값을 넣지 않으면 null로 값을 반환해주어서 원하는 선택적으로 생성자를 구성할 수 있습니다.
import lombok.*;
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class SuccessCode {
private int status;
private String code;
private String message;
@Builder
SuccessCode(int status, String code, String message) {
this.status = status;
this.code = code;
this.message = message;
}
}
// Builder Pattern
SuccessCode successCode = SuccessCode.builder()
.code("200")
.build();
log.debug("message : " + successCode.getMessage()); // null
log.debug("code : " + successCode.getCode()); // 200
99) 기타 꿀팁
1. [IntelliJ] 쉽게 생성자를 구성하는 방법
1.1. IntelliJ에서 command + n 단축키를 누르고 'Constructor'를 누릅니다.
1.2. 구성하려는 인자를 선택하여 'OK' 버튼을 누릅니다.
1.3. 원하는 만큼 매개변수 대상으로 생성자가 생성되었습니다.
오늘도 감사합니다. 😀
반응형
'Java > 아키텍처 & 디자인 패턴' 카테고리의 다른 글
[Java] RESTful API 설계 방법 -2 : 구성하기 (0) | 2023.03.26 |
---|---|
[Java] RESTful API 설계 방법 -1 : 이해하기 (0) | 2023.03.22 |
[Java] 계층화된 아키텍처(Layered Architecture) : N Tier Architecture (0) | 2023.01.25 |
[Java] 프로그래밍 패러다임 이해하기 (0) | 2023.01.07 |
[Java] 생성자 패턴 - Builder() 심화 속성 이해하기 -1 : Lombok Annotation (0) | 2023.01.06 |