평소 아래와 같이 Request를 Service단에서 Entity로 변환, Repository에서 사용하는 방향을 생각했다.
public record PostCreateRequest(
@NotBlank(message = "제목은 필수입니다.") String title,
@NotBlank(message = "내용은 필수입니다.") String content
) {
public Post toPostEntity(User user) {
return new Post(title, content, user);
}
}
팀원의 코드리뷰를 달던 중 다음과 같이 static을 활용해 메소드를 작성한 것을 보게 됐다.
public record OrderDto(
Long menuId,
String menuName,
String menuImg,
Integer menuPrice
) {
public static OrderDto of(Menu menu){
return new OrderDto(
menu.getMenuId(),
menu.getMenuName(),
menu.getSingleImgUrl(),
menu.getSinglePrice()
);
}
}
어떤 방법이 더 좋은 방법일까?
객채명.메소드명()
으로 사용할 수 있는 메소드// Instance Method
Server server = new Server();
server.example();
// Class Method
Server.example();
클래스 메소드를 통해 객체를 생성할 경우, 이러한 메소드를 정적 팩토리 메소드라고한다.
= 클래스의 인스턴스를 반환하는 단순한 정적 메소드
class Product {
private Product() { }
...
public static of() {
return new Product()
}
}
Product product = Product.of();
그렇다면 여기서 의문이 하나 생긴다.
우리한테는 생성자가 있는데 왜 정적 팩토리 메소드를 사용해야 하는가?
이펙티브 자바에 다음과 같은 구절이 있다고 한다.
Consider static factory methods instead of constructors
(생성자 보다 정적 팩토리 메소드를 고려하라)
지금부터 그 이유에 대해 공부해 보자.
public class Car {
private final CarName carName;
private final CarPosition carPosition;
private Car (String carName) {
this(new CarName(carName));
}
private Car (String carName, int carPosition) {
this(new CarName(carName), new CarPosition(carPosition));
}
public static Car carName (String carName) {
return new Car(carName);
}
public static Car carNameAndPosition (String carName, int carPosition) {
return new Car(carName, carPosition);
}
}
class Day {
private String day;
public Day(String day) { this.day = day; }
public String getDay() { return day; }
}
// Day 객체를 생성하고 관리하는 경량 패턴 팩토리 클래스
class DayFactory {
// Day 객체를 저장하는 캐싱 저장소 역할
private static final Map<String, Day> cache = new HashMap<>();
// 자주 사용될것 같은 Day 객체 몇가지를 미리 등록한다
static {
cache.put("Monday", new Day("Monday"));
cache.put("Tuesday", new Day("Tuesday"));
cache.put("Wednesday", new Day("Wednesday"));
}
// 정적 팩토리 메서드 (인스턴스에 대해 철저한 관리)
public static Day from(String day) {
if(cache.containsKey(day)) {
// 캐시 되어있으면 그대로 가져와 반환
System.out.println("해당 요일은 캐싱되어 있습니다.");
return cache.get(day);
} else {
// 캐시 되어 있지 않으면 새로 생성하고 캐싱하고 반환
System.out.println("해당 요일은 캐싱되어 있지 않아 새로 생성하였습니다.");
Day d = new Day(day);
cache.put(day, d);
return d;
}
}
}
getInsance()
라는 정적 팩토리 메소드를 사용해 오로지 하나의 객체만 반환하도록 해 메모리를 아끼도록 유도할 수 있음class Singleton {
private static Singleton instance;
private Singleton() {}
// 정적 팩토리 메서드
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
public class Diamond {
private final int score;
public Diamond(int score) {
this.score = score;
}
public String description() {
return String.format("%s : %d", this.getClass().getSimpleName(), score);
}
}
public class Silver {
private final int score;
public Silver(int score) {
this.score = score;
}
public String description() {
return String.format("%s : %d", this.getClass().getSimpleName(), score);
}
}
public class App {
public static void main(String[] args) {
Diamond diamond = new Diamond(1900);
Silver silver = new Silver(1300);
System.out.println(diamond);
System.out.println(silver);
}
}
위와 같이 Diamond와 Silver를 main에서 직접적으로 사용한다.
여기서 발생하는 문제는 구현 클래스가 그대로 드러난다는 것이다.
다음과 같이 인터페이스를 반환하도록 수정할 수 있다.
암묵적으로 매개변수가 여러개일 때는 of, 1개일 때는 from을 사용하는 규약이 존재한다.
하지만 개인적으로 코드는 “명확성”이 중요하다고 생각하기 때문에..
팀원들과 논의해 네이밍을 수정하고, 해당 네이밍을 문서화해서 정리해두는 것도 나쁘지 않겠다는 생각이 든다.
왜 규약을 그렇게 했지.. ㅋ.ㅋ