일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- RDBMS
- application.yml
- 어노테이션
- spring
- Static
- HTTP상태코드
- SQL
- JPA
- restAPI
- 인텔리제이오류
- Swagger
- 의존성주입
- MariaDB
- Java
- SpringSecurity
- 스프링
- 스프링시큐리티
- 스프링오류
- HTTP
- 시큐리티
- API
- server
- 쿼리
- 스웨거
- 스프링RESTAPI
- JWT
- 서버
- 상태코드
- 자바
- 오버라이딩
- Today
- Total
취뽀몽
[Java] 점층적 생성자 패턴(Telescoping Constructor Pattern), 자바 빈즈 패턴(JavaBeans Pattern), 빌더 패턴(Builder Pattern) 본문
[Java] 점층적 생성자 패턴(Telescoping Constructor Pattern), 자바 빈즈 패턴(JavaBeans Pattern), 빌더 패턴(Builder Pattern)
허몽구 2023. 3. 3. 15:16전 포스팅 정적 팩토리 메소드와 생성자에는 제약이 있는데, 바로 선택적 매개변수가 많을 때 적절히 대응하는 것이 어렵다는 점이다.
단점을 보완하고자 빌더 패턴을 사용하는데, 우선 점층적 생성자 패턴과 자바빈즈 패턴에 대해 알아보고 빌더 패턴에 대해 설명하도록 하겠다.
점층적 생성자 패턴은 필수 매개변수만 받는 생성자, 필수 매개변수와 선택 매개변수 1개를 받는 생성자, 필수 매개변수와 선택 매개변수 2개를 받는 생성자, ... 형태로 선택 매개변수를 모두 받는 생성자까지 늘려가는 방식을 말한다.
코드로 예시를 살펴보자.
public class User {
private final String name; // 이름 (필수)
private final int age; // 나이 (필수)
private final String school; // 학교 (선택)
private final int grade; // 학년 (선택)
private final String gender; // 성별 (선택)
private final int phoneNumber; // 번호 (선택)
public User(String name, int age){ // 필수 매개변수를 가지는 생성자
this(name, age, null, 0, null, 0);
}
public User(String name, int age, String school){
this(name, age, school, 0, null, 0);
}
public User(String name, int age, String school, int grade){
this(name, age, school, grade, null, 0);
}
public User(String name, int age, String school, int grade, String gender){
this(name, age, school, grade, gender, 0);
}
public User(String name, int age, String school, int grade, String gender, int phoneNumber){
this(name, age, school, grade, gender, phoneNumber);
}
}
이 클래스의 객체를 만들기 위해서는 자신이 사용하고자 하는 매개변수를 모두 포함하는 생성자를 골라 사용하면 된다.
User user = new User("철수", 24, "국민초등학교", 0, "남자", 01012345678);
이런 경우에는 사용자가 선택해서 추가해야 하는 부분의 매개변수마저도 포함하기 쉽기 때문에 어쩔 수 없이 그런 매개변수도 값을 지정해줘야 한다. 필수 매개변수는 이름과 나이이기 때문에, 학년(grade)을 포함하지 않기 위해 0을 지정해줬다.
위의 예제는 매개변수가 6개밖에 없기 때문에 복잡해보이지 않을 수 있지만 만약 20개, 30개씩 매개변수를 지정해야 한다면 가독성과 효율이 떨어지기 마련이다. 즉, 매개변수가 많아지면 클라이언트 코드를 작성하거나 읽기 어렵다는 뜻이다.
점층적 생성자 패턴을 정리하자면
1) 매개변수가 많을수록 생성자의 수가 많아지기 때문에 코드의 효율성과 가독성이 떨어진다.
2) 클래스 생성자를 호출할 때 해당 매개변수를 옳게 작성했는지, 개수를 제대로 입력한 것이 맞는지 일일이 확인해야 하는 불편함이 있다.
3) 매개변수 타입이 같으면 생성자를 만들 수 없다.
4) 확장성이 떨어진다.
위의 단점을 보완하기 위해, 선택 매개변수가 많을 때 활용할 수 있는 방법은 자바 빈즈 패턴을 사용하는 것이다.
자바빈즈 패턴이란 별도의 생성자를 만들지 않고 객체를 생성하여, 세터(Setter) 메소드를 호출해 원하는 매개변수의 값을 설정하는 방식이다.
public class User {
private final String name; // 이름 (필수)
private final int age; // 나이 (필수)
private final String school; // 학교 (선택)
private final int grade; // 학년 (선택)
private final String gender; // 성별 (선택)
private final int phoneNumber; // 번호 (선택)
}
점층적 생성자 패턴의 User 클래스에 존재했던 생성자를 없애줬다. 그렇다면 메인에서 어떻게 코드를 작성해야 할까?
User user = new User();
user.setName("철수");
user.setAge(24);
user.school("국민초등학교");
user.setGrade(4);
user.setGender("남");
user.setPhoneNumber(01012345678);
다음과 같이 User 객체를 생성한 후 Setter를 사용하여 원하는 값으로 설정해준다.
더욱 가독성있고, 읽기 쉬운 코드가 되었다. 하지만 자바 빈즈 패턴의 가장 심각한 단점이 있다.
자바 빈즈 패턴에서는 객체를 만들기 위해서 메소드를 여러개 호출해야 하고, 객체가 생성되기 전까지 일관성이 무너진 상태가 된다.
일관성이 깨진 객체가 생성된다면, 클래스를 불면으로 만들 수 없고 스레드 안정성을 얻기 힘들다.
자바 빈즈 패턴의 단점을 완화하기 위해 점층적 생성자 패턴의 안정성과 자바 빈즈 패턴의 가독성을 겸비한 빌더 패턴이 나오게 되었다.
빌더 패턴란 복합 객체의 생성 과정과 표현 방법을 분리하여 동일한 생성 절차에서 서로 다른 표현 결과를 만들 수 있게 하는 패턴이다.
클라이언트는 필요한 객체를 직접 만드는 대신, 필수 매개변수만으로 생성자(or 정적 팩토리 메소드)를 호출하여 빌더 객체를 얻을 수 있다. 이후 빌더 객체가 제공하는 Setter 메소드로 원하는 선택 매개변수들을 설정한다.
마지막으로 매개변수가 없는 build() 메소드를 호출하여 객체를 얻는다. 코드를 통해 예시를 살펴보자.
public class User {
private final String name; // 이름 (필수)
private final int age; // 나이 (필수)
private final String school; // 학교 (선택)
private final int grade; // 학년 (선택)
private final String gender; // 성별 (선택)
private final int phoneNumber; // 번호 (선택)
public static class Builder(){
// 필수 매개변수
private final String name;
private final int age;
// 선택 인자 => 초기화 필수
private final String school = null;
private final int grade = 0;
private final String gender = null;
private final int phoneNumber = 0;
public Builder(String name, int age){
this.name = name;
this.age = age;
}
public Builder school(String str){
school = str;
return this;
}
public Builder grade(int var){
grade = var;
return this;
}
public Builder gender(String str){
gender = str;
return this;
}
public Builder phoneNumber(int var){
phoneNumber = var;
return this;
}
public User(Builder builder){
name = builder.name;
age = builder.age;
school = builder.school;
grade = builder.grade;
gender = builder.gender;
phoneNumber = builder.phoneNumber;
}
}
}
빌더의 Setter 메소드는 자기 자신을 반환하기 때문에 (return this) 연쇄적으로 호출이 가능하다. 이런 방식을 플루언트 API(fluent API) 혹은 메소드 연쇄 라고 한다. 그렇다면, 메인에서 어떻게 호출하는지 알아보자.
User user = new Builder("철수", 24)
.school("국민초등학교")
.grade(4)
.gender("남")
.phoneNumber(01012345678)
.build();
각 인자가 어떤 의미인지 알기 쉽고, Setter 메소드를 사용하지 않기 때문에 변경되지 않는 객체를 생성할 수 있다.
또한 자바 빈즈 패턴에서 문제가 되었던 객체 일관성도 깨지지 않고 유지된다.
빌더 패턴은 롬복의 @Builder 어노테이션을 사용할 수 있다. 방법은 2가지이다.
1) 생성자에 @Builder 어노테이션 붙이기
public class User {
private final String name; // 이름 (필수)
private final int age; // 나이 (필수)
private final String school; // 학교 (선택)
private final int grade; // 학년 (선택)
private final String gender; // 성별 (선택)
private final int phoneNumber; // 번호 (선택)
@Builder
public User(String name, int age, String school, int grade, String gender, int phoneNumber){
this.name = name;
this.age = age;
this.school = school;
this.grade = grade;
this.gender = gender;
this.phoneNumber = phoneNumber;
}
}
2) 클래스에 @Builder 어노테이션 붙이기
@Builder
public class User {
private final String name; // 이름 (필수)
private final int age; // 나이 (필수)
private final String school; // 학교 (선택)
private final int grade; // 학년 (선택)
private final String gender; // 성별 (선택)
private final int phoneNumber; // 번호 (선택)
}
처음 @Builder 어노테이션을 접했을 때, 신세계다! 하면서 무작정 클래스에 어노테이션을 갖다 붙이곤 했는데, 가급적 클래스 선언부에 @Builder 어노테이션을 사용하지 않는게 좋다.
머리로는 이해가 되는데 풀어쓰려니까 어렵네요 풀어써줭 ~
정리하자면, 생성자나 정적 팩토리 메소드가 처리해야 할 매개변수가 많다면 빌더 패턴을 선택하여 사용하는 것이 좋다.
빌더는 점층적 생성자 패턴보다 코드의 가독성이 높아지고, 자바 빈즈 패턴보다 더욱 안전하기 때문이다.
'java' 카테고리의 다른 글
[JAVA] 자바가 확장한 객체 지향 (0) | 2023.03.19 |
---|---|
[JAVA] 자바와 객체 지향 (0) | 2023.03.16 |
[JAVA] 자바와 절차적 / 구조적 프로그래밍 (0) | 2023.03.08 |
[Java] 오버로딩과 오버라이딩(Overloading And Overriding) (0) | 2023.03.06 |
[Java] 정적 팩토리 메소드(static factory method) (0) | 2023.02.28 |