생성자 오버로딩 이슈를 정적 팩토리 메소드로 해결하기

HeavyJ·2023년 5월 24일
0

Java

목록 보기
1/3
post-thumbnail

오버로딩

자바의 오버로딩은 여러 메소드를 하나의 이름으로 사용할 수 있다는 장점 때문에 메소드 이름을 절약할 수 있습니다.

자바에서 오버로딩으로 다형성을 지원하여 재사용성이 증가하고 유지보수가 간단해집니다.

다만, 이 오버로딩에는 조건이 존재합니다.

  1. 메소드 이름은 같아야 한다.
  2. 메소드의 인자값이 달라야한다. ( 3가지 중 하나가 달라야 합니다 )
    • 순서
    • 개수
    • 타입
  3. 리턴 타입은 상관이 없습니다.

하지만, 같은 타입의 매개변수인데 매개변수의 형태 혹은 종류에 따라 다른 결과를 내고 싶을 때가 있습니다.

저 같은 경우에는 이전 글 정보와 다음 글 정보를 필드에 담아서 게시글 상세정보 Dto를 생성할 때 생성자 오버로딩에서 문제가 발생했습니다.

PostInfoDto.class

public class PostInfoDto{
	private Long id;
    
    private Long boardId;
    
    // .....
    
    // 이전 글 id
    private Long previousId;
    
    // 이전 글 제목
    private String previousTitle;
    
    // 다음 글 id
    private Long nextId;
    
    // 다음 글 제목
    private String nextTitle;


	public PostInfoDto(Post post){
    	this.id = post.getId();
        this.boardId = post.getBoard().getId();
    }
    
    public PostInfoDto(Post post, Post previousPost, Post nextPost){
    	this.id = post.getId();
        this.boardId = post.getBoard().getId();
        this.previousId = previousPost.getId();
        this.previousTitle = previousPost.getPostTitle();
        this.nextId = nextPost.getId();
        this.nextTitle = nextPost.getPostTitle();
    }

이런식으로 이전글 다음글 객체를 생성자에 매개변수로 넣었을 때 발생하는 문제는 이전글이나 다음글 중 하나가없을(null) 경우입니다.

객체가 null일 경우 get 메소드로 필드 값을 가져올 수 없기 때문에 에러가 발생합니다.

따라서 추가적인 생성자가 필요한데 이 때 생성자 오버로딩을 고려할 수 있습니다.

public PostInfoDto(Post post, Post previousPost){
	this.id = post.getId();
    this.boardId = post.getBoard().getId();
    this.previousId = previousPost.getId();
    this.previousTitle = previousPost.getPostTitle();
}

public PostInfoDto(Post post, Post nextPost){
	this.id = post.getId();
    this.boardId = post.getBoard().getId();
    this.nextId = nextPost.getId();
    this.nextTitle = nextPost.getPostTitle();
}

하지만, 이렇게 설계를 하게 되면 오버로딩의 규칙에 위반됩니다.

  • 같은 타입의 매개변수 존재
  • 같은 개수의 매개변수 존재

순서를 바꿔서 오버로딩을 하는 방법이 있을 수 있지만, 이 방법은 혼란을 야기할 수 있고 위 코드를 보면 같은 타입의 매개변수들이 2개가 있기 때문에 순서를 바꾸는 것도 의미가 없습니다.

따라서 이전 글이 없는 경우 / 다음 글이 없는 경우, 2가지 경우에 대한 객체 생성을 하기 위해 정적 팩토리 메소드 방식을 고려해볼 수 있습니다.

정적 팩토리 메소드

정적 팩토리 메서드는 객체 생성 역할을 하는 클래스 메소드입니다.

자바에는 객체 생성을 해주는 생성자가 존재하는데 왜 정적 팩토리 메소드를 쓰기도 할까요??
3가지 장점에 대해서 알아보겠습니다.

1. 이름을 가질 수 있습니다.

생성자로 객체를 생성하는 방법은 생성자를 구별하기 쉽지 않습니다. 생성자 오버로딩을 통해서 여러 목적을 가진 생성자가 있다는 것을 확인할 수 있지만 이 생성자는 어떤 역할을 하고, 저 생성자는 어떤 역할을 하는지 파악하기 위해서는 생성자의 내부 구조를 분석해야 합니다.

정적 팩토리 메소드는 객체의 목적에 따라 이름을 가질 수 있기 때문에 생성의 목적을 빠르게 파악할 수 있다는 장점이 있습니다.

2. 호출할 때마다 새로운 객체를 생성할 필요가 없습니다.

"정적" 팩토리 메소드다 보니까 매번 새로운 인스턴스를 생성하지 않기 때문에 메모리 측면에서 효율적일 수 있습니다.
객체를 생성하는 중복 과정을 줄일 수 있습니다.

하지만, 정적 팩토리 메소드는 생성자를 private으로 선언하기 때문에 상속을 통한 확장성을 가져갈 수 없다는 단점이 있습니다.

3. 객체 생성을 캡슐화할 수 있습니다.

생성자를 이용할 때는 내부 구조를 알아야 매개변수를 넣어서 객체를 생성할 수 있습니다.

정적 팩토리 메소드를 사용하면 생성자를 클래스의 메소드 안에 숨겨 내부 상태를 숨긴 채 객체 생성이 가능합니다. 따라서 데이터의 은닉이 가능합니다.

정적 팩토리 메소드 적용하기

PostController.class

// ..
// 생략

