4개의 동일한 필드를 가진 두 Dto의 중복 코드를 효율적으로 관리하려고 한다.
현재는 상속을 통해 공통 필드를 포함하는 ChatResponseDto라는 부모 클래스를 생성하고, 이를 ChatRecommendResponseDto와 ChatLyricsResponseDto가 상속받는 구조이다.
단순하게 공통 필드 코드를 최소화한다는 것에만 중점을 뒀었다.
공통 필드를 갖는 ChatResponseDto
@Getter
@SuperBuilder
public class ChatResponseDto {
@JsonProperty("chat_id")
private Long id;
@JsonProperty("diary_id")
private Long diaryId;
@JsonProperty("created_at")
private LocalDateTime createdAt;
@JsonProperty("is_liked")
private Boolean isLiked;
}
ChatRecommendResponseDto
@SuperBuilder
@Getter
public class ChatRecommendResponseDto extends ChatResponseDto{
@JsonProperty("recommendedSongs")
private List<SongResponseDto> recommendedSongs;
}
ChatLyricsResponseDto
@Getter
@SuperBuilder
public class ChatLyricsResponseDto extends ChatResponseDto {
@JsonProperty("generatedLyrics")
private String generatedLyrics;
}
ChatRecommendResponseDto와 ChatLyricsResponseDto는 각각 필요한 추가 필드만 정의하여 공통 로직을 최소화할 수 있었다.
하지만 상속은 높은 결합도로 인해 코드 유연성과 캡슐화를 제한할 수 있다는 단점이 있다.
ChatResponseDto의 isLiked 필드 타입을 Boolean에서 커스텀 객체로 변경할 경우, 두 자식 Dto에서도 동일한 변경 사항을 수용해야 한다. ChatLyricsResponseDto가 isLiked 필드를 사용하지 않는다고 해도, 상속으로 인해 해당 필드가 자식 클래스에 강제로 포함된다. 필요하지 않은 정보가 자식 클래스에 노출됨으로써 캡슐화가 깨지는 문제를 초래한다.컴포지션를 사용하면 상속의 단점인 강한 결합도를 보완하면서 상속과 동일한 역할인 공통 필드 코드를 최소화할 수 있다.
has-a 관계를 통해 객체를 조립하여 기능을 확장공통 필드가 포함된 ChatResponseDto를 개별 Dto의 구성 요소로 추가했다.
@Getter
@Builder
public class ChatResponseDto {
@JsonProperty("chat_id")
private Long id;
@JsonProperty("diary_id")
private Long diaryId;
@JsonProperty("created_at")
private LocalDateTime createdAt;
@JsonProperty("is_liked")
private Boolean isLiked;
}
ChatRecommendResponseDto
@Builder
@Getter
public class ChatRecommendResponseDto{
@JsonProperty("chat_response")
private ChatResponseDto chatResponseDto; // 개별 구성 요소로 추가
@JsonProperty("recommendedSongs")
private List<SongResponseDto> recommendedSongs;
}
ChatLyricsResponseDto
@Getter
@Builder
public class ChatLyricsResponseDto{
@JsonProperty("chat_response")
private ChatResponseDto chatResponseDto; // 개별 구성 요소로 추가
@JsonProperty("generatedLyrics")
private String generatedLyrics;
}
ChatResponseDto의 구조를 변경하더라도 이를 구성 요소로 포함한 두 자식 Dto에는 영향을 미치지 않으며, 독립적으로 구조 변경이 가능하다.ChatLyricsResponseDto가 isLiked 필드를 사용하지 않더라도 ChatResponseDto를 구성 요소로만 참조하므로, 불필요한 정보가 자식 클래스에 강제로 포함되지 않는다. 따라서 필요한 정보만 노출하여 캡슐화를 유지하고, 코드의 결합도를 낮출 수 있다.컴포지션을 사용하여 상속의 단점을 보완하고, 코드 유연성과 캡슐화를 개선했다. 상속 방식은 현재 공통 필드 관리를 잘 수행하고 있지만, 향후 유지보수성과 확장성을 고려할 때 독립적 코드 관리가 가능한 컴포지션 방식을 채택하는 것이 적합하다고 판단했다.
참고
✔️ 컴포지션