우리는 왜? 불변성을 지키면서 코딩을 해야할까?
내가 알고있는 불변성이라는 개념은 'final'
불변객체는 재할당은 가능하지만, 한번 할당하면 내부 데이터를 변경할 수 없는 객체
class ImmutablePerson {
private final int age;
private final int name;
public ImmutablePerson(int age, int name) {
this.age = age;
this.name = name;
}
}
private final로 변수를 선언했기 때문에 외부에서 당연히 접근 불가
final로 했기 때문에 setter도 사용 불가능
그럼 이 불변성을 지키는것의 장단점은 무엇일까?
장점
- 객체에 대한 신뢰도가 높아진다 => 객체가 한번 생성되어서 그게 변하지 않는다면 transaction 내에서 그 객체가 변하지 않기에 우리가 믿고 쓸 수 있기 때문!
단점
- 객체가 가지는 값마다 새로운 객체가 필요합니다. 따라서 메모리 누수와 새로운 객체를 계속 생성해야하기 때문에 성능저하를 발생시킬 수 있다
1. 점층적 생성자 패턴
public class Person {
// 필수 맴버변수
private String gender;
private String name;
private int age;
// 선택적 맴버변수
private String lang;
private double height;
// 필수 맴버변수 생성자
Person(String name,String gender,int age){
this.name = name;
this.gender = gender;
this.age = age;
}
// 모든 맴버 변수를 설정하는 생성자
Person(String name,String gender,int age,String lang,double height){
this.name = name;
this.age = age;
this.gender = gender;
this.lang = lang;
this.height = height;
}
// 선택적 맴버변수 생성자(lang)
Person(String name,String gender,int age,String lang){
this.name = name;
this.gender = gender;
this.age = age;
this.lang = lang;
}
// 선택적 맴버변수 생성자(height)
Person(String name,String gender,int age,double height){
this.name = name;
this.gender = gender;
this.age = age;
this.height = height;
}
}
위와 같이 모든 경우의 수를 모두 만들어주는 생성자인데 문제는 변수가 하나 추가되면 새로운 생성자를 추가해줘야하기에 유연성이 0
그래서 나온 것이 자바빈 패턴(JavaBean)
2. 자바빈 패턴(JavaBean)
public class Person {
private String name;
private String gender;
private int age;
private String lang;
private double height;
public void setName(String name) {
this.name = name;
}
public void setGender(String gender) {
this.gender = gender;
}
public void setAge(int age) {
this.age = age;
}
public void setLang(String lang) {
this.lang = lang;
}
public void setHeight(double height) {
this.height = height;
}
}
대표적으로 setter를 사용하는 친숙한 문법이다. 근데 이 setter를 쓰면 문제가 생긴다.
코드의 가독성 및 길이는 줄어서 확실히 좋지만 불변성을 지키는 객체를 만들기는 힘들다는 것!
가령, Person 객체를 만들면 이 객체를 수정할 수 있는게 아니라 그대로 유지하고 싶은데(불변성을 지키고 싶은데) setter를 이용하면 새로운 객체로 바꿀 수 있기 때문에 문제가 생김
3. 빌더 패턴(Builder Pattern) - Effective Java Style
public class Person {
private final String name;
private final String gender;
private final int age;
public static class Builder{
private final String name;
private final String gender;
private final int age;
private String lang;
private double height;
// 필수 맴버변수
public Builder(String name,String gender,int age){
this.name = name;
this. gender = gender;
this.age = age;
}
public Builder lang(String lang){
this.lang = lang;
return this;
}
public Builder height(double height){
this.height = height;
return this;
}
public Person build(){
return new Person(this);
}
}
private Person(Builder builder){
this.name = builder.name;
this.gender = builder.gender;
this.age = builder.age;
}
}
public class Main {
public static void main(String[] args) {
Person person = new Person.Builder("KIM","M",22)
.lang("M")
.height(170)
.build();
}
}
코드의 가독성도 좋고 일관성과 불변성 만족
근데 여기서 Lombok을 사용하면 더 줄일 수 있다.
4. 빌더 패턴(Builder Pattern) - Lombok Style
@Builder
public class NutritionFacts {
private final int servingSize; //final을 붙이면 필수 매개변수
private final int servings;
private final int calories;
private final int fat;
private final int sodium;
private final int carbohydrate;
private int values; //final을 빼면 선택 매개변수
}
public class NutritionFacts {
private final int servingSize;
private final int servings;
private final int calories;
private final int fat;
private final int sodium;
private final int carbohydrate;
@Builder
public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium, int carbohydrate) {
this.servingSize = servingSize;
this.servings = servings;
// ...
}
}
NutritionFacts facts = NutritionFacts.builder()
.calories(230)
.fat(10)
.build();
@Builder를 클래스 or 생성자에 붙일 수 있다.
하지만, @Builder를 클래스 위에 붙이는건 옳지 않은 방법이라고 한다.
Finally, applying @Builder to a class is as if you added @AllArgsConstructor(access = AccessLevel.PACKAGE) to the class and applied the @Builder annotation to this all-args-constructor. This only works if you haven't written any explicit constructors yourself. If you do have an explicit constructor, put the @Builder annotation on the constructor instead of on the class. Note that if you put both `@Value` and `@Builder` on a class, the package-private constructor that `@Builder` wants to generate 'wins' and suppresses the constructor that `@Value` wants to make.
@Builder
projectlombok.org
JPA 객체 생성 방법
객체를 생성할 때는 다음 3가지 방법 중 사용하게 된다. 생성자 정적 팩토리 메서드 Builder 패턴 엔티티 상황에 따라서 이 중 한가지를 선택하고, 파라미터에 객체 생성에 필요한 데이터를 다 넘
ssdragon.tistory.com
올바른 JPA Entity, @Builder 사용법
Entity 클래스를 작성할 때 어노테이션을 많이 씁니다. 아래와 같이 많이 쓰게 되는데요.이 어노테이션에서 문제 3가지가 보입니다. 이를 개선하기 위해 해결방법을 정리해봅니다.Setter는 그 의도
velog.io
이런식으로 사용하면 될듯?
필수, 선택 매개변수를 가지고 하는거라는데 이 방법은 @Builder를 위에 올려서 사용하는 방법이니 아마 안사용하지않을까?
@Builder(builderMethodName = "")을 사용한다.
@Builder(builderMethodName = "innerBuilder")
public class NutritionFacts {
private final int servingSize; // 필수 매개변수
private final int servings; // 필수 매개변수
@Builder.Default private int calories = 0; // 선택 매개변수
@Builder.Default private int fat = 0; // 선택 매개변수
public static NutritionFactsBuilder builder(int servingSize, int servings) {
return innerBuilder().servings(servings).servingSize(servingSize);
}
}
- builder 함수를 쓰기 위해서는 무조건 servingSize, servings 를 주입 받아야 하는 custom method 를 선언을 해두면, 처음에 선언한 innerBuilder() 를 통해서 객체를 만들고, 다음에 롬복에서 제공하는 builder 패턴을 사용하겠다는 의미이다.
- @Builder.Default 값이 할당 되지 않은 경우 초기값을 설정하겠다는 뜻이다.
그럼 빌더 패턴을 모든 경우에 쓰는게 좋을까? No!!!!
객체를 생성하는 대부분의 경우에는 빌더 패턴을 적용하는 것이 좋다.
물론 예외적인 케이스가 있을 수 있는데, 대표적으로 크게 다음의 2가지 상황에서는 빌더를 구현할 필요가 없다.
- 객체의 생성을 라이브러리로 위임하는 경우
- 변수의 개수가 2개 이하이며, 변경 가능성이 없는 경우
예를 들어 엔티티(Entity) 객체나 도메인(Domain) 객체로부터 DTO를 생성하는 경우라면 직접 빌더를 만들고 하는 작업이 번거로우므로 MapStruct나 Model Mapper와 같은 라이브러리를 통해 생성을 위임할 수 있다. 또한 변수가 늘어날 가능성이 거의 없으며, 변수의 개수가 2개 이하인 경우에는 정적 팩토리 메소드를 사용하는 것이 더 좋을 수도 있다. 빌더의 남용은 오히려 코드를 비대하게 만들 수 있으므로 변수의 개수와 변경 가능성 등을 중점적으로 보고 빌더 패턴을 적용할지 판단하면 된다.
'CS' 카테고리의 다른 글
Access Control 종류(ACL, RBAC, ABAC) (1) | 2024.11.05 |
---|---|
애플리케이션의 종류 (0) | 2024.01.04 |
[CS] 데이터베이스 정규화와 이상현상(Normalization & Anomaly) (0) | 2023.04.19 |
개발하면서 자주 만났던 이슈 및 용어 정리 (0) | 2023.04.19 |
[CS] 네트워크 (1) | 2023.02.28 |