내가 from(), of() 메서드로 네이밍 했던 이유

Kevin·2024년 2월 22일
0
post-thumbnail

그 때 당시에는 Spring은 물론 Java에 대한 개념도 매우 약할 때라 지금도 약하지만

레퍼런스의 코드를 가져다 사용하기에 급급했다.

당시의 나는 해당 개념에 대한 정확한 이해보다는 기능 구현이 우선이 되었기에, 내가 코드에 적용했던 다른 코드나 기술들에 대해 정확히 왜 사용하고, 어떤 기능인지에 대한 것들은 모두 미뤄뒀었다.

그래서 지금부터 내가 잘 모르고 작성했던 코드들에 대한 공부를 차근히 진행하고자 한다.

나는 아직도 너무 무지하고, 개념을 확실히 알고 넘어 가야 한다는 습관이 완벽히 자리 잡지 않았기에

이 시리즈는 한동안 쭉 작성하지 않을까 싶다.


이 시리즈의 일곱번째 글의 주제는 내가 메서드 네이밍으로 왜 `.from()`과 `.of()`을 사용했을까라는 주제이다.

나는 단순히 여러 레퍼런스들에서 이런 식의 네이밍을 하는 코드들을 여러번 봤었기에, 단순히 DTO에서 엔티티 또는 엔티티에서 DTO로 변환해줄 때 이런 식으로 코드를 작성하는 구나라고 생각을 했고,

.from() 메서드와 .of() 으로 네이밍 하는지에 대해서는 이해하지 못하고 있었다.

이제부터 왜 내가 .from() 메서드와 .of() 메서드로 네이밍을 했는지 알아보자.


정적 팩토리 메서드가 뭔데?

.from() 메서드와 .of() 메서드 네이밍의 뜻을 알기 위해서 우리는 먼저 정적 팩토리 메서드라는 친구를 먼저 알아야 한다.

아래는 정적 팩터리 메서드를 활용한 코드이다.

class Post {
    private String title;
    private String writerName;

    private Post(String title, String writerName) {
        this.title = title;
        this.writerName = writerName;
    }

    static Post createAnonymousPost(String title) {
        return new Post(title, "");

    }

    static Book createByTitle(String title, String writerName) {
        return new Post(title, writerName); {
    }
}

위 코드에서는 생성자에 private 접근 제어자를 두어 new 키워드를 이용하여 직접 객체를 생성하는 것을 막고, 정적 메서드를 통해 Post 객체를 생성한 뒤 반환한다.

Post post1 = Post.createAnonymousPost("방가방가"); 
Post post2 = Post.createByTitle("방가루", "kevin");

위 코드들을 보면 정적 팩터리 메서드가 하는 역할을 어느정도 유추할 수 있을 것이다.

정적 팩터리 메서드는 기존 public 메서드로 객체를 생성하는 방법과 달리, public 생성자를 private으로 지정해두고, 사용자가 메서드를 호출하면 해당 객체를 반환해주는 메서드이다.

그러면 public 생성자를 두고 왜 정적 팩터리 메서드를 사용할까?

내가 이해한 정적 팩터리 메서드의 장점들은 아래와 같다.

  1. 정적 팩토리 메서드는 이름을 가질 수 있다.

    → 생성자인 Post(int, int, Random)과 정적 팩터리 메서드인 Post.createAnonymousPost(int, int, Random) 중 어느쪽이 ‘익명 게시글을 반환한다’라는 뜻을 더 잘 설명 할 수 있겠는가

    → 이렇듯 정적 팩토리 메서드는 메서드 사용자로 하여금 어떤 상태의 객체를 반환받을 수 있는지에 대해 유추할 수 있게 한다.

    만약 한 클래스에 시그니처가 같은 생성자가 여러 개 필요할 것 같으면, 생성자를 정적 팩터리 메서드로 바꾸고 각각의 차이를 잘 드러내는 이름을 지어주자.

  2. 정적 팩토리 메서드는 호출될 때마다 인스턴스를 새로 생성하지 않아도 된다.

    → 인스턴스를 미리 만들어 놓거나, 새로 생성한 인스턴스를 캐싱하여 재활용 하는 식으로 불필요한 객체 생성을 피할 수 있다.

    new 키워드를 사용하면, 객체는 무조건 새로 생성된다. 만약, 자주 생성될 것 같은 인스턴스는 클래스 내부에 미리 생성해 놓은 다음 반환한다면 코드를 최적화할 수 있을 것 이다.

     class Post {
         private static final Post post = new Post("", "");
     	  private String title;
     		private String writerName;
     
         private Post(String title, String writerName) {
             this.title = title;
             this.writerName = writerName;
         }
     
         static Post createBasicPost() {
             return new post;
         }
     }
    • 위 코드에서는 미리 Post 객체를 생성해두고, createBasicPost() 메서드 호출시 Post 객체를 반환해준다.

    • 이 방식은 싱글턴에서 사용하는 방법이기도 하다.
      - 싱글턴 → 인스턴스를 오직 하나만 생성할 수 있는 클래스
           public class Singleton {
               
               private static final Singleton instance = new Singleton();
               
               // private constructor to avoid client applications to use constructor
               private Singleton(){}
            
               public static Singleton getInstance(){
                   return instance;
               }
           }
       
    • 인스턴스화 불가 클래스 → 생성자의 접근제어자를 private 으로 설정하여, 외부에서 new 키워드로 새로운 인스턴스를 생성할 수 없게 할 수 있다.

지금까지 정적 팩터리 메서드에 대한 개념이었고, 사실 from() 메서드와 of() 메서드에 대한 개념은 매우 간단하다.

두 메서드의 네임은 정적 팩터리 메서드에서 자주 사용하는 네이밍이다.

from() 메서드는 매개변수를 하나 받아서, 해당 타입의 인스턴스를 반환할 때 사용하는 네이밍이다.
예시 코드 : Data d = Date.from(instant);

of() 메서드는 여러 매개변수를 받아 적합한 타입의 인스턴스를 반환할 때 사용하는 메서드이다.
예시 코드 : Set<Rank> faceCards = EnumSet.of(JACK, QUEEN, KING);

이를 내가 현재 작성중인 코드에 적용 시키면 다음과 같아진다.

기존 코드

public ChatRoomDTO(Long id, String roomName, String lastReceivedChat, List<String> visitedNames, LocalDateTime createdAt, LocalDateTime updateAt) {
            this.id = id;
            this.roomName = roomName;
            this.lastReceivedChat = lastReceivedChat;
            this.visitedNames = visitedNames;
            this.createdAt = createdAt;
            this.updateAt = updateAt;
        }

적용 후 코드

public static ChatRoomResponseDTO.ChatRoomDTO from(ChatRoom chatRoom){

        return ChatRoomDTO
                .builder()
                .id(chatRoom.getId())
                .roomName(chatRoom.getRoomName())
                .lastReceivedChat(chatRoom.getLastReceivedChat())
                .visitedNames(chatRoom.getVisitedNames())
                .createdAt(chatRoom.getCreatedAt())
                .updateAt(chatRoom.getUpdatedAt())
                .build();
    }
profile
Hello, World! \n

0개의 댓글