<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>nakta-y.log</title>
        <link>https://velog.io/</link>
        <description>느려도 확실히</description>
        <lastBuildDate>Wed, 13 Dec 2023 04:28:26 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>nakta-y.log</title>
            <url>https://velog.velcdn.com/images/nakta-y/profile/ce9b3f6c-507d-49c1-9b9f-f1ac3d53228d/social_profile.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. nakta-y.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/nakta-y" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[Lv.3] 가장 긴 팰린드롬]]></title>
            <link>https://velog.io/@nakta-y/Lv.3-%EA%B0%80%EC%9E%A5-%EA%B8%B4-%ED%8C%B0%EB%A6%B0%EB%93%9C%EB%A1%AC</link>
            <guid>https://velog.io/@nakta-y/Lv.3-%EA%B0%80%EC%9E%A5-%EA%B8%B4-%ED%8C%B0%EB%A6%B0%EB%93%9C%EB%A1%AC</guid>
            <pubDate>Wed, 13 Dec 2023 04:28:26 GMT</pubDate>
            <description><![CDATA[<h3 id="문제-설명">문제 설명</h3>
<p>앞뒤를 뒤집어도 똑같은 문자열을 팰린드롬(palindrome)이라고 합니다.
문자열 s가 주어질 때, s의 부분문자열(Substring)중 가장 긴 팰린드롬의 길이를 return 하는 solution 함수를 완성해 주세요.</p>
<p>예를들면, 문자열 s가 &quot;abcdcba&quot;이면 7을 return하고 &quot;abacde&quot;이면 3을 return합니다.</p>
<br/>

<h3 id="제한사항">제한사항</h3>
<ul>
<li>문자열 s의 길이 : 2,500 이하의 자연수</li>
<li>문자열 s는 알파벳 소문자로만 구성</li>
</ul>
<br/>

<h3 id="입출력-예">입출력 예</h3>
<p><img src="https://velog.velcdn.com/images/nakta-y/post/953e5f71-d51e-426b-84a7-878aedc5d797/image.png" alt=""></p>
<br/>

<h3 id="문제-풀이">문제 풀이</h3>
<p>이 문제는 통과된게 신기할 정도로 무작정 풀었던 것 같다.
문자열의 첫 부분과 끝 부분의 기준을 잡고 길이를 하나씩 줄여나가면서
해당 부분들의 <code>char</code> 값이 같다면 팰린드롬을 검사하는 방식이다.</p>
<h4 id="풀이-코드">풀이 코드</h4>
<pre><code class="language-java">class Solution {
    public int solution(String s) {
        int answer = 1;

        for (int i = 0; i &lt; s.length(); i++) {
            for (int j = s.length() - 1; j &gt; i; j--) {
                if (s.charAt(i) == s.charAt(j)) {
                    String subString = s.substring(i, j + 1);
                    if (isPalindrome(subString)) {
                        answer = Math.max(answer, subString.length());
                        break;
                    }
                }
            }
        }
        return answer;
    }

    private boolean isPalindrome(String s) {
        for (int i = 0; i &lt; (s.length() / 2); i++) {
            if (s.charAt(i) != s.charAt(s.length() - 1 - i)) {
                return false;
            }
        }
        return true;
    }
}</code></pre>
<br/>
<br/>

<h3 id="dp-문제-풀이">DP 문제 풀이</h3>
<p>DP 풀이 방식은 다른 분의 풀이 코드를 참조했다.
먼저, <code>문자열 길이</code> x <code>문자열 길이</code> 의 <code>dp</code> 2차원 배열을 생성한다.
다음으로, 1자리 숫자들은 그 자체로 팰린드롬 이므로 dp[index][index] 위치에 1을 기입해준다.
그리고, 2자리 숫자들은 붙어있는 문자들을 비교하여 팰린드롬 이라면 dp[index][index+1] 위치에 1을 기입해준다.
마지막으로, 3자리 이상 숫자들은 <code>3</code>부터 <code>문자열 길이</code> 만큼 증가하면서 해당 문자열의 첫번째 값과 마지막 값을 비교하고 같다면 <code>dp</code>를 참조하여 내부에 있는 문자열이 팰린드롬인지 확인하게 된다.</p>
<h4 id="dp-풀이-코드">DP 풀이 코드</h4>
<pre><code class="language-java">class Solution {
    public static int solution(String s) {
        int answer = 1;
        int len = s.length();
        char[] a = s.toCharArray();

        int[][] dp = new int[len][len];

        // 1. 1자리
        for (int i = 0; i &lt; len; i++)
            dp[i][i] = 1;

        // 2. 2자리
        for (int i = 0; i &lt; len - 1; i++) {
            if (a[i] == a[i + 1]) {
                dp[i][i + 1] = 1;
                answer = 2;
            }
        }
        // 3. 3자리 이상
        for (int k = 3; k &lt;= len; k++) {
            for (int i = 0; i &lt;= len - k; i++) {
                int j = i + k - 1; // k길이 만큼 떨어진 index
                if (a[i] == a[j] &amp;&amp; dp[i + 1][j - 1] == 1) { // 문자열이 같고, [i-1][j+1] 가 팰린드롬이라면
                    dp[i][j] = 1;
                    answer = k;
                }
            }
        }

        return answer;
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Lv.2] 리코쳇 로봇]]></title>
            <link>https://velog.io/@nakta-y/Lv.2-%EB%A6%AC%EC%BD%94%EC%B3%87-%EB%A1%9C%EB%B4%87</link>
            <guid>https://velog.io/@nakta-y/Lv.2-%EB%A6%AC%EC%BD%94%EC%B3%87-%EB%A1%9C%EB%B4%87</guid>
            <pubDate>Fri, 08 Dec 2023 08:55:23 GMT</pubDate>
            <description><![CDATA[<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/169199">https://school.programmers.co.kr/learn/courses/30/lessons/169199</a></p>
<h3 id="문제-설명">문제 설명</h3>
<p>리코쳇 로봇이라는 보드게임이 있습니다.</p>
<p>이 보드게임은 격자모양 게임판 위에서 말을 움직이는 게임으로, 시작 위치에서 목표 위치까지 최소 몇 번만에 도달할 수 있는지 말하는 게임입니다.</p>
<p>이 게임에서 말의 움직임은 상, 하, 좌, 우 4방향 중 하나를 선택해서 게임판 위의 장애물이나 맨 끝에 부딪힐 때까지 미끄러져 이동하는 것을 한 번의 이동으로 칩니다.</p>
<p>다음은 보드게임판을 나타낸 예시입니다.</p>
<blockquote>
<p>. . . D . . R
  . D . G . . .
  . . . . D . D
  D . . . . D .
  . . D . . . .</p>
</blockquote>
<p>여기서 &quot;.&quot;은 빈 공간을, &quot;R&quot;은 로봇의 처음 위치를, &quot;D&quot;는 장애물의 위치를, &quot;G&quot;는 목표지점을 나타냅니다.
위 예시에서는 &quot;R&quot; 위치에서 아래, 왼쪽, 위, 왼쪽, 아래, 오른쪽, 위 순서로 움직이면 7번 만에 &quot;G&quot; 위치에 멈춰 설 수 있으며, 이것이 최소 움직임 중 하나입니다.</p>
<p>게임판의 상태를 나타내는 문자열 배열 board가 주어졌을 때, 말이 목표위치에 도달하는데 최소 몇 번 이동해야 하는지 return 하는 solution함수를 완성하세요. 만약 목표위치에 도달할 수 없다면 -1을 return 해주세요.</p>
<br/>

<h3 id="제한사항">제한사항</h3>
<ul>
<li>3 ≤ board의 길이 ≤ 100<ul>
<li>3 ≤ board의 원소의 길이 ≤ 100</li>
<li>board의 원소의 길이는 모두 동일합니다.</li>
<li>문자열은 &quot;.&quot;, &quot;D&quot;, &quot;R&quot;, &quot;G&quot;로만 구성되어 있으며 각각 빈 공간, 장애물, 로봇의 처음 위치, 목표 지점을 나타냅니다.</li>
<li>&quot;R&quot;과 &quot;G&quot;는 한 번씩 등장합니다.</li>
</ul>
</li>
</ul>
<br/>

<h3 id="입출력-예">입출력 예</h3>
<p><img src="https://velog.velcdn.com/images/nakta-y/post/0ca6a011-078d-40b3-916d-c6831ab2d9b6/image.png" alt=""></p>
<br/>

<h3 id="문제-풀이">문제 풀이</h3>
<p>해당 문제는 기존 BFS와 방식이 거의 같아서 크게 특별할 것이 없는 문제이다.
시작 위치에서 목표 위치까지 가는 방식에서 미끄러지는 로직을 추가만 하면 된다.
유의할 점은 가장 처음 시작 위치 또는 벽에 막혀 더이상 미끄러지지 않을 때만 <code>Queue</code> 에서 노드를 꺼내야 한다는 점이다.</p>
<br/>

<h4 id="풀이-코드">풀이 코드</h4>
<pre><code class="language-java">class Node {
    int x;
    int y;
    int[] currentDirection;
    int count;

    public Node(int x, int y, int[] currentDirection, int count) {
        this.x = x;
        this.y = y;
        this.currentDirection = currentDirection;
        this.count = count;
    }
}

class Solution {
    public int solution(String[] board) {
        int answer = -1;
        Queue&lt;Node&gt; bfsQueue = new LinkedList&lt;&gt;();
        boolean[][] visited = new boolean[board.length][board[0].length()];
        int[] startPosition = new int[2];
        int[] xDirection = {0, 0, 1, -1};
        int[] yDirection = {1, -1, 0, 0};

        // find start position
        for (int i = 0; i &lt; board.length; i++) {
            for (int j = 0; j &lt; board[i].length(); j++) {
                if (board[i].charAt(j) == &#39;R&#39;) {
                    startPosition[0] = i;
                    startPosition[1] = j;
                }
            }
        }

        // BFS
        bfsQueue.add(new Node(startPosition[0], startPosition[1], new int[]{0, 0}, 0));

        while (!bfsQueue.isEmpty()) {
            // 미끄러져 이동
            Node peekNode = bfsQueue.peek();
            if (peekNode.currentDirection[0] != peekNode.currentDirection[1]
                    &amp;&amp; isSafePath(peekNode.x + peekNode.currentDirection[0], peekNode.y + peekNode.currentDirection[1], board)) {
                peekNode.x += peekNode.currentDirection[0];
                peekNode.y += peekNode.currentDirection[1];
                continue;
            }

            Node node = bfsQueue.poll();

            // 목표 도달 체크
            if (board[node.x].charAt(node.y) == &#39;G&#39;) {
                answer = node.count;
                break;
            }

            if (visited[node.x][node.y]) continue;
            visited[node.x][node.y] = true;

            // 방향 전환
            for (int i = 0; i &lt; 4; i++) {
                int x = node.x + xDirection[i];
                int y = node.y + yDirection[i];

                if (!isSafePath(x, y, board)) {
                    continue;
                }

                bfsQueue.add(new Node(x, y, new int[]{xDirection[i], yDirection[i]}, node.count + 1));
            }
        }

        return answer;
    }

    private boolean isSafePath(int x, int y, String[] board) {
        return x &gt;= 0 &amp;&amp; x &lt; board.length &amp;&amp; y &gt;= 0 &amp;&amp; y &lt; board[x].length() &amp;&amp; board[x].charAt(y) != &#39;D&#39;;
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Lv.3] 입국심사 - JAVA]]></title>
            <link>https://velog.io/@nakta-y/Lv.3-%EC%9E%85%EA%B5%AD%EC%8B%AC%EC%82%AC</link>
            <guid>https://velog.io/@nakta-y/Lv.3-%EC%9E%85%EA%B5%AD%EC%8B%AC%EC%82%AC</guid>
            <pubDate>Fri, 08 Dec 2023 08:44:56 GMT</pubDate>
            <description><![CDATA[<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/43238">https://school.programmers.co.kr/learn/courses/30/lessons/43238</a></p>
<h3 id="문제-설명">문제 설명</h3>
<p>n명이 입국심사를 위해 줄을 서서 기다리고 있습니다. 각 입국심사대에 있는 심사관마다 심사하는데 걸리는 시간은 다릅니다.</p>
<p>처음에 모든 심사대는 비어있습니다. 한 심사대에서는 동시에 한 명만 심사를 할 수 있습니다. 가장 앞에 서 있는 사람은 비어 있는 심사대로 가서 심사를 받을 수 있습니다. 하지만 더 빨리 끝나는 심사대가 있으면 기다렸다가 그곳으로 가서 심사를 받을 수도 있습니다.</p>
<p>모든 사람이 심사를 받는데 걸리는 시간을 최소로 하고 싶습니다.</p>
<p>입국심사를 기다리는 사람 수 n, 각 심사관이 한 명을 심사하는데 걸리는 시간이 담긴 배열 times가 매개변수로 주어질 때, 모든 사람이 심사를 받는데 걸리는 시간의 최솟값을 return 하도록 solution 함수를 작성해주세요.</p>
<br/>

<h3 id="제한사항">제한사항</h3>
<ul>
<li>입국심사를 기다리는 사람은 1명 이상 1,000,000,000명 이하입니다.</li>
<li>각 심사관이 한 명을 심사하는데 걸리는 시간은 1분 이상 1,000,000,000분 이하입니다.</li>
<li>심사관은 1명 이상 100,000명 이하입니다.</li>
</ul>
<br/>

<h3 id="입출력-예">입출력 예</h3>
<p><img src="https://velog.velcdn.com/images/nakta-y/post/a389e9a9-b036-4085-8953-2edef19bf1a8/image.png" alt=""></p>
<br/>

<h3 id="문제-풀이">문제 풀이</h3>
<p>해당 문제의 제한사항을 봤을 때, 주어지는 입국심사를 기다리는 사람의 수를 n으로 잡았을 때 O(n) 알고리즘도 시간 초과가 나버린다.</p>
<p>문제가 애초에 <code>이분탐색</code> 카테고리로 분류되어 있어서 어떤 알고리즘을 써야하는지는 알고 있었지만, 막상 적용해보려니 어떤 값을 기준으로 이분탐색을 해야될지 많이 헷갈렸었다. </p>
<p>현재 문제에서 요구되는 값은 <code>시간</code> 이므로 시간을 기준으로 이분탐색을 진행하였다. 시간을 기준으로 잡기 위해서는 <strong>시작점</strong>과 <strong>끝점</strong>을 알아야하는데, 시작점은 가장 빠르게 끝낼 수 있는 시간인 1 값을 넣어주고 끝점은 심사관 중 가장 심사가 오래걸리는 사람이 모든 대기자 수를 처리하는 값을 넣어줘야한다. </p>
<p>따라서 처음에 <code>times</code> 를 정렬하고 시작점과 끝점을 정의해서 범위를 반씩 줄여나가면 모든 심사관이 모든 대기자 수를 심사해주는 최솟값으로 수렴하게 된다.</p>
<br/>

<h4 id="풀이-코드">풀이 코드</h4>
<pre><code class="language-java">class Solution {
    public long solution(int n, int[] times) {
        long answer = 1;
        Arrays.sort(times);
        long min = 1;
        long max = (long) times[0] * n;

        while (min &lt;= max) {
            long mid = (min + max) / 2;
            long temp = 0;

            for (int i = 0; i &lt; times.length; i++) {
                temp += mid / times[i];
            }

            if (temp &lt; n) {
                min = mid + 1;
            } else {
                answer = mid;
                max = mid - 1;
            }
        }


        return answer;
    }
}
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Lv.3] 경주로 건설 - JAVA]]></title>
            <link>https://velog.io/@nakta-y/Lv.3-%EA%B2%BD%EC%A3%BC%EB%A1%9C-%EA%B1%B4%EC%84%A4</link>
            <guid>https://velog.io/@nakta-y/Lv.3-%EA%B2%BD%EC%A3%BC%EB%A1%9C-%EA%B1%B4%EC%84%A4</guid>
            <pubDate>Mon, 27 Nov 2023 11:33:10 GMT</pubDate>
            <description><![CDATA[<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/67259">https://school.programmers.co.kr/learn/courses/30/lessons/67259</a></p>
<h3 id="문제-설명">문제 설명</h3>
<p>건설회사의 설계사인 죠르디는 고객사로부터 자동차 경주로 건설에 필요한 견적을 의뢰받았습니다.</p>
<p>제공된 경주로 설계 도면에 따르면 경주로 부지는 N x N 크기의 정사각형 격자 형태이며 각 격자는 1 x 1 크기입니다.</p>
<p>설계 도면에는 각 격자의 칸은 0 또는 1 로 채워져 있으며, 0은 칸이 비어 있음을 1은 해당 칸이 벽으로 채워져 있음을 나타냅니다.</p>
<p>경주로의 출발점은 (0, 0) 칸(좌측 상단)이며, 도착점은 (N-1, N-1) 칸(우측 하단)입니다. 죠르디는 출발점인 (0, 0) 칸에서 출발한 자동차가 도착점인 (N-1, N-1) 칸까지 무사히 도달할 수 있게 중간에 끊기지 않도록 경주로를 건설해야 합니다.</p>
<p>경주로는 상, 하, 좌, 우로 인접한 두 빈 칸을 연결하여 건설할 수 있으며, 벽이 있는 칸에는 경주로를 건설할 수 없습니다.</p>
<p>이때, 인접한 두 빈 칸을 상하 또는 좌우로 연결한 경주로를 직선 도로 라고 합니다.</p>
<p>또한 두 직선 도로가 서로 직각으로 만나는 지점을 코너 라고 부릅니다.</p>
<p>건설 비용을 계산해 보니 직선 도로 하나를 만들 때는 100원이 소요되며, 코너를 하나 만들 때는 500원이 추가로 듭니다.</p>
<p>죠르디는 견적서 작성을 위해 경주로를 건설하는 데 필요한 최소 비용을 계산해야 합니다.</p>
<p>예를 들어, 아래 그림은 직선 도로 6개와 코너 4개로 구성된 임의의 경주로 예시이며, 건설 비용은 6 x 100 + 4 x 500 = 2600원 입니다.</p>
<br/>

<h3 id="제한사항">제한사항</h3>
<ul>
<li>board는 2차원 정사각 배열로 배열의 크기는 3 이상 25 이하입니다.</li>
<li>board 배열의 각 원소의 값은 0 또는 1 입니다.<ul>
<li>도면의 가장 왼쪽 상단 좌표는 (0, 0)이며, 가장 우측 하단 좌표는 (N-1, N-1) 입니다.</li>
<li>원소의 값 0은 칸이 비어 있어 도로 연결이 가능함을 1은 칸이 벽으로 채워져 있어 도로 연결이 불가능함을 나타냅니다.</li>
</ul>
</li>
<li>board는 항상 출발점에서 도착점까지 경주로를 건설할 수 있는 형태로 주어집니다.</li>
<li>출발점과 도착점 칸의 원소의 값은 항상 0으로 주어집니다.</li>
</ul>
<BR/>

<h3 id="입출력-예">입출력 예</h3>
<p><img src="https://velog.velcdn.com/images/nakta-y/post/8d58a479-aee3-4f95-a123-9738060c8d2a/image.png" alt=""></p>
<br/>

<h3 id="문제-풀이">문제 풀이</h3>
<p>미로 문제라고 하면 반사적으로 <code>BFS</code> 방식이 떠오른다.</p>
<p>하지만 해당 문제는 <strong>최단 경로</strong>를 찾는 것이 아닌 목적지 까지 도달하는데 필요한 <strong>최소 비용</strong>을 찾는 문제로 기존의 <code>BFS</code> 방식으로는 풀리지 않는다.</p>
<br/>

<h4 id="첫번째-풀이-방식">첫번째 풀이 방식</h4>
<p>처음에는 좌표 (N-1, N-1) 까지 모든 최단 경로를 탐색할 수 있을거라 생각하여 Queue에서 꺼냈을 때 방문 처리를 하는 방식으로 cost 값을 계산했다.</p>
<p>하지만, 이 방식은 입출력 예제 4번에서 바로 실패가 나버린다.
<img src="https://velog.velcdn.com/images/nakta-y/post/7e2c44ae-11b5-48dc-afc1-c6ef0a8fa333/image.png" alt="">
해당 그림에서 board[5][4] 부분을 도달할려고 할 때, 파란색 경로는 9번의 이동인 반면에 빨간색 경로는 11번의 이동을 통해 도달하게 된다. </p>
<p>따라서, 다양한 경로를 탐색하기 위해서는 단순하게 2차원 배열로 방문 처리를 하면 안된다는 것을 알았다.</p>
<br/>

<h4 id="두번째-풀이-방식">두번째 풀이 방식</h4>
<p>방문 처리를 하지 않고 <code>DP</code> 를 활용한 방법으로 board와 같은 크기의 <code>memo</code>의 모든 값을 0으로 초기화 해놓고 <code>BFS</code> 방식으로 다음 좌표값으로 이동할 때 <code>memo</code>에 기록되어 있는 cost 값이 클 때만 이동하는 방식이다.</p>
<p>해당 방식은 마지막 25번 테스트 케이스를 제외하고 모두 정답으로 처리된다. </p>
<p>처음에는 순조롭게 테스트 케이스에서 파란불이 들어오길래 다 푼줄 알고 뿌듯하게 바라보고 있었는데 마지막 케이스에서 빨간불이 들어오면서 많이 당황했다.</p>
<p>질문 게시판과 구글링을 해본 결과 다음과 같은 반례 케이스에서는 해당 방식으로 풀리지 않는다는 것을 알았다.</p>
<p><img src="https://velog.velcdn.com/images/nakta-y/post/2363bca2-ac4d-459e-bace-d4e65998c94a/image.png" alt=""></p>
<p>마우스로 그려서 퀄리티가 많이 떨어진다..🥹</p>
<p>해당 방식에 따르면 1번 위치에서 파란색 경로가 빨간색 경로보다 cost 값이 더 낮기 때문에 빨간색 경로는 1번 위치로 들어오지 못하게 된다.</p>
<p>하지만, 결과적으로 목표 위치인 3번 위치를 비교해 봤을 때, 빨간색 경로가 파란색 경로보다 cost 값이 더 낮아지게 된다.</p>
<br/>

<h4 id="세번째-풀이-방식">세번째 풀이 방식</h4>
<p>결론부터 말하면, 첫번째 방식을 보완하고 두번째 방식을 섞어서 해결하게 되었다.</p>
<p>먼저 방문 처리를 하는데 있어서 해당 경로의 방향 정보도 같이 포함해서 3차원 배열로 방문 처리를 했다.</p>
<p>추가로, 두번째 방식인 <code>DP</code> 방식을 그대로 사용하면서 해당 좌표값에서는 최솟값이 유지되도록 하였다.</p>
<br/>

<h4 id="풀이-코드">풀이 코드</h4>
<pre><code class="language-java">import java.util.LinkedList;
import java.util.Queue;

class Cell {
    int x;
    int y;
    int cost;
    int[] direction;

    public Cell(int x, int y, int cost, int[] direction) {
        this.x = x;
        this.y = y;
        this.cost = cost;
        this.direction = direction;
    }
}

class Solution {
    public int solution(int[][] board) {
        return bfs(board);
    }

    int bfs(int[][] board) {
        Queue&lt;Cell&gt; queue = new LinkedList&lt;&gt;();
        int[][] memo = new int[board.length][board.length];
        boolean[][][] visited = new boolean[board.length][board.length][4];

        int result = Integer.MAX_VALUE;

        int[] dx = {0, 0, 1, -1};
        int[] dy = {1, -1, 0, -0};

        Cell start = new Cell(0, 0, 0, new int[]{0, 0});
        queue.add(start);

        while (!queue.isEmpty()) {
            Cell cell = queue.poll();

            if (cell.x == board.length - 1 &amp;&amp; cell.y == board.length - 1) {
                result = Math.min(result, cell.cost);
                continue;
            }

            for (int index = 0; index &lt; 4; index++) {
                int x = cell.x + dx[index];
                int y = cell.y + dy[index];

                if (x &lt; 0 || y &lt; 0 || x &gt;= board.length || y &gt;= board.length || board[x][y] == 1) {
                    continue;
                }

                int cost = cell.cost + 100;
                if (cell.direction[0] != dx[index] &amp;&amp; cell.direction[1] != dy[index]) {
                    cost += 500;
                }

                if (!visited[x][y][index] || memo[x][y] &gt;= cost) {
                    visited[x][y][index] = true;
                    memo[x][y] = cost;
                    queue.add(new Cell(x, y, memo[x][y], new int[]{dx[index], dy[index]}));
                }
            }
        }

        return result;
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[ITEM 05. 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라]]></title>
            <link>https://velog.io/@nakta-y/ITEM-05.-%EC%9E%90%EC%9B%90%EC%9D%84-%EC%A7%81%EC%A0%91-%EB%AA%85%EC%8B%9C%ED%95%98%EC%A7%80-%EB%A7%90%EA%B3%A0-%EC%9D%98%EC%A1%B4-%EA%B0%9D%EC%B2%B4-%EC%A3%BC%EC%9E%85%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%98%EB%9D%BC</link>
            <guid>https://velog.io/@nakta-y/ITEM-05.-%EC%9E%90%EC%9B%90%EC%9D%84-%EC%A7%81%EC%A0%91-%EB%AA%85%EC%8B%9C%ED%95%98%EC%A7%80-%EB%A7%90%EA%B3%A0-%EC%9D%98%EC%A1%B4-%EA%B0%9D%EC%B2%B4-%EC%A3%BC%EC%9E%85%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%98%EB%9D%BC</guid>
            <pubDate>Fri, 17 Nov 2023 05:55:24 GMT</pubDate>
            <description><![CDATA[<h1 id="🌱-들어가면서">🌱 들어가면서</h1>
<p>프로그래밍을 하다보면, 하나의 객체가 다른 객체를 포함하는 경우가 많이 있다. </p>
<pre><code class="language-java">public class Car {
    private final Engine engine = new Engine();
}</code></pre>
<p>예를 들어, <code>Car</code> 클래스 내부에 <code>Engine</code> 클래스를 필드로 가지고 있는 경우를 생각해보자.</p>
<p><code>Car</code> 클래스 내부 메서드는 <code>Engine</code> 클래스의 속성을 가져와서 쓰고 있다면 <strong><em>Car 클래스는 Engine 클래스에 의존한다.</em></strong> 라고 말할 수 있다.</p>
<p>객체간의 <code>의존</code> 관련 용어에 대해 알고싶다면 [결합도와 응집도] (<a href="https://inpa.tistory.com/entry/OOP-%F0%9F%92%A0-%EA%B0%9D%EC%B2%B4%EC%9D%98-%EA%B2%B0%ED%95%A9%EB%8F%84-%EC%9D%91%EC%A7%91%EB%8F%84-%EC%9D%98%EB%AF%B8%EC%99%80-%EB%8B%A8%EA%B3%84-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-%EC%89%BD%EA%B2%8C-%EC%A0%95%EB%A6%AC">https://inpa.tistory.com/entry/OOP-%F0%9F%92%A0-%EA%B0%9D%EC%B2%B4%EC%9D%98-%EA%B2%B0%ED%95%A9%EB%8F%84-%EC%9D%91%EC%A7%91%EB%8F%84-%EC%9D%98%EB%AF%B8%EC%99%80-%EB%8B%A8%EA%B3%84-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-%EC%89%BD%EA%B2%8C-%EC%A0%95%EB%A6%AC</a>) 관련 내용을 참고해보면 좋을 것 같다. </p>
<p>이번 내용은 이러한 의존 객체를 초기화 하는 작업인 
<code>의존 객체 주입 (Dependency Injection, DI)</code> 에 대해서 다뤄볼 예정이다.</p>
<br/>

<blockquote>
<p>💡 <strong>*의존 객체 주입 (Dependency Injection, DI) 이란?</strong>
객체 지향 프로그래밍에서 한 객체가 다른 객체에 의존성을 직접 생성하는 것이 아니라, 외부에서 의존 객체를 주입받아 사용하는 디자인 패턴이다.</p>
</blockquote>
<p><br/><br/></p>
<h1 id="🙃-의존-객체-주입-잘못된-예시">🙃 의존 객체 주입 잘못된 예시</h1>
<h3 id="유틸리티-클래스-예제-코드">유틸리티 클래스 예제 코드</h3>
<pre><code class="language-java">public class SpellChecker
{
    private static final Lexicon dictionary = new Dictionary();

      private SpellChecker() {}

      public static boolean isValid(String word) {...}
      public static List&lt;String&gt; suggestions(String type) {...}
}</code></pre>
<br/>

<h3 id="싱글톤-singleton-패턴-예제-코드">싱글톤 (Singleton) 패턴 예제 코드</h3>
<pre><code class="language-java">public class SpellChecker {

      private static final Lexion dictionary = new Dictionary();

    private SpellChecker() {}
    public static SpellChecker INSTANCE = new SpellChecker();

    public boolean isValid() {...}
    public List&lt;String&gt; suggestions(String typo) {...}

}</code></pre>
<p>다음과 같이 맞춤법 검사 기능을 지원해주는 <code>SpellChecker</code> 클래스를 <strong>유틸리티 클래스</strong> 와 <strong>싱글톤 (Singleton) 패턴</strong> 으로 예시를 들어보자.</p>
<p>해당 클래스들은 맞춤법이 맞는지 확인할 사전 <code>Lexicon</code> 이 필요하기에 내부에 의존 객체로써 포함하려고 한다.</p>
<p>따라서, <code>private 생성자</code> 를 사용하므로 의존 객체 초기화를 미리 해둔 상태이다.</p>
<p>겉보기에는 아무런 문제가 없어보이는 코드지만,
실제로 맞춤법 검사를 수행하는데 있어서는 어떨까? 
각 나라별 사전이나 특수 어휘용 사전 등 다양한 사전을 활용하여 다양한 경우에서 맞춤법 검사를 해야하지 않을까?</p>
<br/>

<p>물론, 해당 예제 코드 클래스가 <strong><em>단 하나의 경우</em></strong> 에서만 사용할 것이라면,
해당 코드는 아무런 문제가 없다.</p>
<p>하지만, <strong>확장성</strong>을 고려해야 하는 <strong><em>객체지향적 코드</em></strong>로 봤을 때,
해당 코드는 결코 잘 짜여진 코드라고 볼 수 없다.</p>
<br/>

<p>그렇다면, <code>Lexicon</code> 필드의 <code>final</code> 키워드를 제거하고 <code>static method</code> 를 이용해 외부에서 <code>Lexicon</code> 을 바꿀 수 있게 변경하면 어떨까?</p>
<pre><code class="language-java">public class SpellChecker
{
    private static Lexicon dictionary = ...;

      private SpellChecker()
      {
      }

    public static void changeDictionary(Lexicon dictionary) {
        this.dictionary = dictionary;
    }

      public static boolean isValid(String word)
      {
          ...
      {

      public static List&lt;String&gt; suggestions(String type)
      {
          ...
      }
}</code></pre>
<p>싱글톤 (Singleton) 방식과 비슷하니 유틸리티 클래스 예제 코드만 수정했다.</p>
<p>이렇게 코드를 수정하면 외부에서 <code>changeDictionary()</code> 메서드를 호출하여 사전을 필요에 따라 바꿀 수 있으므로 다양한 사전을 넣어줄 수 있다.</p>
<p>하지만, 이렇게 되면 유틸리티 클래스는 결국 <code>상태</code> 를 가지게 된다. 
즉, 다음과 같이 외부에서 <code>dictionary</code> 를 변경할 수 있게 되버리면 해당 유틸리티 클래스의 기능을 이용하는 모든 클래스는 변경된 상태가 공유된다는 말이 된다.</p>
<p>따라서, 멀티 스레드 환경에서는 <strong>동시성 문제</strong>를 야기할 수 있어 동시성 처리를 따로 해줘야 한다는 단점이 있다.</p>
<br/>

<p>다음과 같은 이유 때문에 <code>상태</code> 를 가지면 안되는 클래스에는 <code>의존 객체</code> 사용하면 안된다는 사실을 알 수 있었다.</p>
<br/>

<blockquote>
<p>📑 <strong><em>정리</em></strong></p>
<ul>
<li><strong><em>상태를 가지면 안되는 클래스</em></strong> 에는 의존 객체를 사용하면 <strong><em>확장성</em></strong> 에 좋지 않다.</li>
<li>의존 객체를 외부에서 변경할 수 있게 바꾸면 <strong><em>상태</em></strong> 를 가지게 되어 동시성 문제를 야기할 수 있다.</li>
</ul>
</blockquote>
<p><br/><br/></p>
<h1 id="🙃-의존-객체-주입-올바른-예시">🙃 의존 객체 주입 올바른 예시</h1>
<pre><code class="language-java">public class SpellChecker {

      private final Lexicon dictionary = ...;

    public SpellChecker(Lexicon dictionary) {
        this.dictionary = dictionary;
    }

    public boolean isValid() {...}
    public List&lt;String&gt; suggestions(String typo) {...}
}</code></pre>
<p>다음과 같이 객체를 생성할 때마다 내부 의존 객체를 주입하는 방식은 여러가지 이점이 있다. </p>
<br/>

<p><strong><em>첫 번째로</em></strong>, 확장성이 높아진다.</p>
<pre><code class="language-java">public interface Lexicon {
    void printText();
}</code></pre>
<pre><code class="language-java">public class EnglishDictionary implements Lexicon{
    @Override
    public void printText() {
        System.out.println(&quot;ENGLISH&quot;);
    }
}</code></pre>
<pre><code class="language-java">public class KoreanDictionary implements Lexicon{
    @Override
    public void printText() {
        System.out.println(&quot;KOREAN&quot;);
    }
}</code></pre>
<pre><code class="language-java">public class Main {
    public static void main(String[] args) {
        SpellChecker koreanSpellChecker = new SpellChecker(new KoreanDictionary());
        SpellChecker englishSpellChecker = new SpellChecker(new EnglishDictionary());
    }
}</code></pre>
<p>다음과 같이 <code>Lexicon</code> 인터페이스에서 하위 타입을 구현해 놓으면 여러 하위 타입을 넣고 객체를 만들어 사용이 가능하다.</p>
<br/>

<p><strong><em>두 번째로</em></strong>, 테스트에 용이하다.</p>
<pre><code class="language-java">import org.junit.Test;
import static org.mockito.Mockito.*;

public class SpellCheckerTest {

    @Test
    public void testSpellCheckerWithEnglishDictionary() {
        // given
        Lexicon englishDictionary = mock(EnglishDictionary.class);
        SpellChecker spellChecker = new SpellChecker(englishDictionary);

        // when
        spellChecker.performSpellCheck();

        // then
        verify(englishDictionary).printText();
    }

    @Test
    public void testSpellCheckerWithKoreanDictionary() {
        // given
        Lexicon koreanDictionary = mock(KoreanDictionary.class);
        SpellChecker spellChecker = new SpellChecker(koreanDictionary);

        // when
        spellChecker.performSpellCheck();

        // then
        verify(koreanDictionary).printText();
    }
}</code></pre>
<p>다음과 같이 의존하는 객체를 외부에서 생성하여 직접 넣어줄 수 있기 때문에 각각의 하위 타입에 대한 테스트 코드를 짜기가 수월해진다.</p>
<br/>

<blockquote>
<p>📑 <strong><em>정리</em></strong></p>
<ul>
<li>의존 객체를 생성자로 넘겨주게 되면 <strong>확장성</strong> 이 높아진다.</li>
<li>외부에서 객체를 제공하기 때문에 <strong>테스트</strong> 가 용이해진다.</li>
</ul>
</blockquote>
<br/>
<br/>

<h3 id="자원-팩토리-방식">자원 팩토리 방식</h3>
<p>패턴을 응용해서 <strong>자원 팩토리</strong> 를 생성자에 넘겨주는 방식이 있다.</p>
<p>아래는 <strong>자바8</strong> 에서부터 사용가능한 <code>Supplier&lt;T&gt;</code> 인터페이스가 팩토리를 표현한 예이다.</p>
<pre><code class="language-java">import java.util.function.Supplier;

public class SpellChecker {
    private final Lexicon dictionary;

    public SpellChecker(Supplier&lt;? extends Lexicon&gt; dictionaryFactory) {
        this.dictionary = dictionaryFactory.get();
    }

    public void print() {
        dictionary.printText();
    }
}</code></pre>
<pre><code class="language-java">import java.util.function.Supplier;

public class Main {
    public static void main(String[] args) {
        Type type = Type.ENGLISH;

        Supplier&lt;Lexicon&gt; dictionaryFactory = () -&gt; {
            Lexicon dictionary = null;

            switch (type) {
                case KOREAN: dictionary = new KoreanDictionary(); break;
                case ENGLISH: dictionary = new EnglishDictionary(); break;
            }
            return dictionary;
        };

        SpellChecker spellChecker = new SpellChecker(dictionaryFactory);
        spellChecker.print();
    }
}</code></pre>
<p>참고로, <code>Supplier&lt;T&gt;</code> 인터페이스는 [함수형 인터페이스] (<a href="https://yozm.wishket.com/magazine/detail/2023/">https://yozm.wishket.com/magazine/detail/2023/</a>) 이므로, <code>람다식</code>으로 표현이 가능하다.</p>
<br/>

<pre><code class="language-java">public SpellChecker(Supplier&lt;? extends Lexicon&gt; dictionaryFactory) {
        this.dictionary = dictionaryFactory.get();
    }</code></pre>
<p>주로, <strong>팩토리 방식</strong> 은 <code>한정적 와일드 카드 타입</code>을 사용하여 팩터리의 타입 매개 변수를 제한한다. 타입을 제한하면 사용자는 명시된 타입의 하위 타입이면 무엇이든 팩토리를 만들어 넘길 수 있게 된다.</p>
<br/>

<p>물론 해당 방식은 좋지만, 
의존성이 매우 높은 실제 프로젝트 에서는 각각 팩토리로 만들거나 생성자 매개변수로 넘겨주면 프로그래머 입장에서는 큰 부담이 된다.</p>
<p>이러한 부담을 줄여주기 위해서 실전에서는 <strong>의존 객체 프레임워크</strong> 를 활용하여 해결한다.</p>
<p>예를 들어, <strong>스프링 프레임워크</strong> 에서는 <code>@Autowired</code> 와 같은 <code>Annotation</code> 을 활용하여 의존성을 주입해주는 방식을 지원해준다.</p>
<blockquote>
<p>💡 <strong><em>의존 객체 프레임워크란?</em></strong>
의존성 주입을 지원하는 소프트웨어 프레임워크로, 이러한 프레임워크는 객체 간의 의존성을 효과적으로 관리하고 주입할 수 있는 방법을 제공하여 코드의 유연성과 유지보수성을 향상시켜준다.</p>
</blockquote>
<p><br/><br/></p>
<blockquote>
<ul>
<li>🔎 <strong><em>참조</em></strong></li>
<li>[자원을 명시하지 말고 의존 객체 주입을 사용하라] (<a href="https://steady-coding.tistory.com/622">https://steady-coding.tistory.com/622</a>)</li>
<li>[아이템 5. 자원을 명시하지 말고 의존 객체 주입을 사용하라] (<a href="https://steady-coding.tistory.com/622">https://steady-coding.tistory.com/622</a>)</li>
</ul>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[ITEM 04. 인스턴스화를 막으려거든 private 생성자를 사용하라]]></title>
            <link>https://velog.io/@nakta-y/ITEM-04.-%EC%9D%B8%EC%8A%A4%ED%84%B4%EC%8A%A4%ED%99%94%EB%A5%BC-%EB%A7%89%EC%9C%BC%EB%A0%A4%EA%B1%B0%EB%93%A0-private-%EC%83%9D%EC%84%B1%EC%9E%90%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%9D%BC</link>
            <guid>https://velog.io/@nakta-y/ITEM-04.-%EC%9D%B8%EC%8A%A4%ED%84%B4%EC%8A%A4%ED%99%94%EB%A5%BC-%EB%A7%89%EC%9C%BC%EB%A0%A4%EA%B1%B0%EB%93%A0-private-%EC%83%9D%EC%84%B1%EC%9E%90%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%9D%BC</guid>
            <pubDate>Thu, 16 Nov 2023 05:45:39 GMT</pubDate>
            <description><![CDATA[<h1 id="🌱-들어가면서">🌱 들어가면서</h1>
<p>이번 주제는 이전 ITEM 에서도 자주 거론됐던 <code>private 생성자</code> 이다. 객체의 수를 제한 두는 것이 왜 의미가 있는지는 [ITEM 01] (<a href="https://velog.io/@nakta-y/ITEM-01.-%EC%83%9D%EC%84%B1%EC%9E%90-%EB%8C%80%EC%8B%A0-%EC%A0%95%EC%A0%81-%ED%8C%A9%ED%86%A0%EB%A6%AC-%EB%A9%94%EC%84%9C%EB%93%9C%EB%A5%BC-%EA%B3%A0%EB%A0%A4%ED%95%98%EB%9D%BC">https://velog.io/@nakta-y/ITEM-01.-%EC%83%9D%EC%84%B1%EC%9E%90-%EB%8C%80%EC%8B%A0-%EC%A0%95%EC%A0%81-%ED%8C%A9%ED%86%A0%EB%A6%AC-%EB%A9%94%EC%84%9C%EB%93%9C%EB%A5%BC-%EA%B3%A0%EB%A0%A4%ED%95%98%EB%9D%BC</a>) 에서 설명했으니 한번 읽어보면 좋을 것 같다. 이번 글을 <code>private 생성자</code> 가 어떤 상황에서 필요한지를 중점으로 다룰 예정이다.</p>
<p><br/><br/></p>
<h1 id="🙃-private-생성자">🙃 private 생성자</h1>
<pre><code class="language-java">public class Something {
    private Something(){}
}</code></pre>
<p>다음과 같이 생성자 <code>접근 제한자</code>를 <code>private</code> 로 설정하면 어떻게 될까?</p>
<p>더이상 해당 클래스의 <code>객체</code>  생성자는 접근 제한을 오로지 클래스 내부에 제한시켜 외부에서는 해당 생성자를 호출할 수 없다.</p>
<br/>

<blockquote>
<p>💡 <strong>접근 제한자란?</strong>
Java 에서 클래스, 필드, 생성자, 메소드를 접근할 수 있는 권한을 제한하는 키워드</p>
<ul>
<li>참조하기 좋은 내용 : [접근제어자, 접근제한자] (<a href="https://hajoung56.tistory.com/33">https://hajoung56.tistory.com/33</a>)</li>
</ul>
</blockquote>
<p><br/><br/></p>
<h1 id="🙃-private-생성자를-쓰는-경우">🙃 private 생성자를 쓰는 경우</h1>
<p>앞서 <code>private 생성자</code> 가 어떤 역할을 하는지 알았다.</p>
<p>그렇다면, <code>private 생성자</code> 를 이용하여<code>객체</code> 의 생성을 컨트롤하는 경우는 어떤게 있을까?</p>
<h2 id="1-싱글톤-singleton-패턴">1. 싱글톤 (Singleton) 패턴</h2>
<pre><code class="language-java">public class Singleton {
    private static Singleton INSTANCE = new Singleton();

    private Singleton(){}
}</code></pre>
<p><code>싱글톤 (Singleton) 패턴</code>은 해당 클래스의 <code>객체</code> 의 수를 <strong><em>1개</em></strong> 로 제한을 두기 때문에 생성자를 이용한<code>객체</code> 생성을 막을 수 밖에 없다.</p>
<p>[ITEM 03] (<a href="https://velog.io/@nakta-y/Item-03.-private-%EC%83%9D%EC%84%B1%EC%9E%90%EB%82%98-%EC%97%B4%EA%B1%B0-%ED%83%80%EC%9E%85%EC%9C%BC%EB%A1%9C-%EC%8B%B1%EA%B8%80%ED%84%B4%EC%9E%84%EC%9D%84-%EB%B3%B4%EC%A6%9D%ED%95%98%EB%9D%BC">https://velog.io/@nakta-y/Item-03.-private-%EC%83%9D%EC%84%B1%EC%9E%90%EB%82%98-%EC%97%B4%EA%B1%B0-%ED%83%80%EC%9E%85%EC%9C%BC%EB%A1%9C-%EC%8B%B1%EA%B8%80%ED%84%B4%EC%9E%84%EC%9D%84-%EB%B3%B4%EC%A6%9D%ED%95%98%EB%9D%BC</a>) 에서 다뤘던 내용이니 한번 읽어보면 좋을 것 같다.</p>
<p><br/><br/></p>
<h2 id="2-유틸리티-클래스-utility-class">2. 유틸리티 클래스 (Utility Class)</h2>
<p>일부 클래스는 문자열 관련, 랜덤값 생성, 날짜 및 시간 처리 등 전역에서 사용되는 특정 로직이나 독립적인 기능을 구현해둔 클래스로 설계될 수 있다.</p>
<p>다음은 java에서 기본적으로 제공하는 <code>java.util.Collections</code> 코드의 일부이다.</p>
<pre><code class="language-java">public class Collections {

    private Collections() {
    }

    private static final int BINARYSEARCH_THRESHOLD   = 5000;
    private static final int REVERSE_THRESHOLD        =   18;
    private static final int SHUFFLE_THRESHOLD        =    5;
    private static final int FILL_THRESHOLD           =   25;
    private static final int ROTATE_THRESHOLD         =  100;
    private static final int COPY_THRESHOLD           =   10;
    private static final int REPLACEALL_THRESHOLD     =   11;
    private static final int INDEXOFSUBLIST_THRESHOLD =   35;

    @SuppressWarnings(&quot;unchecked&quot;)
    public static &lt;T extends Comparable&lt;? super T&gt;&gt; void sort(List&lt;T&gt; list) {
        list.sort(null);
    }

    @SuppressWarnings({&quot;unchecked&quot;, &quot;rawtypes&quot;})
    public static &lt;T&gt; void sort(List&lt;T&gt; list, Comparator&lt;? super T&gt; c) {
        list.sort(c);
    }

    ...

}</code></pre>
<p><code>java.util.Collections</code> 클래스는 자바에서 컬렉션 프레임워크를 위한 유틸리티 메서드를 제공하는 유틸리티 클래스이다.</p>
<p>이 클래스는 모든 메서드 및 상수가 <code>static</code> 으로 선언되어 있기 때문에 <code>객체</code> 를 만들 필요가 없다. </p>
<p><br/><br/></p>
<h2 id="3-상수-클래스-constant-class">3. 상수 클래스 (Constant Class)</h2>
<pre><code class="language-java">public final class Constants {
    // private 생성자로 외부에서의 인스턴스화 방지
    private Constants() {
    }

    public static final int MAX_SIZE = 100;
    public static final String DEFAULT_NAME = &quot;John Doe&quot;;
}</code></pre>
<p>상수 클래스란 주로 상수 값을 정의하고 관리하기 위한 클래스로,</p>
<p>메서드를 가지지 않고 모든 멤버가 상수이기에 위의 2번과 같이 <code>static</code> 으로 모두 선언되어 있어 <code>객체</code> 를 만들 필요가 없다.</p>
<p><br/><br/></p>
<h2 id="4-정적-팩토리-메서드-static-factory-method">4. 정적 팩토리 메서드 (static factory method)</h2>
<pre><code class="language-java">public class Laptop {
    private String brand;
    private String model;
    private int price;

    private Laptop(){}

    public static Laptop withBrand(String brand) {
        Laptop laptop = new Laptop();
        laptop.brand = brand;
        return laptop;
    }

    public static Laptop withModel(String model) {
        Laptop laptop = new Laptop();
        laptop.model = model;
        return laptop;
    }
}</code></pre>
<p>생성자를 통한 <code>객체</code> 생성을 막고 <code>정적 팩토리 메서드</code> 를 이용해서 <code>객체</code> 생성을 하는 방법이다.</p>
<p>해당 방법을 쓰는 이유에 대해서는 [ITEM 01] (<a href="https://velog.io/@nakta-y/ITEM-01.-%EC%83%9D%EC%84%B1%EC%9E%90-%EB%8C%80%EC%8B%A0-%EC%A0%95%EC%A0%81-%ED%8C%A9%ED%86%A0%EB%A6%AC-%EB%A9%94%EC%84%9C%EB%93%9C%EB%A5%BC-%EA%B3%A0%EB%A0%A4%ED%95%98%EB%9D%BC">https://velog.io/@nakta-y/ITEM-01.-%EC%83%9D%EC%84%B1%EC%9E%90-%EB%8C%80%EC%8B%A0-%EC%A0%95%EC%A0%81-%ED%8C%A9%ED%86%A0%EB%A6%AC-%EB%A9%94%EC%84%9C%EB%93%9C%EB%A5%BC-%EA%B3%A0%EB%A0%A4%ED%95%98%EB%9D%BC</a>) 에서 정리를 해둔 글이 있으니 한번 읽어보면 좋을 것 같다.</p>
<p><br/><br/></p>
<h2 id="5-빌더-builder-패턴">5. 빌더 (Builder) 패턴</h2>
<pre><code class="language-java">public class Car {
    private final String brand;           // 필수
    private final String model;           // 필수
    private final String color;           // 필수
    private final String country;         // 선택
    private final int year;               // 선택
    private final int weight;             // 선택

    private Car (Builder builder) {
        brand   =   builder.brand;
        model   =   builder.model;
        color   =   builder.color;
        country =   builder.country;
        year    =   builder.year;
        weight  =   builder.weight;
    }

    public static class Builder {
        private final String brand;           // 필수
        private final String model;              // 필수
        private final String color;           // 필수

        // 선택 매개변수 기본값 초기화
        private String country = &quot;&quot;;          // 선택
        private int year = 0;                   // 선택
        private int weight = 0;               // 선택

        public Builder (String brand, String model, String color) {
            this.brand = brand;
            this.model = model;
            this.color = color;
        }

        public Builder country(String country) {
            this.country = country;
            return this;
        }

        public Builder year(int year) {
            this.year = year;
            return this;
        }

        public Builder weight(int weight) {
            this.weight = weight;
            return this;
        }

        public Car build() {
            return new Car(this);
        }
    }
}</code></pre>
<p><code>빌더 (Builder) 패턴</code> 은 매개변수가 많을 때 생성자를 통한 <code>객체</code> 생성이 아닌 내부 <code>Builder</code> 클래스를 따로 만들어 객체 생성 역할을 위임하는 방법이다.</p>
<p>해당 패턴을 쓰는 이유에 대해서는 [ITEM 02] (<a href="https://velog.io/@nakta-y/ITEM-02.-%EC%83%9D%EC%84%B1%EC%9E%90%EC%97%90-%EB%A7%A4%EA%B0%9C%EB%B3%80%EC%88%98%EA%B0%80-%EB%A7%8E%EB%8B%A4%EB%A9%B4-%EB%B9%8C%EB%8D%94%EB%A5%BC-%EA%B3%A0%EB%A0%A4%ED%95%98%EB%9D%BC">https://velog.io/@nakta-y/ITEM-02.-%EC%83%9D%EC%84%B1%EC%9E%90%EC%97%90-%EB%A7%A4%EA%B0%9C%EB%B3%80%EC%88%98%EA%B0%80-%EB%A7%8E%EB%8B%A4%EB%A9%B4-%EB%B9%8C%EB%8D%94%EB%A5%BC-%EA%B3%A0%EB%A0%A4%ED%95%98%EB%9D%BC</a>) 에서 자세하게 정리를 해뒀으니 한번 읽어보면 좋을 것 같다. </p>
<br/>

<blockquote>
<p>📑 <strong>정리</strong></p>
<ul>
<li>private 생성자는 외부에서 생성자를 호출할 수 없다.</li>
<li>private 생성자는 다양한 경우에 사용하지만, 주된 목적은 객체 생성을 컨트롤하기 위해서 필요하다.</li>
</ul>
</blockquote>
<p><br/><br/></p>
<blockquote>
<p>🔎 <strong><em>참조</em></strong></p>
<ul>
<li>[Util 클래스/패키지] (<a href="https://velog.io/@ozragwort/Util-%ED%81%B4%EB%9E%98%EC%8A%A4%ED%8C%A8%ED%82%A4%EC%A7%80">https://velog.io/@ozragwort/Util-%ED%81%B4%EB%9E%98%EC%8A%A4%ED%8C%A8%ED%82%A4%EC%A7%80</a>)</li>
<li>[private 생성자를 사용하는 이유] (<a href="https://codechacha.com/ko/java-private-constructor/">https://codechacha.com/ko/java-private-constructor/</a>)</li>
<li>[인스턴스화를 막으려거든 private 생성자를 사용하라] (<a href="https://velog.io/@holidenty/%EC%9D%B4%ED%8E%99%ED%8B%B0%EB%B8%8C-%EC%9E%90%EB%B0%94-%EA%B0%9D%EC%B2%B4%EC%9D%98-%EC%83%9D%EC%84%B1%EA%B3%BC-%ED%8C%8C%EA%B4%B4-Item4-%EC%9D%B8%EC%8A%A4%ED%84%B4%EC%8A%A4%ED%99%94%EB%A5%BC-%EB%A7%89%EC%9C%BC%EB%A0%A4%EA%B1%B0%EB%93%A0-private-%EC%83%9D%EC%84%B1%EC%9E%90%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%9D%BC">https://velog.io/@holidenty/%EC%9D%B4%ED%8E%99%ED%8B%B0%EB%B8%8C-%EC%9E%90%EB%B0%94-%EA%B0%9D%EC%B2%B4%EC%9D%98-%EC%83%9D%EC%84%B1%EA%B3%BC-%ED%8C%8C%EA%B4%B4-Item4-%EC%9D%B8%EC%8A%A4%ED%84%B4%EC%8A%A4%ED%99%94%EB%A5%BC-%EB%A7%89%EC%9C%BC%EB%A0%A4%EA%B1%B0%EB%93%A0-private-%EC%83%9D%EC%84%B1%EC%9E%90%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%9D%BC</a>)</li>
</ul>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[ITEM 03. private 생성자나 열거 타입으로 싱글턴임을 보증하라]]></title>
            <link>https://velog.io/@nakta-y/Item-03.-private-%EC%83%9D%EC%84%B1%EC%9E%90%EB%82%98-%EC%97%B4%EA%B1%B0-%ED%83%80%EC%9E%85%EC%9C%BC%EB%A1%9C-%EC%8B%B1%EA%B8%80%ED%84%B4%EC%9E%84%EC%9D%84-%EB%B3%B4%EC%A6%9D%ED%95%98%EB%9D%BC</link>
            <guid>https://velog.io/@nakta-y/Item-03.-private-%EC%83%9D%EC%84%B1%EC%9E%90%EB%82%98-%EC%97%B4%EA%B1%B0-%ED%83%80%EC%9E%85%EC%9C%BC%EB%A1%9C-%EC%8B%B1%EA%B8%80%ED%84%B4%EC%9E%84%EC%9D%84-%EB%B3%B4%EC%A6%9D%ED%95%98%EB%9D%BC</guid>
            <pubDate>Thu, 16 Nov 2023 02:14:24 GMT</pubDate>
            <description><![CDATA[<h1 id="🌱-들어가면서">🌱 들어가면서</h1>
<p>이번 주제는 디자인 패턴 종류 중 하나인 싱글톤 (Singlton) 패턴에 대한 것이다. 
익숙하게 봐왔던 패턴이지만 자세하게 공부해 본 적은 없는 것 같다. 이번 글에서는 싱글톤 패턴의 구현 방식, 더불어 주의할 점은 무엇인지, 그리고 열거타입 (Enum)과 어떤 관계가 있는지 알아보고자 한다.</p>
<p></br></br></p>
<h1 id="🙃-싱글톤-singleton-패턴이란">🙃 싱글톤 (Singleton) 패턴이란?</h1>
<p><img src="https://velog.velcdn.com/images/nakta-y/post/a8a90d6a-8fae-457e-9fb7-1bbb436f2af6/image.png" alt=""> <a href="https://www.ionos.com/digitalguide/websites/web-development/singleton-design-pattern/">이미지 출처</a> </p>
<p>싱글톤 패턴을 잘 나타내는 이미지라 생각하여 하나 가져와봤다.</p>
<p><span style="color : blue">싱글톤 패턴(Singleton Pattern)</span>은 <strong><em>디자인 패턴</em></strong> 중 하나로, 
어떤 클래스가 <strong>최대 하나의 인스턴스</strong>만을 갖도록 보장하고, 
이에 대한 <strong>전역적인 접근점</strong>을 제공하는 패턴이다. </p>
<p>간단하게 말해서, 여러 명이서 하나를 공유해서 쓴다는 말이다.</p>
</br>

<h2 id="✅-싱글톤-singleton-패턴을-사용하는-이유">✅ 싱글톤 (Singleton) 패턴을 사용하는 이유</h2>
<p>싱글톤 패턴의 특징은 알았지만 해당 패턴을 왜 사용하는지 의문점이 생길 것이다.
주요 사용 이유는 다음과 같다.</p>
<h3 id="1-메모리-낭비-방지">1. 메모리 낭비 방지</h3>
<p>한개의 인스턴스만을 고정 메모리 영역에 생성하고 추후 해당 객체를 접근할 때 추가적으로 객체를 생성하지 않아 메모리 낭비를 방지할 수 있다.</p>
<h3 id="2-객체-접근-속도">2. 객체 접근 속도</h3>
<p> 이미 생성되어 있는 인스턴스를 호출하기에 속도 측면에서도 빠른 이점이 있다.</p>
<h3 id="3-데이터-공유가-쉬움">3. 데이터 공유가 쉬움</h3>
<p>전역적인 접근점을 제공하기 때문에 다른 클래스 간 데이터 공유가 쉽다.</p>
</br>

<h2 id="⛔️-싱글톤-singleton-패턴의-문제점">⛔️ 싱글톤 (Singleton) 패턴의 문제점</h2>
<p>모든 디자인 패턴이 그렇듯이, 싱글톤 패턴도 문제점을 가지고 있다.</p>
<h3 id="1-의존성이-높아진다">1. 의존성이 높아진다.</h3>
<p>싱글톤 패턴을 사용하는 경우 클래스의 객체를 미리 생성한 뒤에 필요한 경우 정적 메서드를 제공하는데 다른 클래스에서 해당 싱글톤 객체를 이용하기 위해서는 메서드의 존재를 알 필요가 있기 때문에 클래스 사이에 <strong>의존성</strong> 이 높아지게 된다는 문제점이 있다.</p>
<h3 id="2-상속이-어렵다">2. 상속이 어렵다.</h3>
<p>[ITEM 01] (<a href="https://velog.io/@nakta-y/ITEM-01.-%EC%83%9D%EC%84%B1%EC%9E%90-%EB%8C%80%EC%8B%A0-%EC%A0%95%EC%A0%81-%ED%8C%A9%ED%86%A0%EB%A6%AC-%EB%A9%94%EC%84%9C%EB%93%9C%EB%A5%BC-%EA%B3%A0%EB%A0%A4%ED%95%98%EB%9D%BC">https://velog.io/@nakta-y/ITEM-01.-%EC%83%9D%EC%84%B1%EC%9E%90-%EB%8C%80%EC%8B%A0-%EC%A0%95%EC%A0%81-%ED%8C%A9%ED%86%A0%EB%A6%AC-%EB%A9%94%EC%84%9C%EB%93%9C%EB%A5%BC-%EA%B3%A0%EB%A0%A4%ED%95%98%EB%9D%BC</a>) 에서 나와있듯, 싱글톤 패턴은 정적 팩토리 메서드를 활용해야 하기 때문에, 생성자를 <code>private</code> 로 만드는 경우가 많기에 상속을 통해 하위 타입 클래스를 만들 수 없다는 특징이 있다. 이렇게 되면 객체 지향의 <strong>다형성</strong> 을 활용하기 힘들어진다.</p>
<h3 id="3-테스트하기-힘들다">3. 테스트하기 힘들다.</h3>
<p>싱글톤 객체는 모든 클래스에게 자원을 공유하고 있다는 특징이 있다. 
이러한 특징은 <strong><em>서로 독립적이고 어떤 순서로든 실행될 수 있어야 하는 단위 테스트</em></strong> 를 하는데 문제가 될 수 밖에 없다. 싱글톤 패턴은 미리 생성된 인스턴스를 기반으로 구현하므로 각 테스트마다 독립적인 인스턴스를 만들기가 어렵다.</p>
<p>추가로 싱글톤은 인터페이스를 통해 추상화하지 않고 해당 클래스를 직접 참조하므로 <code>Mock</code> 을 만드는데 어려움이 있어 <strong>Mock 테스트</strong> 를 하는데 어려움이 있다.</p>
</br>

<p>이렇듯, 여러가지 장점과 단점이 존재하기 때문에 싱글톤 패턴을 사용하는데는 각별한 주의를 요한다.</p>
<p></br></br></p>
<h1 id="🙃-싱글톤-패턴을-만드는-방법">🙃 싱글톤 패턴을 만드는 방법</h1>
<p>이제부터 싱글턴 만드는 여러가지 방법에 대해서 알아보자.</p>
<h2 id="1-public-static-final-필드-방식">1. public static final 필드 방식</h2>
<p>아래 예시 코드부터 보자.</p>
<pre><code class="language-java">public class Singleton {
    public static final Singleton INSTANCE = new Singleton();

    private Singleton(){}
}</code></pre>
<p>다음 코드는 생성자를 <code>private</code> 접근 제한을 둬 추가 객체 생성을 막고,
<code>Singleton.INSTANCE</code> 를 통해 직접 필드에 접근해 객체를 얻어오는 방식이다.</p>
<p>코드가 간단한 만큼 간단한 방식으로
다음과 같이 작성하면 전체 시스템에서 <strong>해당 객체가 하나뿐</strong>이라는 것을 보장받을 수 있다.</p>
<br/>

<p>하지만, 이 코드는 문제가 있다.</p>
<pre><code class="language-java">import java.lang.reflect.Constructor;

public class ReflectionTest {
    static Singleton singletonFromField;

    public static void main(String[] args) {
        try {
            singletonFromField = Singleton.INSTANCE;

            Constructor ctor = Class.forName(&quot;Singleton&quot;).getDeclaredConstructor();
            ctor.setAccessible(true);

            Singleton singletonFromConstructor = (Singleton) ctor.newInstance();

            System.out.println(singletonFromField);
            System.out.println(singletonFromConstructor);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}</code></pre>
<p>다음과 같이 <code>java Reflection API</code> 를 호출해서 <code>private</code> 접근 제한자로 선언된 생성자에 접근하여 객체를 만들면 추가적으로 객체를 만드는게 가능하기 때문에 싱글톤을 보장하지 못한다는 특징이 있다.</p>
<p>이를 막기 위해서는, 두 번째 인스턴스를 생성할 때 <code>예외처리</code> 를 해주어야 한다.</p>
<blockquote>
<p>💡 <strong>java Reflection API 란?</strong>
구체적인 클래스 타입을 알지 못해도 그 클래스의 정보(메서드, 타입, 변수 등등)에 접근할 수 있게 해주는 자바 API다.</p>
</blockquote>
<br/>

<p>추가적으로, 해당 코드는 <code>역직렬화 (deserialization)</code> 에도 안전하지 못하다.</p>
<pre><code class="language-java">import java.io.*;

public class DeserializationTest {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Singleton singleton1 = Singleton.getInstance();

        String fileName = &quot;singleton.obj&quot;;

        // 직렬화
        ObjectOutputStream out = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(fileName)));
        out.writeObject(singleton1);
        out.close();

        // 역직렬화
        ObjectInputStream in = new ObjectInputStream(new BufferedInputStream(new FileInputStream(fileName)));
        Singleton singleton2 = (Singleton) in.readObject();
        in.close();

        System.out.println(&quot;singleton1 == singleton2 : &quot; + (singleton1 == singleton2));
        System.out.println(singleton1);
        System.out.println(singleton2);
    }
}</code></pre>
<p>해당 코드의 결과는 다음과 같다.</p>
<pre><code class="language-text">singleton1 == singleton2 : false
Singleton@26a1ab54
Singleton@5a2e4553</code></pre>
<p>이러한 이유가 발생하는 이유는 역직렬화 자체가 보이지 않는 생성자의 역할을 수행하기 때문이다. <code>객체</code> 를 또 만들어, 직렬화에 사용된 <code>객체</code> 와 다른 <code>객체</code> 를 만들기 때문에 더 이상 싱글톤을 보장할 수 없게 된다.</p>
<br/>

<p>따라서, 이러한 문제를 해결하기 위해서는 <code>readResolve()</code> 메서드를 정의하면 된다. </p>
<pre><code class="language-java">import java.io.Serializable;

public class Singleton implements Serializable {
    public static final Singleton INSTANCE = new Singleton();

    private Singleton() {
    }

    Object readResolve() {
        return INSTANCE;
    }
}</code></pre>
<p>해당 메서드를 정의하면 역직렬화 과정에서 새로 만들어진 <code>객체</code> 대신 <code>readResolve()</code> 메서드로 반환되는 <code>객체</code> 를 사용할 수 있도록 조정해주기 때문이다. </p>
</br> 

<blockquote>
<p>📑 <strong><em>정리</em></strong></p>
<ul>
<li>코드가 간결하다.</li>
<li>java.Relection API에 취약하므로 추가적인 예외처리가 필요하다.</li>
<li>역직렬화 과정에서 싱글톤이 깨져버리므로 readResolve() 메서드 구현이 필요하다.</li>
</ul>
</blockquote>
<p></br></br> </p>
<h2 id="2-정적-팩토리-메서드-방식">2. 정적 팩토리 메서드 방식</h2>
<p>해당 방식은 2가지 방법이 존재한다.
</br> </p>
<p><strong>방법 1</strong></p>
<pre><code class="language-java">public class Singleton {
    private static final Singleton INSTANCE = new Singleton();

    private Singleton() {
    }

    public static Singleton getInstance() {
        return INSTANCE;
    }
}</code></pre>
</br>

<p><strong>방법 2</strong></p>
<pre><code class="language-java">public class Singleton {
    private static Singleton INSTANCE;

    private Singleton() {
    }

    public static Singleton getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new Singleton();
        }

        return INSTANCE;
    }
}</code></pre>
<p>얼핏 보기에는, 두 코드 모두 같은 로직으로 보이지만 차이점이 확실히 존재한다.</p>
</br>

<p><strong>방법1의 경우에는,</strong>
클래스가 로드될 때 즉시 <code>INSTANCE</code> 필드에 인스턴스를 생성해 두는 방법이다.
사실상 위의  <strong>public static final 필드 방식</strong> 과 다르지 않다.
무엇보다, 이 방법은 <strong><em>쓰레드 안전 (thread-safe)</em></strong> 하다는 장점이 있다.</p>
<p><strong>방법 2의 경우에는,</strong>
처음에 <code>INSTANCE</code> 필드는 <code>null</code> 로 초기화 되어 있는 상태이며, 
사용자가 인스턴스가 필요한 시점에 호출하면 인스턴스를 생성하는 방법이다.
이 방법은 초기화가 지연됨으로써 메모리를 덜 사용할 수 있다는 이점이 있다.</p>
<p>추가적으로, 객체 생성을 <strong>정적 팩토리 메서드</strong> 가 담당하기 때문에 해당 메서드의 내용만 바꾸면 언제든지 싱글톤이 아니게 만들 수 있다는 특징이 있다.</p>
<p>하지만, 멀티스레드 환경에서 여러 스레드가 동시에 <code>getInstance()</code> 메서드를 호출하면 인스턴스가 여러 번 생성될 수 있으므로 동기화 작업을 따로 해줘야한다.</p>
</br> 

<blockquote>
<p>📑 <strong><em>정리</em></strong></p>
<ul>
<li>정적 팩토리 메서드 방법의 경우 언제든지 싱글턴이 아니게 변경할 수 있다.</li>
<li>java.Relection API에 취약하므로 추가적인 예외처리를 해줘야 한다.</li>
<li>역직렬화 과정에서 싱글톤이 깨져버리므로 readResolve() 메서드 구현이 필요하다.</li>
</ul>
</blockquote>
<p></br></br> </p>
<h2 id="3-열거-타입-방식">3. 열거 타입 방식</h2>
<h3 id="열거-타입-enum-이란">열거 타입 (Enum) 이란?</h3>
<blockquote>
<p>한정된 값만을 가지는 데이터 타입을 의미한다.</p>
</blockquote>
<br/>

<p>이 개념을 이해하기 위해서 다음 예시를 보자.</p>
<pre><code class="language-java">public enum Week {
    MONDAY,
    TUESDAY,
    WEDNESDAY,
    THURSDAY,
    FRIDAY,
    SATURDAY,
    SUNDAY
}</code></pre>
<p>위의 열거 타입은 한정된 7개의 값 (MONDAY~SUNDAY)을 가진다.
이러한 한정된 값들을 <code>열거 상수</code> 라고 부른다.</p>
<br/>

<p>기본적인 개념은 알았으니 <strong>메모리 관점</strong> 에서 열거 타입을 보자.</p>
<p>우선 알아둬야 할 것은, 열거타입은 <strong><em>참조 타입</em></strong> 이다.</p>
<pre><code class="language-java">Week day = null;
day = Week.SUNDAY;</code></pre>
<p>다음과 같이 열거 타입 변수를 <code>null</code> 로 선언하고 <code>열거 상수</code> 를 저장할 수 있다. 
참조 타입에 다음과 같이 값을 할당할 수 이유는 <code>열거 상수</code> 는 곧 <code>객체</code> 라는 말이 된다.</p>
<br/>

<p>다음 그림을 한번 보자. 
<img src="https://velog.velcdn.com/images/nakta-y/post/64d0574a-b6ed-4fc9-a8c2-5c22f91dd9a8/image.png" alt=""> [이미지 출처] (<a href="https://honbabzone.com/java/java-enum/">https://honbabzone.com/java/java-enum/</a>)</p>
<p>JAVA에서 <code>열거 상수</code> 는 각각 내부적으로 <code>public static final</code> 필드이면서 객체로 제공되도록 한다. 각각의 <code>열거 상수</code> 는 <code>static</code> 이기 때문에, 클래스가 로드되는 시점에 <code>Method 영역</code> 에 올라가게 된다.</p>
<p><code>Heap 영역</code> 에는 MONDAY~SUNDAY 까지 각각 고유의 객체가 만들어지고 <code>Method 영역</code> 의 <code>열거 상수</code> 들은 해당 객체들을 참조하게 된다.</p>
<br/>

<p>그리고 다음과 같이 변수를 선언했을 때,</p>
<pre><code class="language-java">Week today = Week.MONDAY;</code></pre>
<p><img src="https://velog.velcdn.com/images/nakta-y/post/21084170-8281-4804-971e-0a4e5b4972ba/image.png" alt=""> [이미지 출처] (<a href="https://honbabzone.com/java/java-enum/">https://honbabzone.com/java/java-enum/</a>)</p>
<p><code>today</code> 변수는 <code>Stack 영역</code> 에 선언되고 해당 변수는 <code>Method 영역</code> 에 있는 <code>MONDAY</code> 객체의 주소 값을 복사하므로 결과적으로 <code>Heap 영역</code> 에 생성된 객체를 바라본다는 말이 된다.</p>
<p><br/><br/></p>
<h3 id="싱글톤-singleton-활용">싱글톤 (Singleton) 활용</h3>
<p>열거 타입으로 싱글톤을 선언하는건 매우 간단하다.</p>
<pre><code class="language-java">public enum Singleton {
    INSTANCE;
}</code></pre>
<p>코드가 너무나도 간결하다.</p>
<br/>

<p>우리는 앞서 열거 타입에 정의된 <code>열거 상수</code> 들은 곧 <code>객체</code> 라는 것을 인지하고 있다.
따라서, 다음과 같이 선언되면 <code>객체</code> 를 하나만을 보장할 수 있는 말이 된다.</p>
<p>그리고, 생성자가 없으므로 <code>java Reflection API</code> 와 <code>역직렬화 (deserialization)</code> 에도 안전하다.</p>
</br> 

<blockquote>
<p>📑 <strong><em>정리</em></strong></p>
<ul>
<li>코드가 매우 간결하다.</li>
<li>java Reflection API에 안전하므로 따로 예외처리를 안해줘도 된다.</li>
<li>역직렬화 과정에서도 안전하므로 따로 readResolve() 메서드를 구현하지 않아도 된다.</li>
</ul>
</blockquote>
<p></br></br> </p>
<blockquote>
<p>🔎 <strong><em>참조</em></strong></p>
<ul>
<li>[싱글톤(Singleton) 패턴이란?] (<a href="https://velog.io/@seongwon97/%EC%8B%B1%EA%B8%80%ED%86%A4Singleton-%ED%8C%A8%ED%84%B4%EC%9D%B4%EB%9E%80">https://velog.io/@seongwon97/%EC%8B%B1%EA%B8%80%ED%86%A4Singleton-%ED%8C%A8%ED%84%B4%EC%9D%B4%EB%9E%80</a>)</li>
<li>[싱글톤 패턴의 사용 이유와 문제점] (<a href="https://velog.io/@jhbae0420/%EC%8B%B1%EA%B8%80%ED%86%A4-%ED%8C%A8%ED%84%B4%EC%9D%98-%EC%82%AC%EC%9A%A9-%EC%9D%B4%EC%9C%A0%EC%99%80-%EB%AC%B8%EC%A0%9C%EC%A0%90">https://velog.io/@jhbae0420/%EC%8B%B1%EA%B8%80%ED%86%A4-%ED%8C%A8%ED%84%B4%EC%9D%98-%EC%82%AC%EC%9A%A9-%EC%9D%B4%EC%9C%A0%EC%99%80-%EB%AC%B8%EC%A0%9C%EC%A0%90</a>)</li>
<li>[Reflection API 간단히 알아보자.] (<a href="https://tecoble.techcourse.co.kr/post/2020-07-16-reflection-api/">https://tecoble.techcourse.co.kr/post/2020-07-16-reflection-api/</a>)</li>
<li>[Java Enum이란] (<a href="https://honbabzone.com/java/java-enum/">https://honbabzone.com/java/java-enum/</a>)</li>
<li>[싱글톤 객체가 깨져버리는 경우 (역직렬화)] (<a href="https://inpa.tistory.com/entry/JAVA-%E2%98%95-%EC%8B%B1%EA%B8%80%ED%86%A4-%EA%B0%9D%EC%B2%B4-%EA%B9%A8%EB%9C%A8%EB%A6%AC%EB%8A%94-%EB%B0%A9%EB%B2%95-%EC%97%AD%EC%A7%81%EB%A0%AC%ED%99%94-%EB%A6%AC%ED%94%8C%EB%A0%89%EC%85%98#%EC%97%AD%EC%A7%81%EB%A0%AC%ED%99%94%EB%A1%9C_%EA%B9%A8%EC%A7%80%EB%8A%94_%EC%8B%B1%EA%B8%80%ED%86%A4">https://inpa.tistory.com/entry/JAVA-%E2%98%95-%EC%8B%B1%EA%B8%80%ED%86%A4-%EA%B0%9D%EC%B2%B4-%EA%B9%A8%EB%9C%A8%EB%A6%AC%EB%8A%94-%EB%B0%A9%EB%B2%95-%EC%97%AD%EC%A7%81%EB%A0%AC%ED%99%94-%EB%A6%AC%ED%94%8C%EB%A0%89%EC%85%98#%EC%97%AD%EC%A7%81%EB%A0%AC%ED%99%94%EB%A1%9C_%EA%B9%A8%EC%A7%80%EB%8A%94_%EC%8B%B1%EA%B8%80%ED%86%A4</a>)</li>
<li>[[이펙티브 자바] 객체의 생성과 파괴 Item3 - private 생성자나 열거 타입으로 싱글턴임을 보증하라] (<a href="https://velog.io/@holidenty/%EC%9D%B4%ED%8E%99%ED%8B%B0%EB%B8%8C-%EC%9E%90%EB%B0%94-%EA%B0%9D%EC%B2%B4%EC%9D%98-%EC%83%9D%EC%84%B1%EA%B3%BC-%ED%8C%8C%EA%B4%B4-Item3-private-%EC%83%9D%EC%84%B1%EC%9E%90%EB%82%98-%EC%97%B4%EA%B1%B0-%ED%83%80%EC%9E%85%EC%9C%BC%EB%A1%9C-%EC%8B%B1%EA%B8%80%ED%84%B4%EC%9E%84%EC%9D%84-%EB%B3%B4%EC%A6%9D%ED%95%98%EB%9D%BC">https://velog.io/@holidenty/%EC%9D%B4%ED%8E%99%ED%8B%B0%EB%B8%8C-%EC%9E%90%EB%B0%94-%EA%B0%9D%EC%B2%B4%EC%9D%98-%EC%83%9D%EC%84%B1%EA%B3%BC-%ED%8C%8C%EA%B4%B4-Item3-private-%EC%83%9D%EC%84%B1%EC%9E%90%EB%82%98-%EC%97%B4%EA%B1%B0-%ED%83%80%EC%9E%85%EC%9C%BC%EB%A1%9C-%EC%8B%B1%EA%B8%80%ED%84%B4%EC%9E%84%EC%9D%84-%EB%B3%B4%EC%A6%9D%ED%95%98%EB%9D%BC</a>)</li>
</ul>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[ITEM 02. 생성자에 매개변수가 많다면 빌더를 고려하라]]></title>
            <link>https://velog.io/@nakta-y/ITEM-02.-%EC%83%9D%EC%84%B1%EC%9E%90%EC%97%90-%EB%A7%A4%EA%B0%9C%EB%B3%80%EC%88%98%EA%B0%80-%EB%A7%8E%EB%8B%A4%EB%A9%B4-%EB%B9%8C%EB%8D%94%EB%A5%BC-%EA%B3%A0%EB%A0%A4%ED%95%98%EB%9D%BC</link>
            <guid>https://velog.io/@nakta-y/ITEM-02.-%EC%83%9D%EC%84%B1%EC%9E%90%EC%97%90-%EB%A7%A4%EA%B0%9C%EB%B3%80%EC%88%98%EA%B0%80-%EB%A7%8E%EB%8B%A4%EB%A9%B4-%EB%B9%8C%EB%8D%94%EB%A5%BC-%EA%B3%A0%EB%A0%A4%ED%95%98%EB%9D%BC</guid>
            <pubDate>Wed, 15 Nov 2023 05:22:14 GMT</pubDate>
            <description><![CDATA[<h1 id="🌱-들어가면서">🌱 들어가면서</h1>
<p>프로그램을 만들 때, 규모가 점점 커지면 커질수록 클래스 내부 필드와 메서드는 복잡해지기 마련이다. 그래서 코드를 체계적으로 관리하지 않으면 코드의 가독성과 유지보수성이 저하되고 이는 생산성에 영향을 미칠 수 밖에 없다. 그렇다면 많은 매개변수를 가지는 객체는 어떻게 생성해야 할까? 이번 글에서는 이러한 고민을 다뤄볼려고 한다.</p>
<p></br></br></p>
<h1 id="🙃-필수-매개변수와-선택-매개변수">🙃 필수 매개변수와 선택 매개변수</h1>
<p><code>Car</code> 클래스의 매개변수 정보는 다음과 같다고 가정해보자.</p>
<pre><code class="language-java">public class Car {
    private String brand;           // 필수
    private String model;           // 필수
    private String color;           // 필수
    private String country;         // 선택
    private int year;               // 선택
    private int weight;             // 선택
}</code></pre>
<p><code>Car</code> 객체를 만들 때, <code>brand</code>, <code>model</code>, <code>color</code> 는 반드시 값이 있어야 되며 이 정보들이 제공되지 않으면 <code>Car</code> 객체를 적절하게 생성하거나 사용하는 것이 어려울 수 있다. </p>
<p>그리고 <code>country</code>, <code>year</code>, <code>weight</code> 는 사용자가 원하는 경우에만 제공하면 되므로, <code>Car</code> 객체를 생성할 때는 반드시 설정할 필요가 없다.</p>
<p>일반적으로 클래스를 정의할 때,
객체 생성에 꼭 필요한 매개변수와 굳이 필요하지 않은 매개변수를 다음과 같이 구분한다.</p>
</br> 

<blockquote>
<p>📑 <strong><em>정리</em></strong></p>
<ul>
<li><strong>필수 매개변수</strong> : 클래스나 메서드 등을 생성하거나 호출할 때 반드시 제공되어야 하는 매개변수</li>
<li><strong>선택 매개변수</strong> : 클래스나 메서드 호출 시 제공되어도 되고, 생략되어도 괜찮은 매개변수</li>
</ul>
</blockquote>
<p></br> </br> </p>
<h1 id="🙃-매개변수가-많을-때-생성-패턴">🙃 매개변수가 많을 때 생성 패턴</h1>
<h3 id="1-점층적-생성자-패턴">1. 점층적 생성자 패턴</h3>
<p>쉽게 말해서 <code>필수 매개변수</code>를 포함한 생성자 수를 점층적으로 늘려가는 방식이다.</p>
<p>아래 예시 코드를 보자.</p>
<pre><code class="language-java">public class Car {
    private String brand;           // 필수
    private String model;           // 필수
    private String color;           // 필수
    private String country;         // 선택
    private int year;               // 선택
    private int weight;             // 선택

    public Car(String brand, String model, String color) {
        this(brand, model, color, &quot;&quot;);
    }

    public Car(String brand, String model, String color, String country) {
        this(brand, model, color, &quot;&quot;, 0);
    }

    public Car(String brand, String model, String color, String country, int year) {
        this(brand, model, color, &quot;&quot;, 0, 0);
    }

    public Car(String brand, String model, String color, String country, int year, int weight) {
        this.brand = brand;
        this.model = model;
        this.color = color;
        this.country = country;
        this.year = year;
        this.weight = weight;
    }
}</code></pre>
<p>다음 코드는 한 눈에 보기에도 비효율적인 발상이라는 것을 눈치챌 수 있다.</p>
<p><strong><em>첫 번째로</em></strong>, <code>year</code>, <code>weight</code> 만 값을 할당하고 싶을 때 강제적으로 <code>country</code> 값을 임의로 설정할 수 밖에 없다.</p>
<p><strong><em>두 번째로</em></strong>, <code>year</code>, <code>country</code> 만 값을 할당하거나 <code>weight</code>, <code>country</code> 만 값을 할당하는 경우의 생성자를 생성할 수 없다.</p>
<p>특히, 현재 선택 매개변수의 수가 6개에서 50개로 확 늘어난다면 어떻게 될까? 클래스 내부에서 생성자의 비중이 거의 다 차지하게 될 것이다.</p>
</br> 

<blockquote>
<p>📑 <strong><em>정리</em></strong></p>
<ul>
<li>원하는 매개변수 조합으로 생성자를 생성할 수 없다.</li>
<li>코드가 매우 복잡해진다</li>
<li>프로그래머가 실수할 가능성이 높고 디버깅이 힘들어진다</li>
</ul>
</blockquote>
<p></br> </br> </p>
<h3 id="2-자바빈즈-패턴">2. 자바빈즈 패턴</h3>
<p>자바빈즈 패턴은 간단하게 말해서 <code>Setter</code> 메서드를 이용해 필요한 값만 넣는 방식이다. </p>
<p>아래 예시 코드를 보자.</p>
<pre><code class="language-java">public class Car {
    private String brand;           // 필수
    private String model;           // 필수
    private String color;           // 필수
    private String country;         // 선택
    private int year;               // 선택
    private int weight;             // 선택

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public void setModel(String model) {
        this.model = model;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public void setCountry(String country) {
        this.country = country;
    }

    public void setYear(int year) {
        this.year = year;
    }

    public void setWeight(int weight) {
        this.weight = weight;
    }
}</code></pre>
<p>확실히 점층적 생성자 패턴과 비교 했을 때는 여러가지 조합을 생각하면서 생성자를 만들지 않아도 되고 무엇보다 코드 가독성이 좋아보인다.</p>
<p>하지만, 자바 빈즈 패턴은 큰 단점이 있다.</p>
<p>첫 번째로, <code>불변 객체</code> 를 만들기가 어려워진다. <code>불변 객체</code> 는 한번 객체가 생성되면 상태가 변경되지 않아야 하는데, <code>Setter</code>를 이용하면 객체의 상태를 변경할 수 있기 때문이다.</p>
<p>두 번째로, 객체의 일관성이 무너진다. <code>Setter</code> 를 이용하면 객체의 상태를 중간에 변경할 수 있기 때문에 객체가 일관된 상태를 유지하기 어려워진다. </p>
</br> 

<blockquote>
<p>📑 <strong><em>정리</em></strong></p>
<ul>
<li><strong>불변 객체</strong> 를 만들기가 어렵다.</li>
<li><strong>객체의 일관성</strong> 이 무너진다.</li>
</ul>
</blockquote>
</br> 
</br> 

<h3 id="3-빌더-패턴">3. 빌더 패턴</h3>
<p>빌더 패턴은 객체 생성과정을 <code>캡슐화</code> 하고 <code>유연성</code> 을 높이기 위한 디자인 패턴이다.</p>
<p>거두절미하고 빌더 패턴의 예시 코드를 보자.</p>
<pre><code class="language-java">public class CarWithBuilder {
    private final String brand;           // 필수
    private final String model;           // 필수
    private final String color;           // 필수
    private final String country;         // 선택
    private final int year;               // 선택
    private final int weight;             // 선택

    private CarWithBuilder(Builder builder) {
        brand   =   builder.brand;
        model   =   builder.model;
        color   =   builder.color;
        country =   builder.country;
        year    =   builder.year;
        weight  =   builder.weight;
    }

    public static class Builder {
        private final String brand;       // 필수
        private final String model;       // 필수
        private final String color;       // 필수

        // 선택 매개변수 기본값 초기화
        private String country = &quot;&quot;;      // 선택
        private int year = 0;             // 선택
        private int weight = 0;           // 선택

        public Builder (String brand, String model, String color) {
            this.brand = brand;
            this.model = model;
            this.color = color;
        }

        public Builder country(String country) {
            this.country = country;
            return this;
        }

        public Builder year(int year) {
            this.year = year;
            return this;
        }

        public Builder weight(int weight) {
            this.weight = weight;
            return this;
        }

        public CarWithBuilder build() {
            return new CarWithBuilder(this);
        }
    }
}</code></pre>
<p>빌더 패턴은 다음과 같이 구현된다.</p>
<p>클래스 내부에 <code>static class</code>인 <code>Builder</code> 를 정의해놓고
그 내부에는 필수 매개변수를 받는 생성자와
<code>Setter</code> 역할을 하는 선택 매개변수 설정 메서드들로 구성되어 있고 
마지막 <code>build()</code> 메서드를 호출하여 객체를 생성하는 방식이다.</p>
</br>

<p>객체를 생성하는 코드를 한번 보자.</p>
<pre><code class="language-java">public static void main(String[] args) {
CarWithBuilder carWithBuilder = new CarWithBuilder.Builder(&quot;Benz&quot;, &quot;S-Class&quot;, &quot;Black&quot;)
                                .country(&quot;Germany&quot;)
                                .build();
}</code></pre>
<p>필수 매개변수를 통해 <code>Builder</code> 를 생성하고 필요에 따라 메서드를 호출하여 연속적으로 선택 매개변수를 설정할 수 있다. </p>
<p>이러한 방식이 가능한 이유는 빌더 패턴은 <code>Method Chaining</code> 방식을 이용하기 때문에 생성자와 하위 메서드들이 <code>Builder</code> 자신인 <code>this</code> 를 반환하기 때문에 연속적으로 값을 설정할 수 있다.</p>
<p>빌더 패턴의 특징을 보면 알 수 있듯이 위의 <code>점층적 생성자 패턴</code> 과 <code>자바빈즈 패턴</code> 의 장점을 모두 합쳤다는 것을 알 수 있다.</p>
<blockquote>
<p>💡 <strong><em>빌더 패턴의 유효성 검사</em></strong></p>
<ul>
<li>빌더 클래스 내부의 메서드에서 이루어진다. </li>
<li>주로 객체 생성 build() 메서드를 호출할 때 매개변수에 대한 유효성을 검사한다.</li>
</ul>
</blockquote>
</br>

<p>위의 빌더 패턴 방식은 그 자체만으로도 충분히 좋은 패턴임이 분명하지만,
빌더 패턴은 <strong><em>계층적으로 설계된 클래스</em></strong> 일 때, 빛을 발한다.</p>
<p>아래의 예시 코드를 보자.</p>
<pre><code class="language-java">public abstract class Pizza 
{
    public enum Topping 
      { 
          HAM, MUSHROOM, ONION, PEPPER, SAUSAGE 
      }
    final Set&lt;Topping&gt; toppings;

    abstract static class Builder&lt;T extends Builder&lt;T&gt;&gt; 
      {
        EnumSet&lt;Topping&gt; toppings = EnumSet.noneOf(Topping.class);
        public T addTopping(Topping topping) 
          {
            toppings.add(Objects.requireNonNull(topping));
            return self();
        }

        abstract Pizza build();

        protected abstract T self();
    }

    Pizza(Builder&lt;&gt; builder)
    {
        toppings = builder.toppings.clone();
    }
}</code></pre>
<p>다음 추상 클래스 코드에서 <code>Builder&lt;T extends Builder&lt;T&gt;&gt;</code> 부분을 주목해보자.</p>
<p>해당 방식은 <code>재귀적 타입 한정 (Recursive type bound)</code> 으로 불리는데 <strong><em>Item 30</em></strong> 에서 내용 정리가 나오므로 그때 자세히 정리하겠다.</p>
</br>

<p>해당 <code>Pizza</code> 클래스를 상속 받은 <code>NyPizza</code> 클래스와 <code>Calzone</code> 클래스 코드는 다음과 같다.</p>
<pre><code class="language-java">public class NyPizza extends Pizza 
{
    public enum Size 
      { 
          SMALL, MEDIUM, LARGE 
      }

    private final Size size;

    public static class Builder extends Pizza.Builder&lt;Builder&gt; 
      {
        private final Size size;

        public Builder(Size size) 
          {
            this.size = Objects.requireNonNull(size);
        }

        @Override 
          public NyPizza build() 
          {
            return new NyPizza(this);
        }

        @Override
          protected Builder self() 
          { 
              return this; 
          }
    }

    private NyPizza(Builder builder) {
        super(builder);
        size = builder.size;
    }

    @Override public String toString() 
      {
        return toppings + &quot;로 토핑한 뉴욕 피자&quot;;
    }
}</code></pre>
<pre><code class="language-java">public class Calzone extends Pizza 
{
    private final boolean sauceInside;

    public static class Builder extends Pizza.Builder&lt;Builder&gt; 
      {
        private boolean sauceInside = false;

        public Builder sauceInside() 
          {
            sauceInside = true;
            return this;
        }

        @Override 
          public Calzone build() 
          {
            return new Calzone(this);
        }

        @Override 
          protected Builder self() 
          { 
              return this; 
          }
    }

    private Calzone(Builder builder) {
        super(builder);
        sauceInside = builder.sauceInside;
    }

    @Override 
      public String toString() 
      {
        return String.format(&quot;%s로 토핑한 칼초네 피자 (소스는 %s에)&quot;, 
                          toppings, sauceInside ? &quot;안&quot; : &quot;바깥&quot;);
    }
}</code></pre>
<p>각 피자는 <code>Pizza</code> 를 상속받고, 각 피자의 빌더는 <code>Pizza.Builder</code> 를 상속 받는 것을 볼 수 있다.</p>
<p>해당 코드에서 재밌는 점을 발견할 수 있는데,
<code>NyPizza</code>, <code>Calzone</code> 내부 <code>Builder</code> 는 각각 <code>Pizza build()</code> 를 구현해야 했지만 실제로는 각각 <code>NyPizza build()</code>, <code>Calzone build()</code> 로 상위 클래스가 아닌 자신의 클래스로 반환하는 것을 볼 수 있다.</p>
<p>해당 기능을 <code>공변 반환 타이핑 (covariant return typing)</code> 이라고 부른다.</p>
<p>이렇게 되면 따로 <code>type casting</code>을 하지 않아도 돼서 편해진다.</p>
<blockquote>
<p>💡 <strong><em>공변 반환 타이핑 (covariant return typing) 이란?</em></strong></p>
<ul>
<li>JDK 1.5부터 추가된 개념이다. </li>
<li>부모 클래스의 메소드를 오버라이딩하는 경우, 부모 클래스의 반환 타입은 자식 클래스의 타입으로 변경이 가능하다.</li>
</ul>
</blockquote>
</br>

<p>이제 각 피자의 객체를 생성해보자.</p>
<pre><code class="language-java">public static void main(String[] args) {
        NyPizza nyPizza = new NyPizza.Builder(NyPizza.Size.LARGE)
                .addTopping(Pizza.Topping.SAUSAGE)
                .addTopping(Pizza.Topping.ONION)
                .build();

        Calzone calzone = new Calzone.Builder()
                .sauceInside()
                .addTopping(Pizza.Topping.ONION)
                .addTopping(Pizza.Topping.HAM)
                .build();

        System.out.println(nyPizza);
        System.out.println(calzone);
    }</code></pre>
<p><code>재귀적 한정 타입 (Recursive type bound)</code> 로 인해서 각 피자는 빌더로 객체를 생성할 때 <code>addTopping()</code> 메서드를 호출해도 자신의 Builder 타입 <code>this</code> 를 반환하기 때문에 문제없이 생성할 수 있는 것을 볼 수 있다.</p>
<p>개인적으로 해당 예제 코드를 보면서 타입이 안전하고 코드를 보기좋게 잘 짰다는 느낌을 받았다.</p>
</br>

<p>모든 방식이 그렇듯, 빌더 패턴도 <strong><span style="color: red">단점</span></strong>이 존재한다.</p>
<p>빌더 패턴을 쓰는 이상, 객체를 만들 때 마다 <strong><em>내부 빌더 코드를 매번 작성해야 한다.</em></strong>
빌더 코드 자체가 성능에 크게 영향을 끼치지는 않겠지만, 아주 미세한 성능 차이에도 민감한 상황이라면 해당 패턴을 쓰는데 고민을 해봐야 될 것이다.</p>
<br/>

<blockquote>
<p>📑 <strong><em>정리</em></strong></p>
<ul>
<li>복잡한 <strong>객체 생성을 추상화</strong>하고, 객체 생성에 필요한 세부 단계를 분리하여 캡슐화한다.</li>
<li>객체 생성에 필요한 단계를 나누어 놓기 때문에 원하는 설정 단계만을 선택하여 <strong>객체를 생성을 유연하게</strong> 할 수 있다.</li>
<li>빌더 패턴을 사용하여 객체를 생성하면 상태를 더이상 변경할 수 없어 <strong>객체의 일관성</strong>이 유지된다.</li>
<li>객체 생성 코드를 단순화하고 <strong>가독성을 향상</strong>시킨다.</li>
<li>내부 빌더 코드를 <strong>매번 작성</strong>해줘야 한다.</li>
<li><strong>매개변수가 4개 이상</strong>일 때 그 효과가 좋다고 말한다.</li>
</ul>
</blockquote>
<p><br/><br/></p>
<blockquote>
<p>🔎 <strong><em>참조</em></strong></p>
<ul>
<li>[[이펙티브 자바] 객체의 생성과 파괴 Item2 - 생성자에 매개변수가 많다면 빌더를 고려하라] (<a href="https://velog.io/@holidenty/%EC%9D%B4%ED%8E%99%ED%8B%B0%EB%B8%8C-%EC%9E%90%EB%B0%94-%EA%B0%9D%EC%B2%B4%EC%9D%98-%EC%83%9D%EC%84%B1%EA%B3%BC-%ED%8C%8C%EA%B4%B4-Item2-%EC%83%9D%EC%84%B1%EC%9E%90%EC%97%90-%EB%A7%A4%EA%B0%9C%EB%B3%80%EC%88%98%EA%B0%80-%EB%A7%8E%EB%8B%A4%EB%A9%B4-%EB%B9%8C%EB%8D%94%EB%A5%BC-%EA%B3%A0%EB%A0%A4%ED%95%98%EB%9D%BC">https://velog.io/@holidenty/%EC%9D%B4%ED%8E%99%ED%8B%B0%EB%B8%8C-%EC%9E%90%EB%B0%94-%EA%B0%9D%EC%B2%B4%EC%9D%98-%EC%83%9D%EC%84%B1%EA%B3%BC-%ED%8C%8C%EA%B4%B4-Item2-%EC%83%9D%EC%84%B1%EC%9E%90%EC%97%90-%EB%A7%A4%EA%B0%9C%EB%B3%80%EC%88%98%EA%B0%80-%EB%A7%8E%EB%8B%A4%EB%A9%B4-%EB%B9%8C%EB%8D%94%EB%A5%BC-%EA%B3%A0%EB%A0%A4%ED%95%98%EB%9D%BC</a>)</li>
<li><a href="https://ingnoh.tistory.com/153">공변 반환 타입</a></li>
</ul>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[ITEM 01. 생성자 대신 정적 팩토리 메서드를 고려하라]]></title>
            <link>https://velog.io/@nakta-y/ITEM-01.-%EC%83%9D%EC%84%B1%EC%9E%90-%EB%8C%80%EC%8B%A0-%EC%A0%95%EC%A0%81-%ED%8C%A9%ED%86%A0%EB%A6%AC-%EB%A9%94%EC%84%9C%EB%93%9C%EB%A5%BC-%EA%B3%A0%EB%A0%A4%ED%95%98%EB%9D%BC</link>
            <guid>https://velog.io/@nakta-y/ITEM-01.-%EC%83%9D%EC%84%B1%EC%9E%90-%EB%8C%80%EC%8B%A0-%EC%A0%95%EC%A0%81-%ED%8C%A9%ED%86%A0%EB%A6%AC-%EB%A9%94%EC%84%9C%EB%93%9C%EB%A5%BC-%EA%B3%A0%EB%A0%A4%ED%95%98%EB%9D%BC</guid>
            <pubDate>Tue, 14 Nov 2023 05:24:03 GMT</pubDate>
            <description><![CDATA[<h1 id="🌱-들어가면서">🌱 들어가면서</h1>
<p><code>프로그래밍</code>이란 마치 작은 세계를 창조하는 것과 같다고 생각했다. 그 세계 속에서 <code>객체</code>는 주인공이자 배역, 그리고 어떤 면에서는 작은 조력자이기도 하다. 그렇기에 <code>객체</code>가 어떻게 태어나고, 어떻게 움직이고, 어떻게 사라지는지를 고민하는 것은 자바 프로그래밍을 하는데에 있어서 <code>객체</code>의 <code>생명주기</code>에 대한 고민이 절로 떠오르게 된다.</p>
<blockquote>
<p>💡 <strong>객체의 생명주기란?</strong>
객체가 생성된 후부터 폐기될 때 까지의 기간으로, 객체가 생성되어 메모리에 올라가는 시점부터 더이상 사용하지 않아 파괴되면서 메모리에서 사라지게 되는 기간을 말한다.</p>
</blockquote>
</br>
</br>

<h1 id="🙃-객체-생성과-파괴가-중요한-이유">🙃 객체 생성과 파괴가 중요한 이유</h1>
<p>그렇다면 객체의 생명주기가 왜 중요할까? 
결론부터 말하면 자바에서 객체 생성과 파괴를 고려해야 하는 이유는 프로그램의 성능, 메모리 관리, 코드 가독성, 유지보수성 등 여러 측면에서 중요한 영향을 미친다. 
차근차근 알아보자.</p>
<h3 id="1-객체-생성-비용">1. 객체 생성 비용</h3>
<p>객체 생성은 비싸고 비용이 많이 드는 작업이다. 64비트 JDK에서 객체는 12바이트의 헤더, 8바이트의 배수로 이뤄지기 때문에 최소 16바이트를 소비한다. 또한 객체 참조에도 메모리를 소비한다. 수치로만 봤을 때는 적은 비용이라고 생각이 들지만, 전체 프로그램을 생각한다면 무시할 수 없는 비용이다. 예를 들어서, <code>primitive int</code>는 4바이트에 불과하지만, <code>Integer</code> 객체를 사용할 때는 16바이트(객체) + 8바이트(참조)로 <strong>5배</strong>나 되는 메모리를 사용하게 된다.</p>
<h3 id="2-메모리-누수">2. 메모리 누수</h3>
<p>메모리 누수란 더 이상 사용하지 않는 객체들이 <code>힙(Heap)</code> 영역에 남아있어 불필요하게 메모리를 차지하고 있는 상황을 의미한다. 사용하지 않는 객체들이 계속 메모리에 상주하고 있으면 성능 저하를 야기하고, 최악의 상황에는 <code>Out of Memory Error</code>를 뱉어내게 되므로 주의가필요하다.</p>
<h3 id="3-가비지-컬렉션-garbage-collection">3. 가비지 컬렉션 (Garbage Collection)</h3>
<p>자바에서는 위와 같은 메모리 누수를 방지하기 위해서 <code>Garbage Collection</code>을 도입하여 더 이상 참조되지 않는 객체를 탐지하여 자동으로 메모리에서 해제해주는 런타임 시스템이 있다. 보기에는 만능인 것처럼 보이지만, 가비지 컬렉션도 결국 하나의 <code>프로그램</code>이기 때문에 가비지 컬렉션이 더 자주 발생하도록 유발된다면 프로그램의 전체적인 성능에 영향을 미칠 수 밖에 없다. 특히 가비지 컬렉션으로 해제되지 않는 자원을 가진 객체 (InputStream, Socket) 등을 가질 경우에는 명시적으로 자원을 해제하는 것이 필요하다.</p>
</br>
</br>

<h1 id="🙃-객체-생성-방법">🙃 객체 생성 방법</h1>
<h3 id="1-생성자">1. 생성자</h3>
<p>인스턴스가 생성될 때마다 호출되는 <code>인스턴스 초기화 메서드</code>로 객체가 생성될 때마다 초기화 해주는 메서드로 생각하면 된다.</p>
<pre><code class="language-java">public class Person {
    private String name;
    private String major;

    public Person(String name, String major) {
        this.name = name;
        this.major = major;
    }

    public static void main(String[] args) {
        Person person = new Person(&quot;mark&quot;, &quot;IT&quot;);
    }
}</code></pre>
<br/>

<h3 id="2-정적-팩토리-메서드">2. 정적 팩토리 메서드</h3>
<p><code>객체 생성의 역할을 하는 클래스 메서드</code>로 말로 설명하기는 어려운 느낌이 있으므로 간단한 예시를 보자.</p>
<pre><code class="language-java">public class Person {
    private String name;
    private String major;

    private Person() {}

    public static Person of(String name, String major) {
        Person person = new Person();
        person.name = name;
        person.major = major
        return person;
    }

    public static void main(String[] args) {
        Person person = Person.of(&quot;mark&quot;, &quot;IT&quot;);
    }
}</code></pre>
<p>위의 코드를 보면 정적 팩토리 메서드를 사용해서 <code>new</code> 키워드를 사용해 생성자를 직접 호출하지 않고 <code>static method</code>를 이용해 객체를 생성하여 객체를 생성했다. </p>
</br>
</br>

<h1 id="🙃-정적-팩토리-메서드의-장점">🙃 정적 팩토리 메서드의 장점</h1>
<p>왜 굳이 위와 같이 <code>static method</code>를 사용해서 객체를 생성할까? 오히려 위의 생성자 방식보다 코드가 더 복잡해 보이기도 한다. 하지만, 이러한 정적 팩토리 메서드 방식에는 여러가지 장점이 있다.</p>
</br>

<h3 id="1-이름을-가질-수-있다">1. 이름을 가질 수 있다.</h3>
<p>자바의 생성자의 이름은 클래스의 이름과 반드시 같아야 한다. 
이는, 자바에서 정의하는 생성자의 특징이나 제한사항으로 반드시 지켜져야 한다. </p>
<p>하지만, 정적 팩토리 메서드는 객체를 생성할 때 메서드에 <strong>이름</strong>을 지어줄 수 있다!
이게 무슨 의미일까? 아래 예제 코드를 보자.</p>
<pre><code class="language-java">public class Laptop {
    private String brand;
    private int price;

    public Laptop(String brand, int price) {
        this.brand = brand;
        this.price = price;
    }

    public Laptop(String brand) {
        this.brand = brand;
    }

    public Laptop(int price) {
        this.price = price;
    }

    public static void main(String[] args) {
        Laptop laptopWithBoth = new Laptop(&quot;SAMSUNG&quot;, 123_450);
        Laptop laptopWithBrand = new Laptop(&quot;LG&quot;);
        Laptop laptopWithPrice = new Laptop(456_780);
    }
}</code></pre>
<p>위의 코드는 생성자 방식을 이용해서 객체를 생성하는 코드이다.</p>
<p><code>brand</code>와 <code>price</code> 모두 초기화 값을 주고 만드는 객체와 각각의 초기화 값만 주고 만드는 객체로 총 3개의 객체가 만들어졌다. 해당 코드의 큰 특징은 객체를 만들 때 모두 <code>new Laptop</code> 생성자를 사용해서 만들었다는 것이다. </p>
<p>이렇게 생성된 객체들은 <strong><em>어떤 의도인지, 어떤 의미를 가지는지</em></strong> 파악하기가 어렵다는 단점이 있다.</p>
<p>이번엔 정적 팩토리 메서드를 사용한 방법을 보자.</p>
<pre><code class="language-java">public class Laptop {
    private String brand;
    private int price;

    private Laptop() {}

    public static Laptop of(String brand, int price) {
        Laptop laptop = new Laptop();
        laptop.brand = brand;
        laptop.price = price;
        return laptop;
    }

    public static Laptop withBrand(String brand) {
        Laptop laptop = new Laptop();
        laptop.brand = brand;
        return laptop;
    }

    public static Laptop withPrice(int price) {
        Laptop laptop = new Laptop();
        laptop.price = price;
        return laptop;
    }

    public static void main(String[] args) {
        Laptop laptopWithBoth = Laptop.of(&quot;SAMSUNG&quot;, 123_450);
        Laptop laptopWithBrand = Laptop.withBrand(&quot;LG&quot;);
        Laptop laptopWithPrice = Laptop.withPrice(456_780);
    }
}</code></pre>
<p>위의 코드를 보면 객체 생성을 할 때 호출하는 메서드가 <strong>이름</strong> 을 가진다는 것을 알 수 있다. </p>
<p><code>of</code>, <code>withBrand</code>, <code>withPrice</code>의 이름을 가짐으로써 생성된 객체들이 어떤 의도로 생
성 되었는지 명확하게 알 수 있다.</p>
</br>

<p>추가로, 생성자는 <strong>*&quot;하나의 시그니처로 생성자를 하나만 만들 수 있다&quot;*</strong> 라는 제약이 있다.
역시 무슨 의미인지 모르겠으므로 아래 예제 코드를 보자.</p>
<pre><code class="language-java">public class Laptop {
    private String brand;
    private String model;
    private int price;

    public Laptop(String brand) {
        this.brand = brand;
    }

    public Laptop(String model) {
        this.model = model;
    }
}</code></pre>
<p>해당 코드는 <code>컴파일 에러</code>가 발생한다.
왜일까? 그 이유는 매개변수 타입과 수가 같은 생성자를 중복해서 선언했기 때문이다.
사람의 입장에서는 같은 <code>String</code> 타입이라도 <strong>brand</strong>와 <strong>model</strong>의 차이점을 알고있지만, 
컴퓨터의 입장에서는 <strong>brand</strong>와 <strong>model</strong> 상관없이 같은 <code>String</code> 타입으로 보이기 때문에  생성자가 호출되면 어떤 생성자를 호출할지 모르기 때문이다.</p>
<p>하지만, 정적 팩토리 메서드 방법에서는 어떨까?</p>
<pre><code class="language-java">public class Laptop {
    private String brand;
    private String model;
    private int price;

    private Laptop(){}

    public static Laptop withBrand(String brand) {
        Laptop laptop = new Laptop();
        laptop.brand = brand;
        return laptop;
    }

    public static Laptop withModel(String model) {
        Laptop laptop = new Laptop();
        laptop.model = model;
        return laptop;
    }
}</code></pre>
<p>해당 코드는 당연하게도 <code>컴파일 에러</code>가 발생하지 않는다.
<strong>brand</strong>와 <strong>model</strong> 을 넣어주는 생성자 메서드의 이름이 각각 다르기 때문에 선언된 메서드 이름을 호출하여 원하는 필드에 값을 넣어 객체를 생성하는 것이 가능하다.</p>
</br>

<blockquote>
<p>📑 <strong><em>정리</em></strong></p>
<ul>
<li>객체 생성 정적 메서드에 이름을 부여하여 <strong>코드 가독성을 높일 수 있다.</strong></li>
<li>자바에서 제공하는 <strong>생성자의 매개변수 제약 조건이 사라진다.</strong> </li>
</ul>
</blockquote>
</br>
</br>

<h3 id="2-호출될-때마다-인스턴스를-새로-생성하지-않아도-된다">2. 호출될 때마다 인스턴스를 새로 생성하지 않아도 된다.</h3>
<p>정적 팩토리 메서드는 객체의 생성을 책임지고 있으므로 인스턴스의 생성 방식에 대해서 관리할 수 있다. 이 말은 다음과 같은 의미를 말한다.</p>
<blockquote>
<ol>
<li>항상 새로운 객체가 반환되도록 할 수 있다.</li>
<li>새로운 객체를 생성을 금지하고 항상 같은 객체만 반환하게 할 수 있다.</li>
</ol>
</blockquote>
<p>  생성자를 사용하면 <code>new</code> 키워드가 붙기 때문에 항상 새로운 객체를 생성한다. 하지만 <code>Collections</code> 처럼 클래스 메서드만 이용하는 것과 같이 <strong><em>객체를 만들지 않도록 하거나 또는 객체를 오로지 하나만 쓸 수 있도록</em></strong> 만들고 싶은 경우에 정적 팩토리 메서드는 빛을 발한다.</p>
<p>먼저 객체화 불가 클래스 예제 코드를 보자.</p>
<pre><code class="language-java">  public class Collections {
    private Collections() {
    }

    @SuppressWarnings(&quot;unchecked&quot;)
    public static &lt;T extends Comparable&lt;? super T&gt;&gt; void sort(List&lt;T&gt; list) {
        list.sort(null);
    }

    @SuppressWarnings({&quot;unchecked&quot;, &quot;rawtypes&quot;})
    public static &lt;T&gt; void sort(List&lt;T&gt; list, Comparator&lt;? super T&gt; c) {
        list.sort(c); 
    }

    ...
}
</code></pre>
<p>다음 코드는 익숙하게 사용했던 java.util 내의 Collections 유틸리티 클래스 코드 일부이다. 유틸리티 클래스는 주로 관련된 메서드 집힙을 <code>static method</code>로 제공하여 특정 작업을 수행하는데 사용되는 클래스로 <strong>해당 클래스는 상태를 저장하지 않기 때문에 객체를 만들 필요가 없다.</strong> </p>
<p>다음은 객체를 단 하나만 생성하는 예제 코드를 보자.</p>
<pre><code class="language-java">public class Singleton {
    private static Singleton INSTANCE = new Singleton();

    private Singleton(){}

    public static Singleton getInstance() {
        return INSTANCE;
    }
}</code></pre>
<p>다음 코드는 <code>static method</code> 인 <code>getInstance()</code> 를 호출하면 항상 같은 객체를 반환하게 만드는 클래스 코드이다. 이 말은 무엇을 의미할까?</p>
<p><strong><em>정적 팩토리 메서드는 객체 생성을 통제할 수 있다는 말이 된다.</em></strong></p>
<p>정적 팩토리 메서드를 이용하면 객체 생성을 통제할 수 있는 클래스인 <code>인스턴스 통제 클래스 (instance-controlled class)</code> 를 만들 수 있고 단 하나의 객체만을 생성하고 사용할 수 있게 만드는 <code>불변 클래스 (noninstantiable)</code> 를 만들 수도 있다.</p>
<blockquote>
<p>💡 <strong>인스턴스 통제 클래스 (instance-controlled class) 란?</strong>
특정 클래스의 인스턴스 생성을 제어하거나 관리하는 디자인 패턴을 나타내며, 이 디자인 패턴은 일반적으로 싱글톤(Singleton) 패턴과 관련이 있다.</p>
</blockquote>
<blockquote>
<p>💡 <strong>하위 타입이란?</strong> <strong>불변 클래스 (noninstantiable) 란?</strong>
한번 생성되면 내부 상태가 변경되지 않는 클래스를 말한다. 즉, 객체의 상태(멤버 변수)를 수정할 수 없는 클래스를 의미한다.</p>
</blockquote>
</br>

<blockquote>
<p>📑 <strong><em>정리</em></strong></p>
<ul>
<li><strong>불필요한 객체의 생성을 막을 수 있다.</strong></li>
<li>상황에 따라서 <strong>객체 생성을 통제</strong>하는 것이 가능하다.</li>
</ul>
</blockquote>
</br>
</br>

<h3 id="3-반환-타입의-하위-타입-객체를-반환할-수-있다">3. 반환 타입의 하위 타입 객체를 반환할 수 있다.</h3>
<blockquote>
<p>💡 <strong>하위 타입이란?</strong>
객체 지향 프로그래밍에서 상속 관계에서 나타나는 개념으로, 하위 타입은 부모 클래스 또는 인터페이스를 상속받은 클래스나 인터페이스를 의미한다.
<img src="https://velog.velcdn.com/images/nakta-y/post/baceb01f-d159-4bce-b32c-90d2d9a57b54/image.webp" alt=""> <a href="https://tutorial.eyehunts.com/java/java-polymorphism-definition-type-example/">이미지 출처</a></p>
</blockquote>
<p>자기 자신이 아닌 반환할 객체의 클래스를 선택할 수 있다는건 클래스에서 만들어줄 객체의 클래스를 선택할 수 있는 <strong>유연함</strong>이 생긴다는 말과 같다.</p>
<p>아래 예제 코드를 보자.</p>
<pre><code class="language-java">public class Membership {
    static Membership of(int point) {
        if (point &gt;= 120_000) {
            return new VIP();
        } else if (point &gt;= 100_000){
            return new Gold();
        } else {
            return new White();
        }
    }
}

class VIP extends Membership {
}

class Gold extends Membership {
}

class White extends Membership {
}</code></pre>
<p>다음 코드는 <code>Membership</code> 클래스의 <code>static method</code>를 호출하여
하위 타입인 <code>VIP</code>, <code>Gold</code>, <code>White</code> 객체를 만들어 반환하는 코드이다. </p>
<p>위 방식의 장점은 <code>Membership</code> 클래스 내부에서 하위 타입을 구현한 것도 아니고,
<code>Membership</code> 클래스는 하위 타입 생성을 연결해주는 역할만을 하기 때문에,
구현 로직은 숨길 수 있으면서 API를 경량화 할수 있기 때문에 난이도를 낮출 수 있다.</p>
<p>프로그래머의 입장에서 해당 메서드만 보고 객체를 가져올 것임을 알기 때문에,
굳이 하위 타입을 찾아볼 필요가 없게된다.</p>
</br>

<blockquote>
<p>📑 <strong><em>정리</em></strong></p>
<ul>
<li>하위 타입의 객체를 <strong>유연하게 반환</strong>할 수 있다.</li>
<li>하위 타입의 <strong>구현체를 숨길 수 있다.</strong></li>
<li>API를 <strong>경량화</strong> 할 수 있다.</li>
<li>프로그래머의 API 사용 <strong>난이도를 낮춰줄 수 있다.</strong></li>
</ul>
</blockquote>
</br>
</br>

<h3 id="4-입력-매개변수에-따라-매번-다른-클래스의-객체를-반환할-수-있다">4. 입력 매개변수에 따라 매번 다른 클래스의 객체를 반환할 수 있다.</h3>
<p>4번 내용은 3번 내용과 맥락이 같다고 볼 수 있다.</p>
<p>3번에서 봤던 예제 코드를 다시 살펴보자.</p>
<pre><code class="language-java">public class Membership {
    static Membership of(int point) {
        if (point &gt;= 120_000) {
            return new VIP();
        } else if (point &gt;= 100_000){
            return new Gold();
        } else {
            return new White();
        }
    }
}

class VIP extends Membership {
}

class Gold extends Membership {
}

class White extends Membership {
}</code></pre>
<p><code>Membership</code> 클래스 내의 <code>static method</code> 로직을 보면,
매개변수로 주어진 <code>point</code> 값에 따라서 <code>VIP</code>, <code>Gold</code>, <code>White</code> 객체를 선택적으로 반환하고 있다는 것을 알 수 있다.</p>
</br>

<blockquote>
<p>📑 <strong><em>정리</em></strong></p>
<ul>
<li><strong>선택적으로</strong> 하위 타입의 객체를 반환할 수 있다.</li>
</ul>
</blockquote>
</br>
</br>

<h3 id="5-정적-팩토리-메서드를-작성하는-시점에서-반환할-객체의-클래스가-존재하지-않아도-된다">5. 정적 팩토리 메서드를 작성하는 시점에서 반환할 객체의 클래스가 존재하지 않아도 된다.</h3>
<p>말이 너무 어려우므로, 코드를 같이 살펴보자.</p>
<pre><code class="language-java">import java.util.Optional;
import java.util.ServiceLoader;

public interface Item {
    String type();

    static Item getInstance() {
        ServiceLoader&lt;Item&gt; serviceLoader = ServiceLoader.load(Item.class);

        Optional&lt;Item&gt; item = serviceLoader.findFirst();

        return item.orElseThrow(() -&gt; new IllegalStateException(&quot;No implementation found&quot;));
    }
}

class ImportantItem implements Item {
    @Override
    public String type() {
        return &quot;Important&quot;;
    }
}

class NormalItem implements Item {
    @Override
    public String type() {
        return &quot;Normal&quot;;
    }
}</code></pre>
<p>다음 코드는 메서드와 반환할 타입만 정해두고 실제로 반환될 클래스는 나중에 구현하는게 가능하다. 협업을 진행할 때 인터페이스를 합의해서 만들고 <code>static method</code> 를 미리 만들어 두는 것이 가능할 것 같다. </p>
<p>예제 코드는 다음과 같이, 나와있지만 실제로 <code>findFirst()</code> 메서드를 호출했을 때, 어떤 구현 클래스를 가져올지 모르기 때문에, 실제로 활용하는 환경에서는 어떻게 사용하는지 궁금하다.</p>
</br>

<blockquote>
<p>📑 <strong><em>정리</em></strong></p>
<ul>
<li><strong>구현 클래스가 없어도</strong> 정적 팩토리 메서드를 이용해 객체를 반환하는 것이 가능하다.</li>
</ul>
</blockquote>
</br>
</br>

<h1 id="🙃-정적-팩토리-메서드의-단점">🙃 정적 팩토리 메서드의 단점</h1>
<h3 id="1-상속을-하려면-public-혹은-protected-생성자가-필요하니-정적-팩토리-메서드만-제공하면-하위-클래스를-만들-수-없다">1. 상속을 하려면 public 혹은 protected 생성자가 필요하니 정적 팩토리 메서드만 제공하면 하위 클래스를 만들 수 없다.</h3>
<p>정적 팩토리 메서드를 사용하면 <code>private</code> 생성자를 이용하므로 <code>상속</code> 을 할 수 없다. 
하지만, <code>조합</code> 원칙이나 <code>불변 타입</code>을 만드는 관점에서 볼 때는 장점이 되기도 한다.</p>
<p>실제로 정적 팩토리 메서드를 이용하려면 해당 클래스의 역할과 책임을 잘 파악해서 관계를 어떻게 맺을지 고민해보는 것이 필요해 보인다.</p>
<blockquote>
<p>💡 <strong>조합 원칙이란?</strong>
객체 지향 프로그래밍에서 코드를 구성할 때, 상속보다는 객체를 조합하여 사용하라는 원칙을 의미한다. 이는 유연성, 재사용성, 복잡성 감소 등의 이점을 제공하여 코드의 유지보수와 확장이 더 용이하도록 한다.
[상속 보다는 조합을 사용하자] (<a href="https://tecoble.techcourse.co.kr/post/2020-05-18-inheritance-vs-composition/">https://tecoble.techcourse.co.kr/post/2020-05-18-inheritance-vs-composition/</a>)</p>
</blockquote>
</br>

<blockquote>
<p>📑 <strong><em>정리</em></strong></p>
<ul>
<li>정적 팩토리 메서드만을 이용해 객체를 생성할 경우 <strong>상속이 불가능하다.</strong></li>
<li><strong>조합 원칙</strong>이나 <strong>불변 타입</strong> 관점에서 볼 때는 장점이 될 수 있다.</li>
</ul>
</blockquote>
</br>
</br>

<h3 id="2-정적-팩토리-메서드는-프로그래머가-찾기-어렵다">2. 정적 팩토리 메서드는 프로그래머가 찾기 어렵다.</h3>
<p>생성자는 자바의 기본 문법이기 때문에 보자마자 바로 파악할 수 있지만, 정적 팩토리 메서드는 하나의 기법으로 프로그래머가 해당 클래스에 정적 팩토리 메서드를 사용했다는 사실을 알아야 파악할 수 있다는 단점이 있다. 이를 완화하기 위해서는 널리 통용되는 이름을 정적 팩토리 메서드 이름으로 사용하는 방법이 있다.</p>
</br>

<h4 id="💡-정적-팩토리-메서드-네이밍-방식">💡 정적 팩토리 메서드 네이밍 방식</h4>
<table>
<thead>
<tr>
<th align="left">메서드</th>
<th>설명</th>
<th align="center">예제</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><code>from</code></td>
<td>매개변수를 하나 받아 해당 타입의 인스턴스를 반환(형변환 method)</td>
<td align="center">Date d = Date.from(instant);</td>
</tr>
<tr>
<td align="left"><code>of</code></td>
<td>여러 매개변수를 받아 적합한 타입의 인스턴스를 반환하는 집계 메서드</td>
<td align="center">Set<Rank> faceCards = EnumSet.of(JACK, QUEEN, KING);</td>
</tr>
<tr>
<td align="left"><code>valueOf</code></td>
<td>from과 of의 더 자세한 버전</td>
<td align="center">Set<Rank> faceCards = BigInteger.valueOf(Integer.MAX_VALUE);</td>
</tr>
<tr>
<td align="left"><code>instance getInstance</code></td>
<td>매개변수를 받을 경우 매개변수로 명시한 인스턴스를 반환하지만 같은 인스턴스임을 보장하지는 않음</td>
<td align="center">StackWalker luke = StackWalker.getInstance(options);</td>
</tr>
<tr>
<td align="left"><code>create newInstance</code></td>
<td>instance 혹은 getInstance와 같지만 매번 새로운 인스턴스를 생성해 반환한다.</td>
<td align="center">Object newArr = Array.newInstance(classObj,arrayLen);</td>
</tr>
<tr>
<td align="left"><code>getType</code></td>
<td>getInstance와 같으나 생성할 클래스가 아닌 다른 클레스의 팩터리 메서드를 정의할 때 사용한다.</td>
<td align="center">FileStore fs = Files.getFileStore(path)</td>
</tr>
<tr>
<td align="left"><code>newType</code></td>
<td>newInstance와 같으나 생성할 클래스가 아닌 다른 클레스의 팩터리 메서드를 정의할 때 사용한다.</td>
<td align="center">BufferedReader br = Files.newBufferedReader(path);</td>
</tr>
<tr>
<td align="left"><code>type</code></td>
<td>getType과 newType의 간결한 버전</td>
<td align="center">List<Complaint> litany = Collections.list(legachLitancy);</td>
</tr>
</tbody></table>
<p></br></br></p>
<blockquote>
<p>🔎 <strong><em>참조</em></strong></p>
<ul>
<li>[객체 생성 비용 줄이기] (<a href="https://jaehochoe.medium.com/betterkotlin-%ED%9A%A8%EC%9C%A8%EC%84%B1-f88e8b5f81b0">https://jaehochoe.medium.com/betterkotlin-%ED%9A%A8%EC%9C%A8%EC%84%B1-f88e8b5f81b0</a>)</li>
<li>[정적 팩토리 메서드는 왜 사용할까?] (<a href="https://tecoble.techcourse.co.kr/post/2020-05-26-static-factory-method/">https://tecoble.techcourse.co.kr/post/2020-05-26-static-factory-method/</a>)</li>
<li>[[이펙티브 자바] 객체의 생성과 파괴 Item1 - 생성자 대신 정적 팩터리 메서드를 고려하라] (<a href="https://velog.io/@holidenty/%EC%9D%B4%ED%8E%99%ED%8B%B0%EB%B8%8C-%EC%9E%90%EB%B0%94-%EA%B0%9D%EC%B2%B4%EC%9D%98-%EC%83%9D%EC%84%B1%EA%B3%BC-%ED%8C%8C%EA%B4%B4-Item1-nul1xqx9">https://velog.io/@holidenty/%EC%9D%B4%ED%8E%99%ED%8B%B0%EB%B8%8C-%EC%9E%90%EB%B0%94-%EA%B0%9D%EC%B2%B4%EC%9D%98-%EC%83%9D%EC%84%B1%EA%B3%BC-%ED%8C%8C%EA%B4%B4-Item1-nul1xqx9</a>)</li>
</ul>
</blockquote>
]]></description>
        </item>
    </channel>
</rss>