공부거리
Setter를 지양해야하는 이유
cocodingding
2024. 3. 6. 08:14
Setter를 지양해야하는 이유는 뭘까
- 불변성 유지: Setter를 사용하면 객체의 상태를 변경할 수 있음. 이는 코드를 이해하기 어렵게 만들고 예상치 못한 부작용을 초래할 수 있음. 객체의 불변성을 유지하면 코드의 예측 가능성이 높아지고 디버깅이 용이함.
- 캡슐화 위반: Setter를 남용하면 객체의 내부 상태를 외부에서 직접 변경할 수 있음. 이는 객체 지향 프로그래밍의 중요한 원칙인 캡슐화를 위반하는 것임. 캡슐화는 객체의 내부 상태를 외부로부터 숨기고 외부에서는 객체의 메서드를 통해서만 상호 작용할 수 있도록 하는데, Setter를 남용하면 이 원칙을 해칠 수 있음.
- 복잡성 증가: Setter를 사용하면 언제든지 객체의 상태를 변경할 수 있음. 이는 코드의 복잡성을 증가시킬 수 있음. 예측할 수 없는 상태 변화는 코드를 이해하기 어렵게 만듦.
- 디버깅과 테스트 어려움: Setter를 남용하면 상태의 변경이 어디서 발생했는지 추적하기 어려울 수 있음. 이는 디버깅을 어렵게 만들고 테스트 케이스를 작성하기도 어려움.
따라서 Setter를 지양하고 불변 객체를 선호하는 것이 좋음. 객체의 상태를 초기화한 후 변경할 수 없도록 설계하여 객체의 불변성을 유지하는 것이 중요함.
Setter의 대안
- 생성자
- Builder
생성자
//유저 생성 예시
//Entity
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Table(name = "users")
public class User extends Auditable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long userId;
private String userName;
private String password;
private String userEmail;
private String userPhone;
private String userAddress;
private String postcode;
@Enumerated(value = EnumType.STRING)
private UserRoleEnum role;
private String streamKey;
@OneToMany(fetch = FetchType.LAZY, mappedBy = "user")
private List<Broadcast> broadcastList;
@OneToMany(fetch = FetchType.LAZY, mappedBy = "user")
private List<Orders> orderList;
private String refreshToken;
//엔티티에 생성자를 생성해준다.
public User(String userName, String userEmail, String password, String streamKey, String userPhone, String userAddress, String postcode) {
this.userName = userName;
this.userEmail = userEmail;
this.password = password;
this.role = UserRoleEnum.USER;
this.streamKey = streamKey;
this.userPhone = userPhone;
this.userAddress = userAddress;
this.postcode = postcode;
}
//service
@Transactional
public void createUser(UserRequestDto requestDto) {
User user = userRepository.save(
//이런식으로 생성자에 값을 넣어줘서 객체를 생성
new User(
requestDto.getUserName(),
requestDto.getUserEmail(),
passwordEncoder.encode(requestDto.getPassword()),
UUID.randomUUID() + requestDto.getUserEmail().split("@")[0],
requestDto.getUserPhone(),
requestDto.getUserAddress(),
requestDto.getPostcode()
)
);
new UserResponseDto(user);
}
멤버변수가 많아지면 코드가 길어지고
상황에 따라 엔티티의 변수의 갯수가 다른 생성자가 여러개 생길 수도 있어,
개인에 따라 가독성이 떨어진다고 느껴질 수도 있다.
Builder
//생성자에 Builder 어노테이션을 적용
@Builder
public Trade(Commission commission, Member member, String title, String content, String authorEmail, Status status) {
this.commission = commission;
this.member = member;
this.title = title;
this.content = content;
this.authorEmail = authorEmail;
this.status = status;
}
//service
public TradeResponseDto createTrade(TradePostDto tradePostDto) {
Commission commission = commissionService.existCommission(tradePostDto.getCommissionId());//커미션 검증
String userEmail = getAuthMember();
Member member = memberService.findVerifyMemberByEmail(userEmail);
//빌더패턴을 이용해 값을 넣어준다.
Trade trade = Trade.builder()
.title(tradePostDto.getTitle())
.content(tradePostDto.getContent())
.commission(commission)
.member(member)
.authorEmail(commission.getMember().getEmail())
.status(Status.Waiting_Acceptance)
.build();
tradeRepository.save(trade);
return TradeResponseDto.fromEntity(trade);
}
빌더패턴은 요구사항에 맞게 필요한 데이터만 이용하여 유연한 클래스 생성이 가능하게된다.
때문에 다양한 생성자들이 사라지고 전체 생성자 하나만으로 필요한 변수만을 가진 객체를 생성할 수 있어,
유지보수 및 가독성이 향상됩니다.
또한 객체를 생성할 때 인자 값의 순서가 상관없다는 장점이 있습니다.
다만 생성코드 자체는 길어질 수 있어 이 또한 가독성 별로라는 사람도 있을 수 있다.