	// 이전 글
	Post previousPost = postService.getPreviousPostByPostId(boardId, post);
    // 다음 글
    Post nextPost = postService.getNextPostByPostId(boardId, post);

	PostInfoDto postInfoDto;

    if (previousPost == null) {
		postInfoDto = PostInfoDto.createFromNextPost(post, postHeartSet, nextPost);
	}
	else if(nextPost == null){
		postInfoDto = PostInfoDto.createFromPreviousPost(post, postHeartSet, previousPost);
	}
	else{
		postInfoDto = PostInfoDto.createPostInfoDto(post, postHeartSet, previousPost, nextPost);
	}

PostController에서 이전 글과 다음 글의 null 여부에 따라서 다른 팩토리 메소드를 호출하는 형태로 설계했습니다.

  • createFromNextPost()는 이전 글이 없는 경우
  • createFromPreviousPost()는 다음 글이 없는 경우
  • createPostInfoDto()는 이전 글과 다음 글이 모두 존재하는 경우

PostInfoDto.class

    private PostInfoDto(Post post, Post previousPost, Post nextPost){
    	this.id = post.getId();
        this.boardId = post.getBoard().getId();
        this.previousId = previousPost.getId();
        this.previousTitle = previousPost.getPostTitle();
        this.nextId = nextPost.getId();
        this.nextTitle = nextPost.getPostTitle();
    }

public static PostInfoDto createFromPreviousPost(Post post, Post previousPost){
        //다음글이 없습니다 정적 페토리 메소드
        Post nextPost = Post.builder()
                .id(null)
                .postTitle(null)
                .build();
        return new PostInfoDto(post, previousPost, nextPost);
    }
    public static PostInfoDto createFromNextPost(Post post, Post nextPost){
        //이전글이 없습니다 정적 페토리 메소드
        Post previousPost = Post.builder()
                .id(null)
                .postTitle(null)
                .build();
        return new PostInfoDto(post, postHeartSet, previousPost, nextPost);
    }
    public static PostInfoDto createPostInfoDto(Post post, Post previousPost, Post nextPost){
        return new PostInfoDto(post, previousPost, nextPost);
    }

PostInfoDto에서는 정적 팩토리 메소드의 내부 구조에 대해서 알아보겠습니다.

기존의 생성자를 private 접근 제어자로 바꿔줍니다.

createFromPreviousPost()에서는 nextPost의 필드 값을 null로 만들어준 뒤 생성자를 return 합니다.

createFromNextPost()에서는 previouPost의 필드 값을 null로 만들어준 뒤 생성자를 return 합니다.

createPostInfoDto()에서는 따로 null처리 할 필요가 없기 때문에 바로 생성자를 return 해줍니다.

정적 팩토리 메소드로 이름을 설정해줘서 가독성 좋고 편하게 목적에 맞는 객체 생성이 가능해졌습니다.
그리고 매번 new() 키워드로 객체 생성을 하지 않아도 됐고 Controller 클래스에서 생성자의 구조에 대한 은닉까지 가능해져서 캡슐화까지 이뤄졌습니다😁


기존에는 생성자 오버로딩 방식을 통해서 목적에 맞는 객체 생성을 진행했었는데 정적 팩토리 메소드를 적절히 섞는 것도 필요하다고 느꼈습니다.

네이밍이 가능하다는 점 때문에 협업할 때 가독성에 큰 도움을 줄 것 같기도 하네요 👍

profile
There are no two words in the English language more harmful than “good job”.

0개의 댓글