<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>juoh0920.log</title>
        <link>https://velog.io/</link>
        <description>동료들이 같이 일하고 싶어하는 백엔드 개발자가 되고자 합니다!</description>
        <lastBuildDate>Mon, 01 Aug 2022 07:01:39 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. juoh0920.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/ljo_0920" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[디자인 패턴 - 템플릿 메소드 패턴]]></title>
            <link>https://velog.io/@ljo_0920/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-%ED%85%9C%ED%94%8C%EB%A6%BF-%EB%A9%94%EC%86%8C%EB%93%9C-%ED%8C%A8%ED%84%B4</link>
            <guid>https://velog.io/@ljo_0920/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-%ED%85%9C%ED%94%8C%EB%A6%BF-%EB%A9%94%EC%86%8C%EB%93%9C-%ED%8C%A8%ED%84%B4</guid>
            <pubDate>Mon, 01 Aug 2022 07:01:39 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=582754">헤드 퍼스트 디자인 패턴</a>을 읽고 정리한 글입니다.</p>
<blockquote>
<p>템플릿 메소드 패턴은 알고리즘의 골격을 정의한다. 템플릿 메소드를 사용하면 알고리즘의 일부 단계를 서브클래스에서 구현할 수 있으며, 알고리즘의 구조는 그대로 유지하면서 알고리즘의 특정 단계를 서브클래스에서 재정의할 수도 있다.</p>
</blockquote>
<ul>
<li>서브클래스에서 언제든 필요할 때마다 알고리즘을 가져다가 쓸 수 있도록 캡슐화 해보자</li>
<li>할리우드 디자인 원칙을 알아보자</li>
</ul>
</br>

<h2 id="개요---커피와-홍차-만들기">개요 - 커피와 홍차 만들기</h2>
<p>커피와 홍차는 둘 다 카페인을 가지고 있고, 가장 중요한 점은 매우 비슷한 방법으로 만들어진다는 것이다.</p>
<p>커피 만드는 법</p>
<ol>
<li>물을 끓인다.</li>
<li>끓는 물에 커피를 우려낸다.</li>
<li>커피를 컵에 따른다.</li>
<li>설탕과 우유를 추가한다.</li>
</ol>
<p>홍차 만드는 법</p>
<ol>
<li>물을 끓인다.</li>
<li>끓는 물에 찻잎을 우려낸다.</li>
<li>홍차를 컵에 따른다.</li>
<li>레몬을 추가한다.</li>
</ol>
</br>

<h2 id="coffee-클래스와-tea-클래스-만들기">Coffee 클래스와 Tea 클래스 만들기</h2>
<p>이제 커피와 홍차를 만드는 클래스를 준비해 보자.</p>
<pre><code class="language-java">public class Coffee {

    public void prepareRecipe() {
        boilWater();
        brewCoffeeGrinds();
        pourInCup();
        addSugarAndMilk();
    }

    private void boilWater() {
        System.out.println(&quot;물 끓이는 중&quot;);
    }

    private void brewCoffeeGrinds() {
        System.out.println(&quot;필터로 커피를 우려내는 중&quot;);
    }

    private void pourInCup() {
        System.out.println(&quot;컵에 따르는 중&quot;);
    }

    private void addSugarAndMilk() {
        System.out.println(&quot;설탕과 우류를 추가하는 중&quot;);
    }

}</code></pre>
<pre><code class="language-java">public class Tea {

    public void prepareRecipe() {
        boilWater();
        steepTeaBag();
        pourInCup();
        addLemon();
    }

    private void boilWater() {
        System.out.println(&quot;물 끓이는 중&quot;);
    }

    private void steepTeaBag() {
        System.out.println(&quot;필터로 커피를 우려내는 중&quot;);
    }

    private void pourInCup() {
        System.out.println(&quot;컵에 따르는 중&quot;);
    }

    private void addLemon() {
        System.out.println(&quot;설탕과 우류를 추가하는 중&quot;);
    }

}</code></pre>
<p>조금 전에 Coffee 클래스에서 구현했던 방법과 비슷한 것을 느낄 수 있다.</p>
<p>두 번째와 네 번째 단계가 조금 다르지만 기본적으로 같다고 할 수 있다. 이렇게 공통적으로 코드가 중복된다면 디자인 수정을 고려해보자. </p>
<p>혹시 Coffee와 Tea 클래스는 거의 같으니까 두 클래스의 공통된 부분을 추상화해서 베이스 클래스로 만드는 것을 어떨까??</p>
</br>

<h2 id="coffee-클래스와-tea-클래스-추상화하기">Coffee 클래스와 Tea 클래스 추상화하기</h2>
<p><img src="https://velog.velcdn.com/images/ljo_0920/post/5590618d-57ca-4137-a2f7-11b6b6bcde92/image.png" alt=""></p>
<p><a href="https://books.google.co.kr/books?id=Lw8LEAAAQBAJ&amp;printsec=frontcover&amp;hl=ko&amp;source=gbs_ge_summary_r&amp;cad=0#v=onepage&amp;q&amp;f=false">https://books.google.co.kr/books?id=Lw8LEAAAQBAJ&amp;printsec=frontcover&amp;hl=ko&amp;source=gbs_ge_summary_r&amp;cad=0#v=onepage&amp;q&amp;f=false</a></p>
<p>CaffeineBeverage</p>
<ul>
<li>prepareRecipe() 메소드는 서브클래스마다 다르기 때문에 추상 메서드로 선언</li>
<li>boilWater()와 putInCup() 메서드는 두 클래스에서 공통으로 사용되므로 슈퍼클래스에 정의</li>
</ul>
<p>서브 클래스(Coffee, Tea)</p>
<ul>
<li>서브 클래스는 prepareRecipe() 메소드를 오버라이드해서 음료 제조법을 구현</li>
<li>Coffee나 Tea 클래스에만 있던 메소드는 서브 클래스에 그대로 남김</li>
</ul>
<aside>
💡 혹시 또 다른 공통점을 놓치지는 않았을까?? 알고리즘이 똑같은 메서드는 추상화할 수 없을까??

</aside>

</br>

<h2 id="추상화-방법-들여다보기">추상화 방법 들여다보기</h2>
<p>제조법을 다시 살펴보면 커피와 홍차 제조법의 알고리즘이 똑같다는 사실을 알 수 있다.</p>
<ol>
<li>물을 끓인다.</li>
<li><code>뜨거운 물을 사용해서 커피 또는 찻잎을 우려낸다.</code></li>
<li>만들어진 음료를 컵에 따른다.</li>
<li><code>각 음료에 맞는 첨가물을 추가한다.</code></li>
</ol>
<p><img src="https://velog.velcdn.com/images/ljo_0920/post/85c2f026-2993-4dc8-a814-8a972eb38ef6/image.png" alt=""></p>
<p><a href="https://books.google.co.kr/books?id=Lw8LEAAAQBAJ&amp;printsec=frontcover&amp;hl=ko&amp;source=gbs_ge_summary_r&amp;cad=0#v=onepage&amp;q&amp;f=false">https://books.google.co.kr/books?id=Lw8LEAAAQBAJ&amp;printsec=frontcover&amp;hl=ko&amp;source=gbs_ge_summary_r&amp;cad=0#v=onepage&amp;q&amp;f=false</a></p>
<ul>
<li>2, 4 이 둘은 추상화되지 않았지만 똑같다. 서로 다른 음료에 적용될 뿐이다.</li>
<li>이제 prepareRecipe() 까지 추상화하는 방법을 찾아보자. 생각해보면 커피를 필터로 우려내는 일과 티백을 물에 넣어서 홍차를 우려내는 일은 별로 다르지 않다. 사실 거의 같다고 볼 수 있기 때문에 brew() 메서드를 만들어서 커피를 우려내는 홍차를 우려내는 똑같은 메서드를 사용해보자.</li>
<li>이와 마찬가지로  설탕과 우유를 추가하는 일이나 레몬을 추가하는 일도 마찬가지다. 음료에 첨가물을 넣는다는 사실 자체는 똑같기 때문이다. 따라서 addConfiments() 메소드를 양쪽에 사용해보자</li>
</ul>
<pre><code class="language-java">public abstract class CaffeineBeverage {

    public void prepareRecipe() {
        boilWater();
        brew();
        pourInCup();
        addCondiments();
    }

    protected abstract void addCondiments();

    protected abstract void brew();

    private void boilWater() {
        System.out.println(&quot;물 끓이는 중&quot;);
    }

    private void pourInCup() {
        System.out.println(&quot;컵에 따르는 중&quot;);
    }

}</code></pre>
<ul>
<li>이제 다시 만든 메소드를 prepareRecipe()에 넣어보자.</li>
</ul>
<pre><code class="language-java">public class Coffee extends CaffeineBeverage {

    @Override
    protected void brew() {
        System.out.println(&quot;필터로 커피를 우려내는 중&quot;);
    }

    @Override
    protected void addCondiments() {
        System.out.println(&quot;설탕과 우류를 추가하는 중&quot;);
    }

}

------------------------------------------------------------------

public class Tea extends CaffeineBeverage{

    @Override
    protected void brew() {
        System.out.println(&quot;필터로 커피를 우려내는 중&quot;);
    }

    @Override
    protected void addCondiments() {
        System.out.println(&quot;설탕과 우류를 추가하는 중&quot;);
    }

}</code></pre>
<ul>
<li>두 클래스에서 음료를 만드는 방법은 CaffeinBeverage에 의해 결정되므로 음료를 우려내는 brew()와 첨가물을 추가하는 addCondiments()를 수정하자.</li>
</ul>
</br>

<h2 id="추상화-과정-다시-살펴보기">추상화 과정 다시 살펴보기</h2>
<p><img src="https://velog.velcdn.com/images/ljo_0920/post/20a5dcb7-46e5-47a7-b837-8f870116d2cd/image.png" alt=""></p>
<p><a href="https://books.google.co.kr/books?id=Lw8LEAAAQBAJ&amp;printsec=frontcover&amp;hl=ko&amp;source=gbs_ge_summary_r&amp;cad=0#v=onepage&amp;q&amp;f=false">https://books.google.co.kr/books?id=Lw8LEAAAQBAJ&amp;printsec=frontcover&amp;hl=ko&amp;source=gbs_ge_summary_r&amp;cad=0#v=onepage&amp;q&amp;f=false</a></p>
<p>조금 다른 방식으로 구현해야 하는 부분이 있긴 하지만, 만드는 방법이 사실상 똑같으므로 제조법을 일반화해서 베이스 클래스에 넣었다. </p>
<p>그 후 베이스 클래스에서 전체 처리 과정을 관리하며, 첫 번째와 세 번째 단계는 직접 처리하고 두 번째와 네 번째 단계는 Tea와 coffee 서브클래스에 의존한다.</p>
</br>

<h2 id="템플릿-메소드-패턴-알아보기">템플릿 메소드 패턴 알아보기</h2>
<p>지금까지 Coffee와 Tea 클래스에 <code>템플릿 메소드</code> 패턴을 적용했다고 할 수 있다. 템플릿 메소드는 CaffeinBeverage 클래스에 들어있다.</p>
<p><img src="https://velog.velcdn.com/images/ljo_0920/post/f2dbb166-d178-4070-8cdf-42e0af25a3b6/image.png" alt=""></p>
<p><a href="https://books.google.co.kr/books?id=Lw8LEAAAQBAJ&amp;printsec=frontcover&amp;hl=ko&amp;source=gbs_ge_summary_r&amp;cad=0#v=onepage&amp;q&amp;f=false">https://books.google.co.kr/books?id=Lw8LEAAAQBAJ&amp;printsec=frontcover&amp;hl=ko&amp;source=gbs_ge_summary_r&amp;cad=0#v=onepage&amp;q&amp;f=false</a></p>
<ul>
<li>prepareRecipe()는 템플릿 메소드이다.</li>
<li>템플릿 메소드란 어떤 알고리즘의 템플릿 역할을 하는 메서드이다.</li>
<li>템플릿 내에서 알고리즘의 각 단계는 메소드로 표현한다.</li>
<li>어떤 메소드는 해당 클래스에서 처리되기도 하고, 서브 클래스에서 처리되는 메소드도 있다.</li>
<li>서브 클래스에서 구현해야 하는 메소드는 abstract로 선언해야 한다.</li>
</ul>
<p>즉 템플릿 메소드는 알고리즘의 각 단계를 정의하며, 서브 클래스에서 일부 단계를 구현할 수 있도록 유도한다.</p>
</br>

<h2 id="템플릿-메소드-패턴의-장점">템플릿 메소드 패턴의 장점</h2>
<h3 id="기존-tea--coffee-클래스">기존 Tea &amp; Coffee 클래스</h3>
<ul>
<li>각 클래스가 각각 작업을 처리한다.<ul>
<li>두 클래스에서 각자 알고리즘을 수행</li>
</ul>
</li>
<li>Coffee 와 Tea 클래스에 중복된 코드가 존재</li>
<li>알고리즘이 바뀌면 서브클래스를 일일이 열어서 여러 군데를 고쳐야 한다.</li>
<li>클래스 구조상 새로운 음료를 추가하려면 꽤 많은 일을 해야 한다.</li>
<li>알고리즘 지식과 구현 방법이 여러 클래스에 분산되어 있다.</li>
</ul>
<h3 id="템플릿-메소드를-사용한-caffeinbeverage">템플릿 메소드를 사용한 CaffeinBeverage</h3>
<ul>
<li>CaffeinBeverage 클래스에서 작업을 처리한다.<ul>
<li>알고리즘을 독점</li>
</ul>
</li>
<li>서브 클래스에서 코드를 재사용할 수 있다.</li>
<li>알고리즘이 한 군데에 모여 있으므로 한 부분만 고치면 된다.</li>
<li>다른 음료도 쉽게 추가할 수 있는 프레임워크를 제공한다.<ul>
<li>음료를 추가할 때 몇 가지 메소드만 더 만들면 된다.</li>
</ul>
</li>
<li>CaffeinBeverage 클래스에 알고리즘 지식이 집중되어 있으며 일부 구현만서브클래스에 의존한다.</li>
</ul>
</br>

<h2 id="템플릿-메소드-패턴의-정의">템플릿 메소드 패턴의 정의</h2>
<p>이제 패턴의 정의와 특징을 자세히 알아보자.</p>
<aside>
💡 템플릿 메소드 패턴은 알고리즘의 골격을 정의한다. 템플릿 메소드를 사용하면 알고리즘의 일부 단계를 서브클래스에서 구현할 수 있으며, 알고리즘의 구조는 그대로 유지하면서 알고리즘의 특정 단계를 서브클래스에서 재정의할 수도 있다.

</aside>

<p>간단하게 말하면 템플릿 메소드 패턴은 알고리즘의 템플릿(틀)을 만든다. 템플릿이란 일련의 단계로 알고리즘을 정의한 메소드이다. 여러 단계 가운데 하나 이상의 단계가 추상 메소드로 정의되며, 그 추상 메소드는 서브 클래스에서 구현된다. 이러면 서브 클래스가 일부분의 구현을 처리하게 하면서도 알고리즘의 구조는 바꾸지 않아도 된다.</p>
<h3 id="클래스-다이어그램">클래스 다이어그램</h3>
<p><img src="https://velog.velcdn.com/images/ljo_0920/post/a6d61ea1-17ed-4c9b-a510-fc48b53639b3/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/ljo_0920/post/7d27514f-d0b2-4cb7-973d-e2dc94e3167e/image.png" alt=""></p>
<p><a href="https://books.google.co.kr/books?id=Lw8LEAAAQBAJ&amp;printsec=frontcover&amp;hl=ko&amp;source=gbs_ge_summary_r&amp;cad=0#v=onepage&amp;q&amp;f=false">https://books.google.co.kr/books?id=Lw8LEAAAQBAJ&amp;printsec=frontcover&amp;hl=ko&amp;source=gbs_ge_summary_r&amp;cad=0#v=onepage&amp;q&amp;f=false</a></p>
<ul>
<li>템플릿 메소드는 알고리즘을 구현할 때 primitiveOperation을 활용한다.<ul>
<li>알고리즘은 이 단계들의 구체적인 구현으로부터 분리되어 있다.</li>
</ul>
</li>
<li>서브 클래스가 알고리즘의 각 단계를 마음대로 건드리지 못하게 final로 선언합니다.</li>
<li>추상 클래스 내에 구상 메소드로 정의된 단계도 있다. 해당 메소드는 fianl로 선언되었으므로 서브 클래스에서 오버라이드할 수 없다.<ul>
<li>이 메소드는 템플릿 메소드에서 직접 호출할 수도 있고</li>
<li>서브클래스에서 호출해서 사용할 수도 있다.</li>
</ul>
</li>
<li>기본적으로 아무것도 하지 않는 구상 메소드를 정의할 수도 있다.<ul>
<li>이런 메소드를 <code>후크(hook)</code> 라고 부른다.</li>
<li>서브 클래스에서 오버라이드할 수도 있지만, 반드시 그래야 하는건 아니다.</li>
</ul>
</li>
</ul>
</br>

<h2 id="후크-알아보기">후크 알아보기</h2>
<p>후크는 추상 클래스에서 선언되지만 기본적인 내용만 구현되어 있거나 아무 코드도 들어있지 않은 메소드이다. </p>
<p>이러면 서브 클래스는 다양한 위치에서 알고리즘에 끼어들 수 있다. 물론 그냥무시하고 넘어갈 수도 있다.  후크는 다양한 용도로 사용된다. 한 가지 사용법의 예시를 알아보자.</p>
<pre><code class="language-java">public abstract class CaffeineBeverage {

    public void prepareRecipe() {
        boilWater();
        brew();
        pourInCup();
        if (customerWantsCondiments()) {
            addCondiments();
        }
    }

    protected abstract void addCondiments();

    protected abstract void brew();

    private void boilWater() {
        System.out.println(&quot;물 끓이는 중&quot;);
    }

    private void pourInCup() {
        System.out.println(&quot;컵에 따르는 중&quot;);
    }

    private boolean customerWantsCondiments() { // hook
        return true;
    }

}</code></pre>
<ul>
<li>별 내용이 없는 기본 메소드를 구현해 놓았다.</li>
<li>해당 메소드는 true만 리턴할 뿐 다른 작업은 하지 않는다.</li>
<li>이 메소드는 서브클래스에서 필요할 때 오버라이드할 수 있는 메소드이므로 후크이다.</li>
</ul>
</br>

<h2 id="후크-활용하기">후크 활용하기</h2>
<p>후크를 사용하려면 서브 클래스에서 후크를 오버라이드해야 한다.</p>
<p>위의 예제에서는 알고리즘의 특정 부분을 처리할지 말지를 결정하는 용도로 후크를 사용했다. </p>
<p>즉, 음료에 첨가물을 추가할지 말지를 결정하는 메소드다.</p>
<pre><code class="language-java">public class CoffeeWithHook extends CaffeineBeverage { // 후크를 오버라이드해서 원하는 기능을 추가

    @Override
    protected void brew() {
        System.out.println(&quot;필터로 커피를 우려내는 중&quot;);
    }

    @Override
    protected void addCondiments() {
        System.out.println(&quot;설탕과 우류를 추가하는 중&quot;);
    }

    @Override
    protected boolean customerWantsCondiments() {
        String answer = getUserInput();
        if (answer.toLowerCase().startsWith(&quot;y&quot;)) {
            return true;
        }
        return false;
    }

    private String getUserInput() {
        String answer = null;
        System.out.println(&quot;커피에 우유와 설탕을 넣을까요? (y/n)? &quot;);
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        try {
            answer = br.readLine();
        } catch (IOException exception) {
            System.out.println(&quot;io exception&quot;);

        }
        if (answer == null) {
            return &quot;no&quot;;
        }
        return answer;
    }
}</code></pre>
</br>

<h2 id="후크-코드-테스트">후크 코드 테스트</h2>
<pre><code class="language-java">public class BeverageTestDrive {

    public static void main(String[] args) {
        CoffeeWithHook coffeeWithHook = new CoffeeWithHook();
        System.out.println(&quot;커피 준비 중&quot;);
        coffeeWithHook.prepareRecipe();
    }

}

===================================================================

커피 준비 중
물 끓이는 중
필터로 커피를 우려내는 중
컵에 따르는 중
커피에 우유와 설탕을 넣을까요? (y/n)? 
y
설탕과 우류를 추가하는 중</code></pre>
<h3 id="q-템플릿을-만들-때-추상-메소드를-써야할-때와-후크를-써야할-때를-어떻게-구분할-수-있을까">Q) 템플릿을 만들 때 추상 메소드를 써야할 때와 후크를 써야할 때를 어떻게 구분할 수 있을까??</h3>
<p>서브 클래스가 알고리즘의 특정 단계를 제공해야만 한다면 추상 메소드를 써야 한다. 알고리즘의 특정 단계가 선택적으로 적용된다면 후크를 쓰면 된다. 후크를 쓰면 서브클래스에서 필요할 때 후크를 구현할 수도 있지만, 꼭 구현해야 하는건 아니기 때문이다.</p>
<h3 id="q-후크는-정확하게-어떤-용도로-쓰이는-걸까">Q) 후크는 정확하게 어떤 용도로 쓰이는 걸까??</h3>
<p>여러 가지 용도로 쓰인다. 알고리즘에서 필수적이지 않은 부분을 서브클래스에서 구현하도록 만들고 싶을 때 후크를 쓸 수 있다. 또한 템플릿 메소드에서 앞으로 일어날 일이나 막 일어난 일에 서브클래스가 반응할 수 있도록 기회를 제공하는 용도로도 쓰일 수 있다. </p>
<p>예를 들면, 내부적으로 특정 목록을 재정렬한 후에 서브 클래스에서 특정 작업을 수행하도록 싶을 때, justReOrderedList() 같은 이름을 가진 후크 메소드를 쓸 수도 있다. 또한 앞에 예제에서 봤듯이 서브클래스가 추상 클래스에서 진행되는 작업을 처리할지 말지 결정하게 하는 기능을 부여하는 용도로 후크를 쓸 수도 있다.</p>
<h3 id="q-서브클래스에서-abstractclass에-있는-모든-추상-메소드를-구현해야-할까">Q) 서브클래스에서 AbstractClass에 있는 모든 추상 메소드를 구현해야 할까??</h3>
<p>그렇다. 모든 서브클래스에서 모든 추상 메소드를 정의해야 한다.</p>
<p>즉, 템플릿 메소드에 있는알고리즘의 단계 중에서 정의되지 않은 부분을 모두 채워 줘야 한다.</p>
<h3 id="q-추상-메소드가-너무-많아지면-서브-클래스에서-일일이-추상-메소드를-구현해야-하니까-별로-좋지-않을-수-있지-않을까">Q) 추상 메소드가 너무 많아지면 서브 클래스에서 일일이 추상 메소드를 구현해야 하니까 별로 좋지 않을 수 있지 않을까??</h3>
<p>맞다. 템플릿 메소드를 만들 때는 그 점을 꼭 생각해 봐야 한다.</p>
<p>알고리즘의 단계를 너무 잘게 쪼개지 않는 것도 한 가지 방법이 될 수 있다. 하지만 알고리즘을 큼직한 몇 가지 단계로만 나누어 놓으면 유연성이 떨어진다는 단점도 있으니 잘 생각해서 결정해야 한다.</p>
<p>그리고 모든 단계가 필수는 아니라는 점도 기억하자. 필수가 아닌 부분을 후크로 구현하면 그 추상 클래스의 서브 클래스를 만들 때 부담이 조금 줄어들 것이다.</p>
</br>

<h2 id="할리우드-원칙">할리우드 원칙</h2>
<p>디자인 원칙 중 할리우드 원칙이 있다. 이 원칙은 보통 다음과 같이 정의될 수 있다.</p>
<aside>
💡 먼저 연락하지 마세요. 저희가 연락드리겠습니다.

</aside>

<p>할리우드에서 배우들과 연락하는 것과 비슷하게, 슈퍼 클래스에서 모든 것을 관리하고 필요한 서브클래스를 불러서 써야 한다는  원칙이다. 이런 할리우드 원칙을 활용하면 <code>의존성 부패(dependency rot)</code>를 방지할 수 있다. </p>
<p>어떤 고수준 구성 요소가 저수준 구성 요소에 의존하고, 그 저수준 구성 요소는 다시 고수준 구성 요소에 의존하고, 그 고수준 구성 요소는 다시 또 다른 구성 요소에, 그 다른 구성 요소는 또 저수준 구성 요소에 의존하는 것과 같은 식으로 의존성이 복잡하게 꼬여있는 상황을 <code>의존성이 부패</code>했다고 부른다. 이렇게 의존성이 부패하면 시스템 디자인이 어떤 식으로 되어 있는지 아무도 알아볼 수 없다.</p>
<p>할리우드 원칙을사용하면, 저수준 구성 요소가 시스템에 접속할 수는 있지만 언제, 어떻게 그 구성 요소를 사용할지는 고수준 구성 요소가 결정한다.</p>
<p>즉 고수준 구성 요소가 저수준 구성 요소에게 “먼저 연락하지 마세요. 제가 먼저 연락 드리겠습니다.” 라고 이야기 하는 것과 같다.</p>
<p><img src="https://velog.velcdn.com/images/ljo_0920/post/48a40437-f907-49fa-af2b-856bc9c78fa8/image.png" alt=""></p>
<p><a href="https://books.google.co.kr/books?id=Lw8LEAAAQBAJ&amp;printsec=frontcover&amp;hl=ko&amp;source=gbs_ge_summary_r&amp;cad=0#v=onepage&amp;q&amp;f=false">https://books.google.co.kr/books?id=Lw8LEAAAQBAJ&amp;printsec=frontcover&amp;hl=ko&amp;source=gbs_ge_summary_r&amp;cad=0#v=onepage&amp;q&amp;f=false</a></p>
<ul>
<li>저수준 구성 요소도 컴퓨테이션에 참여할 수 있다.</li>
<li>하지만 언제, 어떻게 쓰일지는 고수준 구성 요소가 결정한다.<ul>
<li>저수준 구성 요스는 절대 고수준 구성 요소를 직접 호출할 수 없다.</li>
</ul>
</li>
</ul>
</br>

<h2 id="할리우드-원칙과-템플릿-메소드--패턴">할리우드 원칙과 템플릿 메소드  패턴</h2>
<p>할리우드 원칙과 템플릿 메소드 패턴의 관계는 쉽게 알 수 있다. 템플릿 메소드 패턴을 써서 디자인 하면 서브클래스에게 “우리가 연락할 테니까 먼저 연락하지마”라고 얘기하는 구조이기 대문이다. </p>
<p>디자인을 다시 한번 살펴보자</p>
<p><img src="https://velog.velcdn.com/images/ljo_0920/post/e72a08f3-b6d6-4d96-83f2-2fae8bc876f9/image.png" alt=""></p>
<p><a href="https://books.google.co.kr/books?id=Lw8LEAAAQBAJ&amp;printsec=frontcover&amp;hl=ko&amp;source=gbs_ge_summary_r&amp;cad=0#v=onepage&amp;q&amp;f=false">https://books.google.co.kr/books?id=Lw8LEAAAQBAJ&amp;printsec=frontcover&amp;hl=ko&amp;source=gbs_ge_summary_r&amp;cad=0#v=onepage&amp;q&amp;f=false</a></p>
<ul>
<li><code>CaffeineBeverage</code>  는 고수준 구성 요소이다.<ul>
<li>음료를 만드는 방법에 해당하는 알고리즘을 장악하고 있고, 메소드 구현이 필요한 상황에만 서브클래스를 불러낸다.</li>
</ul>
</li>
<li><code>CaffeineBeverage</code> 클래스의 클라이언트는 Tea, Coffee 같은 구상 클래스가 아닌 <code>CaffeineBeverage</code> 에 추상화되어 있는 부분에 의존한다. 이러면 전체 시스템의 의존성을 줄일 수 있다.</li>
<li>서브 클래스는 자질 구레한 메소드 구현을 제공하는 용도로만 쓰인다.</li>
<li>Tea와 Coffee는 호출 <code>당하기</code> 전까지는 추상 클래스를 직접 호출하지 않는다.</li>
</ul>
<h3 id="q-할리우드-원칙과-의존성-뒤집기-원칙은-어떤-관계일까">Q) 할리우드 원칙과 의존성 뒤집기 원칙은 어떤 관계일까??</h3>
<p>의존성 뒤집기 원칙은 될 수 있으면 구상 클래스 사용을 줄이고 추상화된 것을 사용해야 한다는 원칙이다. 할리우드 원칙은 저수준 구성 요소가 컴퓨테이션에 참여하면서도 저수준 구성 요소와 고수준 계층 간 의존을 없애도록 프레임워크는 구성 요소를 구축하는 기법이다.</p>
<p>따라서 두 원칙은 객체를 분리한다는 하나의 목표를 공유하지만, 의존성을 피하는 방법에 있어서 의존성 뒤집기 원칙이 훨씬더 강하고 일반적인 내용을 담고 있다.</p>
<p>할리우드 원칙은 저수준 구성요소를 다양하게 사용할수 있으면서도 다른 클래스가 구성 요소에 너무 의존하지 않게 만들어주는 디자인 구현 기법을 제공한다.</p>
<h3 id="q-저수준-구성-요소에서는-고수준-구성-요소에-있는-메소드를-호출할-수-없는-것일까">Q) 저수준 구성 요소에서는 고수준 구성 요소에 있는 메소드를 호출할 수 없는 것일까??</h3>
<p>그렇지 않다. 사실 저수준 구성 요소에서도 상속 계층구조 위에 있는 클래스가 정의한 메소드를, 상속으로 호출하는 경우도 빈번하게 있다. 하지만 저수준 구성 요소와 고수준 구성 요소 사이에 순환 의존성이 생기지 않도록 해야한다.</p>
</br>

<h2 id="자바-api-속-템플릿-메서드-패턴-알아보기">자바 api 속 템플릿 메서드 패턴 알아보기</h2>
<p>바로 Arrays.sort 메소드이다. </p>
<pre><code class="language-java">private static void legacyMergeSort(Object[] a) {
    Object[] aux = a.clone();
    mergeSort(aux, a, 0, a.length, 0);
}

private static void mergeSort( // 템플릿 메소드
        Object[] src,
    Object[] dest,
    int low,
    int high,
    int off
    ) {
      // Insertion sort on smallest arrays
      if (length &lt; INSERTIONSORT_THRESHOLD) {
          for (int i=low; i&lt;high; i++)
              for (int j=i; j&gt;low &amp;&amp;
                       ((Comparable) dest[j-1]).compareTo(dest[j])&gt;0; j--) // 템플릿 메소드를 완성하려면 comapreTo() 메소드를 구현해야 한다.
                  swap(dest, j, j-1); // Arrays 클래스에 이미 정의되어 있는 구상 ㅇ메솓,
          return;
      }
        // ...

}</code></pre>
<p>만약 배열 속 오리 클래스들을 정렬해야 한다면 Arrays에 있는 정렬용 템플릿 메소드에서 알고리즘을 제공하지만, 오리 비교 방법은 comapreTo() 메소드로 구현해야 한다.</p>
<p>하지만 템플릿 메소드 패턴을 배울 때 서브 클래스에서 일부 단계를 구현한다고 배웠는데, 해당 예제에서는 서브 클래스를 만들지 않고 있다. </p>
<p>sort() 메소드는 정적 메소드이고 정적 메소드 자체는 크게 문제가 되지 않는다. 슈퍼 클래스에 들어있다고 생각하면 되기 때문이다. 하지만 sort() 자체가 특정 슈퍼클래스에 정의되어 있는게 아니므로 sort() 메소드가 우리가 comapreTo() 메소드를 구현했는지 알아낼 수 있는 방법이 필요하다는 점이다.</p>
<p>이러한 문제를 해결하기 위해 Comaprable 인터페이스가 도입되었다. 이제 해당 인터페이스를 구현하기만 하면 문제가 해결 된다.</p>
<pre><code class="language-java">public class Duck implements Comparable&lt;Duck&gt; {

    private final String name;
    private final int weight;

    public Duck(String name, int weight) {
        this.name = name;
        this.weight = weight;
    }

    public String getName() {
        return name;
    }

    public int getWeight() {
        return weight;
    }

    @Override
    public int compareTo(Duck otherDuck) {
        return Integer.compare(this.weight, otherDuck.getWeight());
    }
}</code></pre>
<h3 id="q-오리-정렬-코드가-정말-템플릿-메소드-패턴일까-억지스러운-것일까">Q) 오리 정렬 코드가 정말 템플릿 메소드 패턴일까? 억지스러운 것일까?</h3>
<p>Arrays.sort() 메소드는 분명 템플릿 메소드 패턴의 정의의 방법을 사용하지 않고 있지만 실전에서 패턴을 적용하는 방법이 책에 나와 있는 방법과 완전히 같을 수는 없다. 주어진 상황과 구현ㄴ상 제약조건에 맞게 고쳐서 적용해야 한다.</p>
<p>일반적으로 자바에서는 배열의 서브클래스를 만들 수 없지만, 어떤 배열에서도 정렬 기능을 사용할 수 있도록 만들어야 했다. 그래서 sort() 메소드를 정적 메소드로 정의한 다음, 대소를 비교하는 부분은 정렬될 객체에서 구현되도록 만든 것이다.</p>
<p>온전한 템플릿 메소드라고 할 순 없겠지만 템플릿 메소드 패턴의 기본 정신을 충실히 따르고 있다. 또한 서브클래스를 만들어야 한다는 제약 조건을 없앰으로써 오히려 더 유연하면서 유용한 정렬 메소드를 만들었다.</p>
<h3 id="q-구현해놓은-것을-보니-템플릿-메소드-패턴-보다는-전략-패턴과-가까워-보이는데-템플릿-메소드-패턴이라고-볼-수-있는-근거는-무엇일까">Q) 구현해놓은 것을 보니 템플릿 메소드 패턴 보다는 전략 패턴과 가까워 보이는데 템플릿 메소드 패턴이라고 볼 수 있는 근거는 무엇일까?</h3>
<p>전략 패턴에서 객체 구성을 사용하니까 어떻게 보면 일리가 있지만 전략 패턴에서는 구성할 때 사용하는 클래스에서 알고리즘을 완전히 구현한다.</p>
<p>Arrays 클래스에서 사용하는 알고리즘은 불완전한다. comapreTo() 를 다른 클래스에서 제공해 줘야 하기 때문이다. 따라서 템플릿 메소드 패턴이 적용되었다고 볼 수 있다.</p>
</br>

<h2 id="개념이-비슷해-보이는-패턴">개념이 비슷해 보이는 패턴</h2>
<ul>
<li>템플릿 메소드 패턴 : 알고리즘의 어떤 단계를 구현하는 방법을 서브클래스에서 결정</li>
<li>전략 패턴 : 바꿔 쓸 수 있는 행동을 캡슐화하고, 어떤 행동을 사용할지는 서브클래스에 맡김</li>
<li>팩토리 메소드 패턴 : 구상 클래스의 인스턴스 생성을 서브클래스에서 결정</li>
</ul>
<h3 id="템플릿-메소드-vs-전략-패턴">템플릿 메소드 vs 전략 패턴</h3>
<p>두 가지 모두 같은 요구사항을 구현할 수 있지만 템플릿 메소드 패턴은 알고리즘의 개요를 정의하는 역할을 한다. 진짜 작업 중 일부는 서브클래스에서 처리하며 각 단계마다 다른 구현을 사용하면서도 알고리즘 구조 자체는 그대로 유지할 수 있다. 따라서 알고리즘을 더 강하게 제어할 수 있고, 코드 중복도 거의 없다. 만약 알고리즘이 전부 똑같고 코드 한 줄씩만 다르다면 템플릿 메서드 패턴을 사용한 클래스가 전략 패턴을 사용한 클래스보다 효율적일 수 있다.</p>
<p>하지만 전략 패턴은 상속이 아닌 객체 구성을 사용하기 때문에 상속에서 오는 단점들이 없고  훨씬 더 유연하다는 장점이 있다.  부모 같이 어떤 것에도 의존하지 않고 알고리즘을 전부 알아서 구현할 수 있기 때문이다.</p>
</br>

<h2 id="핵심-정리">핵심 정리</h2>
<ul>
<li>템플릿 메소드는 알고리즘의 단계를 정의하며 일부 단계를 서브클래스에서 구현하도록 할 수 있다.</li>
<li>템플릿 메소드 패턴은 코드 재사용에 큰 도움이 된다.</li>
<li>템플릿 메소드가 들어있는 추상 클래스는 구상 메소드, 추상 메소드, 후크를 정의할 수 있다.</li>
<li>추상 메소드는 서브클래스에서 구현한다.</li>
<li>후크는 추상 클래스에 들어있는 메소드로 아무 일도 하지 않거나 기본 행동만을 정의한다.<ul>
<li>서브 클래스에서 후크를 오버라이드 할 수 있다.</li>
</ul>
</li>
<li>할리우드 원칙에 의하면, 저수준 모듈을 언제 어떻게 호출할지는 고수준 모듈에서 결정하는 것이 좋다.</li>
<li>템플릿 메소드 패턴은실전에서도 꽤 자주 쓰이지만 반드시 교과서적인 방식으로 적용되진 않는다.</li>
<li>전략 패턴과 템플릿 메소드 패턴은 모두 알고리즘을 캡슐화하는 패턴이지만 전략 패턴은 상속을, 템플릿 메소드 패턴은 구성을 사용합니다.</li>
<li>팩토리 메소드 패턴은 특화된 템플릿 메소드 패턴입니다.</li>
</ul>
</br>

<h2 id="객체지향-도구-상자">객체지향 도구 상자</h2>
<ul>
<li>객체지향의 기초(4요소)<ul>
<li>캡슐화</li>
<li>상속</li>
<li>추상화</li>
<li>다형성</li>
</ul>
</li>
<li>객체지향 원칙<ul>
<li>바뀌는 부분을 캡슐화한다.</li>
<li>상속보다는 구성을 활용한다.</li>
<li>구현이 아닌 인터페이스(super type)에 맞춰서 프로그래밍한다.</li>
<li>서로 상호작용을 하는 객체 사이에서는 가능하면 느슨하게 결합하는 디자인을 사용해야 한다.</li>
<li>클래스는 확장에 대해서는 열려 있지만 변경에 대해서는 닫혀 있어야 한다. (OCP)</li>
<li>추상화된 것에 의존하라. 구상 클래스에 의존하지 않도록 한다.</li>
<li>진짜 절친에게만 이야기해야 한다.</li>
<li><strong>먼저 연락하지 마세요. 저희가 연락 드리겠습니다.</strong></li>
</ul>
</li>
<li>객체지향 패턴<ul>
<li>스트레지티 패턴 : 알고리즘군을 정의하고 각각의 알고리즘을 정의하고 각각을 캡슐화하여 교환해서 사용할 수 있도록 만든다. 전략을 사용하면 알고리즘을 사용하는 클라이언트와는 독립적으로 알고리즘을 변경할 수 있다.</li>
<li>옵저버 패턴 : 한 객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체들한테 연락이 가고 자동으로 내용이 갱신되는 방식으로 일대다 의존성을 정의한다.</li>
<li>데코레이터 패턴: 객체에 추가 요소를 동적으로 더할 수 있습니다. 데코레이터를 사용하면 서브 클래스를 만드는 경우에 비해 훨씬 유연하게 기능을 확장할 수 있습니다.</li>
<li>추상 팩토리 패턴 : 서로 연관된, 또는 의존적인 객체들로 이루어진 제품군을 생성하기 위한 인터페이스를 제공한다. 구상 클래스는 서브 클래스에 의해 만들어진다.</li>
<li>팩토리 메소드 패턴 : 객체를 생성하기 위한 인터페이스를 만든다. 어떤 클래스의 인스턴스를 만들지는 서브클래스에서 결정하도록 한다. 팩토리 메소드를 이용하면 인스턴스를 만드는 일을 서브클래스로 미룰 수 있다.</li>
<li>싱글턴 패턴 : 클래스 인스턴스가 하나만 만들어지도록 하고, 그 인스턴스에 대한 전역 접근을 제공한다.</li>
<li>커맨드 패턴 : 요청 내역을 객체로 캡슐화해서 객체를 서로 다른 요청 내역에 따라 매개변수화할 수 있다. 이러면 요청을 큐에 저장하거나 로그로 기록하거나 작업 취소 기능을 사용할 수 있다.</li>
<li>어댑터 패턴 : 특정 클래스 인터페이스를 클라이언트에서 요구하는 다른 인터페이스로 변환한다. 인터페이스가 호환되지 않아 같이 쓸 수 없었던 클래스를 사용할 수 있게 도와준다.</li>
<li>퍼사드 패턴 : 서브시스템에 있는 일련의 인터페이스를 통합 인터페이스로 묶어 준다. 또한 고수준 인터페이스도 정의하므로 서브시스템을 더 편리하게 사용할 수 있다.</li>
<li><strong>템플릿 메소드 패턴 : 알고리즘의 골격을 정의한다. 템플릿 메소드를 사용하면 알고리즘의 일부 단계를 서브클래스에서 구현할 수 있으며, 알고리즘의 구조는 그대로 유지하면서 알고리즘의 특정 단계를 서브클래스에서 재정의할 수도 있다.</strong></li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[디자인 패턴 - 어댑터, 퍼사드]]></title>
            <link>https://velog.io/@ljo_0920/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-%EC%96%B4%EB%8C%91%ED%84%B0-%ED%8D%BC%EC%82%AC%EB%93%9C</link>
            <guid>https://velog.io/@ljo_0920/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-%EC%96%B4%EB%8C%91%ED%84%B0-%ED%8D%BC%EC%82%AC%EB%93%9C</guid>
            <pubDate>Fri, 29 Jul 2022 05:27:54 GMT</pubDate>
            <description><![CDATA[<h1 id="어댑터-패턴--퍼사드-패턴">어댑터 패턴 &amp; 퍼사드 패턴</h1>
<p><a href="https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=582754">헤드 퍼스트 디자인 패턴</a>을 읽고 정리한 글입니다.</p>
<blockquote>
<p>어댑터 패턴 : 특정 클래스 인터페이스를 클라이언트에서 요구하는 다른 인터페이스로 변환한다. 인터페이스가 호환되지 않아 같이 쓸 수 없었던 클래스를 사용할 수 있게 도와준다.</p>
</blockquote>
<blockquote>
<p>퍼사드 패턴 : 서브시스템에 있는 일련의 인터페이스를 통합 인터페이스로 묶어 준다. 또한 고수준 인터페이스도 정의하므로 서브시스템을 더 편리하게 사용할 수 있다.</p>
</blockquote>
</br>

<h2 id="개요---어댑터">개요 - 어댑터</h2>
<p><img src="https://velog.velcdn.com/images/ljo_0920/post/5a79dd00-2998-4f41-8243-a7517c8a2d75/image.png" alt=""></p>
<p><a href="https://secretroute.tistory.com/entry/Head-First-Design-Patterns-%EC%A0%9C7%EA%B0%95-Adapter-%ED%8C%A8%ED%84%B4%EA%B3%BC-Facade-%ED%8C%A8%ED%84%B4">https://secretroute.tistory.com/entry/Head-First-Design-Patterns-제7강-Adapter-패턴과-Facade-패턴</a></p>
<p>우리 주변에서 볼 수 있는 어댑터의 역할은 전원 소켓의 인터페이스를 플러그에서 필요로 하는 인터페이스로 바꿔 준다고 할 수 있다. 객체지향 어댑터도 똑같이 어떤 인터페이스를 클라이언트에서 요구하는 형태로 적응시키는 역할을 한다.</p>
</br>

<h2 id="객체지향-어댑터">객체지향 어댑터</h2>
<p>어떤 소프트웨어 시스템에서 새로운 업체에서 제공한 클래스 라이브러리를 사용해야 하는데 그 업체에서 사용하는 인터페이스가 기존에 사용하던 인터페이스와 다르다고 가정해 보자.</p>
<p><img src="https://velog.velcdn.com/images/ljo_0920/post/b8b0464e-6968-4031-8e0f-2df04cbcb404/image.png" alt=""></p>
<p><a href="https://codingsmu.tistory.com/59">https://codingsmu.tistory.com/59</a></p>
<p>그런데 기존 코드를 바꿔서 이 문제를 해결할 수 없는 상황이고, 업체에서 공급받은 클래스도 변경할 수 없다면 어떻게 해야 할까?? 바로 새로운 업체에서 사용하는 인터페이스를 기존에 사용하던 인터페이스에 적응시켜 주는 클래스를 만들면 된다.</p>
<p><img src="https://velog.velcdn.com/images/ljo_0920/post/80db3426-9d0e-4f6a-a606-092977e9ea24/image.png" alt=""></p>
<p><a href="https://secretroute.tistory.com/entry/Head-First-Design-Patterns-%EC%A0%9C7%EA%B0%95-Adapter-%ED%8C%A8%ED%84%B4%EA%B3%BC-Facade-%ED%8C%A8%ED%84%B4">https://secretroute.tistory.com/entry/Head-First-Design-Patterns-제7강-Adapter-패턴과-Facade-패턴</a></p>
<p>어댑터는 기존 시스템에서 사용하던 인터페이스를 구현해서 새로운 업체에서 제공한 클래스에 요구 내역을 전달할 수 있다. 어댑터는 클라이언트로부터 요청을 받아서 새로운 업체에서 제공하는 클래스를 클라이언트가 받아들일 수 있는 형태의 요청으로 변환해 주는 중개인 역할을 하는 것이다.</p>
</br>

<h2 id="어댑터-사용방법">어댑터 사용방법</h2>
<p>어댑터를 어떻게 사용하는지 한번 살펴보자</p>
<pre><code class="language-java">public interface Duck {

    void quack();
    void fly();

}

public class MallardDuck implements Duck {

    @Override
    public void quack() {
        System.out.println(&quot;quack&quot;);
    }

    @Override
    public void fly() {
        System.out.println(&quot;fly&quot;);
    }
}</code></pre>
<pre><code class="language-java">public interface Turkey {

    void gobble();
    void fly();

}

public class WildTurkey implements Turkey {

    @Override
    public void gobble() {
        System.out.println(&quot;gobble&quot;);
    }

    @Override
    public void fly() {
        System.out.println(&quot;short fly&quot;);
    }
}</code></pre>
<p>Duck 객체가 모자라서 Turkey 객체를 대신 사용해야 하는 상황이라고 가정해 보자. 물론 인터페이스가 다르기에 Turkey 객체를 바로 사용할 수는 없다. 이 때 필요한 것이 어댑터이다.</p>
<pre><code class="language-java">// 우선 적응시킬 형식의 인터페이스를 구현해야 한다. 즉 클라이언트에서 원하는 인터페이스를 구현해야 한다.
public class TurkeyAdapter implements Duck { 


    private final Turkey turkey;

    public TurkeyAdapter(Turkey turkey) { // 그리고 기존 형식 객체의 레퍼런스가 필요한다.
        this.turkey = turkey;
    }

    @Override
    public void quack() {
        turkey.gobble();
    }

    /*
     두 인터페이스에 모두 fly가 있지만 turkey의 fly() 메소드를 Duck의 fly() 메소드에 대응시키도록 작성 
     */
    @Override
    public void fly() {
        for (int i = 0; i &lt; 5; i++) {
            turkey.fly();
        }
    }
}</code></pre>
<pre><code class="language-java">public class DuckTestDrive {

    public static void main(String[] args) {
        Duck duck = new MallardDuck();
        Turkey turkey  = new WildTurkey();
        Duck turkeyAdapter = new TurkeyAdapter(turkey);

        System.out.println(&quot; turkey said that&quot;);
        turkey.gobble();
        turkey.fly();

        System.out.println(&quot;\n duck said that&quot;);
        testDuck(duck);

        System.out.println(&quot;\n turkeyAdapter said that&quot;);
        testDuck(turkeyAdapter);
    }

    private static void testDuck(Duck duck) {
        duck.quack();
        duck.fly();
    }

}

=======================================================
turkey said that
gobble
short fly

duck said that
quack
fly

turkeyAdapter said that
gobble
short fly
short fly
short fly
short fly
short fly</code></pre>
<ul>
<li>클라이언트의 testDuck() 메소드는  오리와 칠면조를 전혀 구분하지 못한다.</li>
</ul>
</br>

<h2 id="어댑터-패턴">어댑터 패턴</h2>
<p><img src="https://velog.velcdn.com/images/ljo_0920/post/f9df63d7-0e41-4e79-bf38-ff984f26aaa2/image.png" alt=""></p>
<p><a href="https://codingsmu.tistory.com/59">https://codingsmu.tistory.com/59</a></p>
<p>이제 어댑터가 어떤 식으로 작동하는지 살펴보자</p>
<ol>
<li>클라이언트는 타깃 인터페이스에 맞게 구현되어 있으며, 타깃 인터페이스로 메소드를 호출해서 어댑터에 요청을 보낸다.</li>
<li>어댑터는 타깃 인터페이스를 구현하며, 어댑티 인스턴스를 가지고 있다. 어댑터는 어댑티 인터페이스로 그 요청을 어댑티에 관한(하나 이상의) 메소드 호출로 변환한다.</li>
<li>클라이언트는 호출 결과를 받긴 하지만 중간에 어댑터가 있다는 사실을 모르므로, 클라이언트와 어댑티는 서로 분리되어 있다.</li>
</ol>
<h3 id="q-어댑터가-얼마나-적응시켜-줘야-할까-대형-타깃-인터페이스를-구현해야-한다면-할-일이-정말-많아지지-않을까">Q. 어댑터가 얼마나 적응시켜 줘야 할까? 대형 타깃 인터페이스를 구현해야 한다면 할 일이 정말 많아지지 않을까?</h3>
<p>어댑터 구현은 타깃 인터페이스로 지원해야 하는 인터페이스의 크기에 비례해서 복잡해진다. 하지만 다른 대안이 없다. 클라이언트에서 호출하는 부분을 새로운 인터페이스에 맞춰서 고치려면 정말 많은 부분을 고려해야 하고, 코드도 많이 고쳐야 한다. 이런 방법보다는 모든 변경 사항을 캡슐화할 어댑터 클래스 하나만 제공하는 방법이 더 나을 것이다.</p>
<h3 id="q-하나의-어댑터는-하나의-클래스만-감싸야-할까">Q. 하나의 어댑터는 하나의 클래스만 감싸야 할까?</h3>
<p>어댑터 패턴은 하나의 인터페이스를 다른 인터페이스로 변환하는 용도로 쓰인다. 하나의 어댑터에서 타깃 인터페이스를 구현하려고 2개 이상의 어댑티를 감싸야 하는 상황도 생길 수 있다. 사실 이런 내용은 퍼사드 패턴과 관련이 있으므로 퍼사드 패턴 때 다시 보자.</p>
<h3 id="q-시스템에-오래된-부분과-새로-만든-부분이-섞여-있으면-어떻게-해야할까-어떤-곳에는-어댑터를-사용하고-다른-곳에서-어댑터로-감싸지-않은-인터페이스를-사용하면-헷갈리지-않을까">Q. 시스템에 오래된 부분과 새로 만든 부분이 섞여 있으면 어떻게 해야할까?? 어떤 곳에는 어댑터를 사용하고 다른 곳에서 어댑터로 감싸지 않은 인터페이스를 사용하면 헷갈리지 않을까?</h3>
<p>이런 상황에서는 두 인터페이스를 모두 지원하는 다중 어댑터(Two Way Adapter)를 만들면 된다. 다중 어댑터로 필요한 인터페이스를 둘 다 구현해서 어댑터가 기존 인터페이스와 새로운 인터페이스 역할을 할 수 있게 하면 된다.</p>
</br>

<h2 id="어댑터-패턴의-정의">어댑터 패턴의 정의</h2>
<p>이제 어댑터 패턴의 정의를 알아보자. </p>
<aside>
💡 어댑터 패턴은 특정 클래스 인터페이스를 클라이언트에서 요구하는 다른 인터페이스로 변환한다. 인터페이스가 호환되지 않아 같이 쓸 수 없었던 클래스를 사용할 수 있게 도와준다.

</aside>

<p>이 패턴을 사용하면 호환되지 않는 인터페이스를 사용하는 클라이언트를 그대로 활용할 수 있다. 인터페이스를 변환해 주는 어댑터를 만들면 되기 때문이다. 이러면 클라이언트와 구현된 인터페이스를 분리할 수 있으며, 변경 내역이 어댑터에 캡슐화되기에 나중에 인터페이스가 바뀌더라도 클라이언트를 바꿀 필요가 없다.</p>
<h3 id="클래스-다이어그램">클래스 다이어그램</h3>
<p><img src="https://velog.velcdn.com/images/ljo_0920/post/d3bd8386-d9c3-4900-bc21-135a5af96f47/image.png" alt=""></p>
<p><a href="https://secretroute.tistory.com/entry/Head-First-Design-Patterns-%EC%A0%9C7%EA%B0%95-Adapter-%ED%8C%A8%ED%84%B4%EA%B3%BC-Facade-%ED%8C%A8%ED%84%B4">https://secretroute.tistory.com/entry/Head-First-Design-Patterns-제7강-Adapter-패턴과-Facade-패턴</a></p>
<p>클라이언트</p>
<ul>
<li>클라이언트는 타킷 인터페이스만 볼 수 있다.</li>
</ul>
<p>어댑터</p>
<ul>
<li>어댑터에서 타깃 인터페이스를 구현한다.</li>
<li>어댑터는 어댑티로 구성되어 있다.</li>
</ul>
<p>어댑티</p>
<ul>
<li>모든 요청은 어댑티에 위임된다.</li>
</ul>
<p>어댑터 패턴은 여러 객체지향 원칙을 반영하고 있다. 어댑티를 새로 바뀐 인터페이스로 감쌀 때는 Composition을 사용한다. 이런 접근번은 어댑티의 모든 서브클래스에 어댑터를 쓸 수 있다는 장점이 있다.</p>
<p>그리고 어댑터 패턴은 클라이언트를 특정 구현이 아닌 인터페이스에 연결한다. 서로 다른 백엔드 클래스로 변환시키는 여러 어댑터를 사용할 수도 있다. 이렇게 인터페이스를 기준으로 코딩했기에 타깃 인터페이스만 제대로 유지한다면 나중에 다른 구현을 추가하는 것도 가능하다.</p>
</br>

<h2 id="객체-어댑터와-클래스-어댑터">객체 어댑터와 클래스 어댑터</h2>
<p>사실 어댑터에는 두 종류가 있다. 하나는 객체 어댑터, 다른 하나는 클래스 어댑터이다. </p>
<p>지금까지 본 예제와 다이어그램 모두 객체 어댑터에 해당하는 내용들이다. 그렇다면 클래스 어댑터란 무엇이고 왜 살펴보지 않았을까? 클래스 어댑터 패턴을 쓰려면 상속이 필요한데 자바에서는 다중 상속이 불가능하므로 자바에서는 불가능하다. 하지만 다중 상속이 가능한 언러를 사용하다 보면 클래스 어댑터를 써야 할 때도 있으니 클래스 다이어그램을 살펴보자</p>
<p><img src="https://velog.velcdn.com/images/ljo_0920/post/57a33a2e-b59e-42c1-ab7f-9165f40f5448/image.png" alt=""></p>
<p><a href="https://secretroute.tistory.com/entry/Head-First-Design-Patterns-%EC%A0%9C7%EA%B0%95-Adapter-%ED%8C%A8%ED%84%B4%EA%B3%BC-Facade-%ED%8C%A8%ED%84%B4">https://secretroute.tistory.com/entry/Head-First-Design-Patterns-제7강-Adapter-패턴과-Facade-패턴</a></p>
<p>어댑티를 적응시킬때 구성을 사용하는 대신, 어댑터를 어댑티와 타깃 클래스의 서브클래스로 만든다. 상속을 사용하는 클래스 어댑터에 비해 객체 어댑터는 composition을 사용하므로 상속을 통한 코드 분량을 줄이지는 못하지만, 어댑티한테 필요한 일을 시키는 코드만 작성하면 되기 때문에 작성해야할 코드가 적고 유연성을 확보할 수 있다.</p>
</br>

<h2 id="어댑터-패턴-실전-적용">어댑터 패턴 실전 적용</h2>
<p><img src="https://velog.velcdn.com/images/ljo_0920/post/d6c62095-1905-4198-b0a8-21f04a84a656/image.png" alt=""></p>
<p><a href="https://swk3169.tistory.com/255">https://swk3169.tistory.com/255</a></p>
<h3 id="enumeration">Enumeration</h3>
<p>Enumeration을 리턴하는 elements() 메소드가 구현되어 있었던, 초기 컬렉션 형식(Vector, Stack, Hashtable 등)은 Enumeration 인터페이스를 이용하면 컬렉션 내에서 각 항목이 관리되는 방식에는 신경 쓸 필요 없이 컬렉션의 모든 항목에 접근이 가능하다.</p>
<h3 id="iterator">Iterator</h3>
<p>최근에는 Enumeration과 마찬가지로 컬렉션에 있는 일련의 항목들에 접근할 수 있게 해 주면서 항목을 제거할 수 도 있게 해 주는 iterator라는 인터페이스를 이용하기 시작했다.</p>
<h3 id="enumeration-vs-iterator">Enumeration vs Iterator</h3>
<p> Enumeration 인터페이스를 사용하는 구형 코드를 다뤄야 할 때도 가끔 있지만 새로운 코드를 만들 때는 Iterator만 사용하는 것이 좋다. 이때 어댑터 패턴을 적용해보자.</p>
<p><img src="https://velog.velcdn.com/images/ljo_0920/post/114ead1b-d90c-4355-bfe5-7332fe47c946/image.png" alt=""></p>
<p><a href="https://codingsmu.tistory.com/59">https://codingsmu.tistory.com/59</a></p>
<ul>
<li>Itrerator : 타깃 인터페이스</li>
<li>Enumeration : 어댑티 인터페이스</li>
<li>그런데 Iterator의 remove() 메소드는 Enumeration에는 이런 기능을 제공하는 메소드가 없다.<ul>
<li>어떻게 해야할까??</li>
</ul>
</li>
</ul>
<h3 id="어댑터-디자인하기">어댑터 디자인하기</h3>
<p>클래스 다이어그램은 다음과 같다. 먼저 타깃 인터페이스를 구현하고, 어댑티 객체로 구성된 어댑터를 구현해야 한다. hasNext()와 next() 메소드는 타깃에서 업대티로 바로 연결된다.</p>
<p><img src="https://velog.velcdn.com/images/ljo_0920/post/f37df0c1-7611-4865-b90c-873285b1ab43/image.png" alt=""></p>
<p><a href="https://codingsmu.tistory.com/59">https://codingsmu.tistory.com/59</a></p>
<p>remove()는 어떻게 처리할까?</p>
<ul>
<li>어댑터 차원에서 완벽하게 작동하는 remove() 메소드의 구현 방법은 없다. 따라서 그나마 좋은 방법은 런타임 예외를 던지는 것이다.</li>
<li>Iterator 인터페이스는 remove()는 default method로 UnsupportedOperationException을 던지고 있다.</li>
</ul>
<p>이처럼 메소드가 일대일로 대응되지 않는 상황에서는 어댑터를 완벽하게 적용할 수 없다. 클라이언트는 예외 발생 가능성을 염두에 두고 있어야 하기 때문이다. 하지만 클라이언트에서 주의를 기울이고, 어댑터 문서를 잘 만들어 두면 괜찮을 것이다.</p>
<h3 id="enumerationiteraotr">EnumerationIteraotr</h3>
<pre><code class="language-java">public class EnumerationIterator implements Iterator&lt;Object&gt; {

    private final Enumeration&lt;?&gt; enumeration;

    public EnumerationIterator(Enumeration&lt;?&gt; enumeration) {
        this.enumeration = enumeration;
    }

    @Override
    public boolean hasNext() {
        return enumeration.hasMoreElements();
    }

    @Override
    public Object next() {
        return enumeration.nextElement();
    }

    @Override
    public void remove() {
        throw new UnsupportedOperationException();
    }
}</code></pre>
</br>

<h2 id="퍼사드-패턴">퍼사드 패턴</h2>
<p>지금까지 어댑터 패턴을 써서 어떤 클래스의 인터페이스를 클라이언트가 원하는 인터페이스로 변환하는 방법을 어댑터 패턴을 이용하여 구현했다.</p>
<p>이제 조금 다른 이유로 인터페이스를 변경하는 또 다른 패턴을 알아보자. 바로 퍼사드 패턴이다. </p>
<p>퍼사드 패턴은 인터페이스를 단순하게 바꾸려고 인터페이스를 변경하다. 하나 이상의 클래스 인터페이스를 깔끔하면서도 효과적인 퍼사드(겉모양, 외관)으로 덮어주기 때문이다.</p>
<h3 id="데코레이터-vs-어댑터-vs-퍼사드">데코레이터 vs 어댑터 vs 퍼사드</h3>
<p>해당 패턴 모두 객체를 감싸고 있는 공통점을 가지고 있다. 하지만 모두 사용하는 용도가 다르다.</p>
<ul>
<li>데코레이터는 인터페이스는 바꾸지 않고 감싸고 있는 객체의 행동과 책임을 확장하는 용도로 사용한다.</li>
<li>어댑터는 하나의 인터페이스를 다른 인터페이스로 변환하는 용도로 사용한다.</li>
<li>퍼사드는 인터페이스를 간단하게 변경하는 용도로 사용한다.</li>
</ul>
</br>

<h2 id="개요---홈시어터-만들기">개요 - 홈시어터 만들기</h2>
<p>패턴을 알아보기 전에 영화나 tv 시리즈 몰아보기가 유행에 따라 각광받고 있는 홈시어터를 구축해보자. 스트리밍 플레이어, 프로젝터, 자동 스크린, 서라운드 음향, 팝콘 기계 등 클래스들이 서로 복잡하게 얽혀 있다.</p>
<p><img src="https://velog.velcdn.com/images/ljo_0920/post/9a45db6e-abec-4385-9a8c-308439b53082/image.png" alt=""></p>
<p><a href="https://invincibletyphoon.tistory.com/22">https://invincibletyphoon.tistory.com/22</a></p>
<h3 id="영화를-보기위한-일련의-작업">영화를 보기위한 일련의 작업</h3>
<p>이제 영화를 보려고 하지만  영화를 보려면 몇 가지 일을 더해야 한다.</p>
<ol>
<li>팝콘 기계를 켠다</li>
<li>팝콘을 튀긴다.</li>
<li>조명을 어둡게 조절한다.</li>
<li>스크린을 내린다.</li>
<li>프로젝터를 켠다.</li>
<li>프로젝터 입력을 스트르밍 플레이어로 설정한다.</li>
<li>프로젝터를 와이드 스크린 모드로 전환한다.</li>
<li>앰프를 켠다.</li>
<li>앰프 입력을 스트리밍 플레이어로 설정한다.</li>
<li>앰프를 서라운드 음향 모드로 전환한다.</li>
<li>앰프 볼륭을 중간인 5로 설정한다.</li>
<li>스트리밍 플레이어를 켠다.</li>
<li>영화를 재생한다.</li>
</ol>
<p>이제 이 작업들을 처리하기 위한 어떤 클래스와 메소드가 필요한지 살펴보자.</p>
<pre><code class="language-java">popper.on();
popper.pop();

lights.dim(10);
screen.down();
projector.on();
projector.setInput(player);
projector.wideScreenMode();

amp.on();
amp.setDvd(player);
amp.setSurroundSound();
amp.setVolume(5);

player.on();
player.play(movie);</code></pre>
<p>클래스가 6개나 필요하고, 만약 영화가 끝나면 어떻게 해야할까?, 방금 했던 일을 전부 역순으로 처리해야 하지 않을까? 다른 라디오나 시스템이 업그레이드하면 이런 복잡한 작동 방법을 또 배워야 하지 않을까?</p>
<ul>
<li>이렇게 복잡한 일을 <code>퍼사드 패턴</code>으로 간단하게 처리할 수 있는지 알아보자</li>
</ul>
</br>

<h2 id="퍼사드-작동-원리">퍼사드 작동 원리</h2>
<p>쓰기 쉬운 인터페이스를 제공하는 퍼사드 클래스를 구현함으로써 복잡한 시스템을 훨씬 편리하게 사용할 수 있다. 물론 기존의 시스템을 직접 건드리고 싶다면 기존 인터페이스를그대로 사용하면 된다.</p>
<p><img src="https://velog.velcdn.com/images/ljo_0920/post/7270a8ae-abab-4267-b3e6-36c3886cad8e/image.png" alt=""></p>
<ol>
<li>홈시어터 시스템용 퍼사드를 만들어보자. <ul>
<li>watchMovie()와 같이 몇 가지 간단한 메소드만 들어있는 HomeTheaterFacade 클래스를 새로 만들어야 한다.</li>
</ul>
</li>
<li>퍼사드 클래스는 홈시어터 구성 요소를 <code>하나의 서브시스템</code>으로 간주한다.<ul>
<li>watchMovie() 메서드는 서브시스템의 메소드를 호출해서 필요한 작업을 처리한다.</li>
</ul>
</li>
<li>이제 클라이언트 코드는 서비스시템이 아닌 홈시어터 퍼사드에 있는 메서드를 호출한다.<ul>
<li>watchMovie() 메서드만 호출하면 조명, 스트리밍 플레이어, 앰프 등 알아서 준비된다.</li>
</ul>
</li>
<li>퍼사드를 쓰더라도 서브시스템에 여전히 직접 접근할 수 있다.<ul>
<li>서브시스템 클래스의 고급 기능이 필요하면 언제든지 사용 가능하다.</li>
</ul>
</li>
</ol>
<h3 id="q-퍼사드로-서브시스템-클래스를-캡슐화하면-저수준-기능을-원하는-클라이언트는-어떻게-서브시스템-클래스에-접근할-수-있을까">Q. 퍼사드로 서브시스템 클래스를 캡슐화하면 저수준 기능을 원하는 클라이언트는 어떻게 서브시스템 클래스에 접근할 수 있을까?</h3>
<p>퍼사드 클래스는 서브시스템 클래스를 캡슐화하지 않는다. 서브시스템의 기능을 사용할 수 있는 간단한 인터페이스를 제공할 뿐이다. 클라이언트에서 특정 인터페이스가 필요하다면 서브시스템 클래스를 <code>그냥 사용</code>하면 된다. 이점이 퍼사드 클래스의 대표적인 장점이다. <code>단순화된 인터페이스를 제공하면서도, 클라이언트에서 필요로 한다면 시스템의 모든 기능을 사용</code>할 수 있도록 해줍니다.</p>
<h3 id="q-퍼사드에서-기능을-추가하거나-각각의-요청을-서브시스템에-그대로-전달하기도-할까">Q. 퍼사드에서 기능을 추가하거나 각각의 요청을 서브시스템에 그대로 전달하기도 할까?</h3>
<p>퍼사드는 단순화된 서브시스템의 기능을 활용하게 해주는 일 외에도 ‘<code>스마트</code>’한 기능을 알아서 추가한다. 예를 들어, 홈시어터  퍼사드는 새로운 행동을 구현하지는 않지만, 팝콘을 튀기기 전에 팝콘 기계를 켜야 한다는 사실을 알고 있습니다. 그래서 팝콘 기계를 알아서 킨다. 그리고 각 구성 요소를 켜고 적절한 모드를 선택하는 것도 알아서 잘할 정도로 ‘스마트’하다.</p>
<h3 id="q-하나의-서브시스템에는-하나의-퍼사도만-만들수-있을까">Q. 하나의 서브시스템에는 하나의 퍼사도만 만들수 있을까?</h3>
<p>그렇지 않다. 특정 서브시스템에 대해 만들 수 있는 퍼사드의 개수에는 제한이 없다.</p>
<h3 id="q-더-간단한-인터페이스를-만들-수-있다는-점-말고-퍼사드의-또-다른-장점은-없을까">Q. 더 간단한 인터페이스를 만들 수 있다는 점 말고 퍼사드의 또 다른 장점은 없을까?</h3>
<p>퍼사드를 사용하면 <code>클라이언트 구현과 서브시스템을 분리</code>할 수 있다. 예를 들어 홈시어터 시스템을 업그레이드 하기로 가정해보자. 이런 경우 인터페이스가 크게 달라질 수 있을 것이다. 만약 클라이언트를 퍼사드로 만들었다면 클라이언트 코드는 고칠 필요 없이 퍼사드만 바꾸면 된다.</p>
<h3 id="q-어댑터는-한-클래스만-감싸고-퍼사드는-여러-클래스를-감쌀-수-있는-것일까">Q. 어댑터는 한 클래스만 감싸고 퍼사드는 여러 클래스를 감쌀 수 있는 것일까?</h3>
<p>그렇지 않다. 어댑터 패턴은 하나 이상의 클래스 인터페이스를 클라이언트에서 필요로 하는 인터페이스로 변환한다. 클라이언트가 여러 클래스를 사용할 수도 있기 대문이다. 반대로 퍼사드도 꼭 여러 클래스를 감싸야만 하는 건 아니다. 아주 복잡한 인터페이스를 가지고 있는 단 하나의 클래스에 대한 퍼사드를 만들 수도 있다.</p>
<p>어댑터와 퍼사드의 차이점은 감싸는 클래스의 개수에 있는 것이 아니라 <code>용도</code>에있다. 어댑터 패턴은 <code>인터페이스를 변경해서 클라이언트에서 필요로 하는 인터페이스로 적응</code>시키는 용도로 쓰인다. 반면 퍼사드 패턴은 <code>어떤 서브시스템에 대한 간단한 인터페이스를 제공</code>하는 용도로 쓰인다.</p>
</br>

<h2 id="홈시어터-퍼사드">홈시어터 퍼사드</h2>
<pre><code class="language-java">public class HomeTheaterFacade {

    // composition 부분, 사용하고자 하는 서브시스템의 모든 구성 요소가 인스턴스 변수 형태로 저장된다.
    private final Amplifier amp;
    private final Tuner tuner;
    private final StreamingPlayer player;
    private final Projector projector;
    private final TheaterLights lights;
    private final Screen screen;
    private final PopcornPopper popper;

    public HomeTheaterFacade(Amplifier amp, Tuner tuner, StreamingPlayer player,
        Projector projector,
        TheaterLights lights, Screen screen, PopcornPopper popper) {
        this.amp = amp;
        this.tuner = tuner;
        this.player = player;
        this.projector = projector;
        this.lights = lights;
        this.screen = screen;
        this.popper = popper;
    }

    public void watchMovie(String movie) {
        System.out.println(&quot;영화 볼 준비 중&quot;);
        popper.on();
        popper.pop();

        lights.dim(10);
        screen.down();
        projector.on();
        projector.wideScreenMode();
        projector.setInput(player);

        amp.on();
        amp.setDvd(player);
        amp.setSurroundSound();
        amp.setVolume(5);

        player.on();
        player.play(movie);
    }

    public void endMovie() {
        System.out.println(&quot;홈시어터 끄는 중&quot;);
        popper.off();
        lights.on();
        screen.up();
        projector.off();
        amp.off();
        player.stop();
        player.off();
    }
}</code></pre>
<ul>
<li>각 서브시스템에 들어있는 구성요소에게 위임하며 단순화된 인터페이스를 제공한다.</li>
</ul>
<pre><code class="language-java">public class HomeTheaterTestDrive {

    public static void main(String[] args) {
        // 구성 요소 초기화
        // 지금은 구성 요소를 직접 생성하지만 보통은 클라이언트에 퍼사드가 주어지므로 직접 구성 요소를 생성하지 않아도 된다.

        Amplifier amp = new Amplifier();
        Tuner tuner = new Tuner();
        StreamingPlayer player = new StreamingPlayer();
        Projector projector = new Projector();
        TheaterLights lights = new TheaterLights();
        Screen screen = new Screen();
        PopcornPopper popper = new PopcornPopper();
        HomeTheaterFacade homeTheater = new HomeTheaterFacade(
            amp,
            tuner,
            player,
            projector,
            lights,
            screen,
            popper
        );
        // 단순화된 인터페이스를 사용
        homeTheater.watchMovie(&quot;king kong&quot;);
        homeTheater.endMovie();
    }

}</code></pre>
</br>

<h2 id="퍼사드-패턴의-정의">퍼사드 패턴의 정의</h2>
<p>퍼사드 패턴을 사용하려면 어떤 서브시스템에 속한 일련의 복잡한 클래스를 단순하게 바꿔서 통합한 클래스를 만들어야 한다. 다른 패턴과 달리 퍼사드 패턴은 상당히 단순한 편이다. 복잡한 추상화 같은 게 필요 없다. 하지만 퍼사드 패턴을 사용하면 클라이언트와 서브시스템이 서로 긴밀하게 연결되지 않아도 되고, 최소 지식 객체지향 원칙을 준수하는데도 도움이 된다.</p>
<p>퍼사드 패턴의 정의는 다음과 같다.</p>
<aside>
💡 퍼사드 패턴은 서브시스템에 있는 일련의 인터페이스를 통합 인터페이스로 묶어 준다. 또한 고수준 인터페이스도 정의하므로 서브시스템을 더 편리하게 사용할 수 있다.

</aside>

<p>여기서 가장 중요한 점은 패턴의 용도이다. 정의를 보면 퍼사드 패턴은 단순화된 인터페이스로 서브시스템을 더 편리하게 사용하려고 쓰인다는 사실을 알 수 있다. 퍼사드 패턴의 클래스 다이어그램에서도 이 사실을 확인할 수 있다.</p>
<h3 id="클래스-다이어그램-1">클래스 다이어그램</h3>
<p><img src="https://velog.velcdn.com/images/ljo_0920/post/716c6515-2b36-443c-afa3-0292c37a698d/image.png" alt=""></p>
<p><a href="https://secretroute.tistory.com/entry/Head-First-Design-Patterns-%EC%A0%9C7%EA%B0%95-Adapter-%ED%8C%A8%ED%84%B4%EA%B3%BC-Facade-%ED%8C%A8%ED%84%B4">https://secretroute.tistory.com/entry/Head-First-Design-Patterns-제7강-Adapter-패턴과-Facade-패턴</a></p>
</br>

<h2 id="최소-지식-원칙">최소 지식 원칙</h2>
<p>최소 지식 원칙(Principle of Least Knowledge)에 따르면 객체 사이의 상호자용은 될 수 있으면 아주 가까운 ‘친구’ 사이에서만 허용하는 편이 좋다. 이 원칙은 보통 다음과 같이 정의될 수 있다.</p>
<aside>
💡 진짜 절친에게만 이야기 해야 한다.

</aside>

<p>그런데 이게 정확히 무슨 소리일까? 시스템을 디자인할 때 어떤 객체든 그 객체와 상호작용을 하는 클래스의 개수와 상호작용 방식에 주의를 기울여야 한다는 뜻이다.</p>
<p>이 원칙을 잘 따르면 여러 클래스가 복잡하게 얽혀 있어서, 시스템의 한 부분을 변경했을 때 다른 부분까지 줄줄이 고쳐야 하는 상황을 미리 방지할 수 있다. 여러 클래스가 서로 복잡하게 의존하고 있다면 관리하기도 힘들고, 남들이 이해하기 어려운 불안정한 시스템이 만들어진다.</p>
<h2 id="친구를-만들지-않고-다른-객체에-영향력-행사하기">친구를 만들지 않고 다른 객체에 영향력 행사하기</h2>
<p>그런데 어떻게 하면 여러 객체와 친구가 되는 것을 피할 수 있을까? </p>
<p>이 원칙은 친구를 만들지 않는 4개의 가이드라인을 제시한다.</p>
<ol>
<li>객체 자체</li>
<li>메소드에 매개변수로 전ㄴ달된 객체</li>
<li>메소드를 생성하거나 인스턴스를 만든 객체</li>
<li>객체에 속하는 구성 요소</li>
</ol>
<p>해당 가이드라인에 따르면 다른 메소드를 호출해서 리턴받은 객체의 메소드를 호출하는 일도 바람직 하지 않다. 따라서 꽤 까다로운 가이드라인이다. 메소드를 호출한 결과로 리턴받은 객체에 들어있는 메소드를 호출하면 <code>다른 객체의 일부분에 요청하게 되고, 직접적으로 알고 지내는 객체의 수가 늘어나는 단점</code>이 있다.</p>
<p>이러한 상황에서 최소 지식 원칙을 따르려면 객체가 대신 요청하도록 만들어야 한다. 그러면 그 객체의 한 구성 요소를 알고 지낼 필요가 없어지고 친구의 수를 줄이는 데도 도움이 된다.</p>
<h3 id="before">Before</h3>
<pre><code class="language-java">public float getTemp() {
        Thermometer thermometer = station.getThermometer();
        return thermometer.getTemperature();
}</code></pre>
<ul>
<li>station으로 부터 thermometer 객체를 받은 다음, 그 객체의 getTemperature() 메소드를 직접 호출</li>
</ul>
<h3 id="after">After</h3>
<pre><code class="language-java">public float getTemp() {
        return station.getTemperature();
}</code></pre>
<ul>
<li>최소 지식 원칙을 적용해서 thermometer 에게 요청을 전달하는 메소드를 station 클래스에 추가</li>
<li>의존해야 하는 클래스의 개수를 줄인다.</li>
</ul>
<h2 id="절친에게만-메소드-호출하기">절친에게만 메소드 호출하기</h2>
<p>다음은 자동차를 나타내는 Car 클래스이다. 이 클래스르 살펴보면 최소 지식 원칙을 따르면서 메소드를 호출하는 방법을 어느 정도 파악할 수 있다.</p>
<pre><code class="language-java">public class Car {

    Engine engine; // 해당 클래스의 구성요소, 구성요소의 메소드는 호출해도 된다.

    public Car(Engine engine) {
        this.engine = engine;
    }

    public void start(Key key) {            // 매개변수로 전달된 객체의 메소드는 호출 가능하다.
        Doors doors = new Doors();          // 새로운 객체를 생성, 해당 객체의 메소드 호출 가능
        boolean authorized = key.turns();   // 매개변수로 전달된 객체
        if (authorized) {
            engine.start();                 // 이 객체의 구성 요소를 대상으로 메소드 호출 가능
            updateDashboardDisplay();       // 객체 내에 있는 메소드 호출 가능
            doors.lock();                   // 직접 생성하거나 인스턴스를 만든 객체의 메소드 호출 가능
        }
    }

    private void updateDashboardDisplay() {
        // update display
    }
}</code></pre>
<h3 id="q-데메테르의-법칙이라는-것도-있던데-최소-지식-원칙과-어떤-관계일까">Q. 데메테르의 법칙이라는 것도 있던데, 최소 지식 원칙과 어떤 관계일까?</h3>
<p>데메테르의 법칙과 최소 지식 원칙은 완전히 똑같은 말이다. 하지만 좀 더 직관적이고 법칙이라는 단어가 없는 최소 지식 원칙을 선호한다. 모든 원칙은 상황에 따라서 적절하게 따라야 한다.</p>
<h3 id="q-최소-지식-원칙도-단점이-있을까">Q. 최소 지식 원칙도 단점이 있을까?</h3>
<p>물론 존재한다. 이 원칙을 잘 따르면 객체 사이의 의존성을 줄일 수 있으며 소프트웨어 관리가 더 편해지지만, <code>메소드 호출을 처리하는 ‘래퍼’ 클래스</code>를 더 만들어야 할 수도 있다. 그러면 시스템이 복잡해지고, 개발 시간도 늘어나고, 성능도 떨어진다.</p>
</br>

<h2 id="핵심-정리">핵심 정리</h2>
<ul>
<li>기존 클래스를 사용하려고 하는데 인터페이스가 맞지 않으면 어댑터를 쓰면 된다.</li>
<li>큰 인터페이스와 여러 인터페이스를 단순하게 바꾸거나 통합해야 하면  퍼사드를 쓰면 된다.</li>
<li>어댑터는 인터페이스를 클라이언트에서 원하는 인터페이스로 바꾸는 역할을 한다.</li>
<li>퍼사드는 클라이언트를 복잡한 서브시스템과 분리하는 역할을 한다.</li>
<li>어댑터를 구현할 때는 타깃 인터페이스의 크기와 구조에 따라 코딩해야 할 분량이 결정된다.</li>
<li>퍼사드 패턴에서드는 서브시스템으로 퍼사드를 만들고 진짜 작업은 서브클래스에 맡긴다.</li>
<li>어댑터 패턴에는 객체 어댑터 패턴과 클래스 어댑터 패턴이 있다.<ul>
<li>클래스 어댑터를 사용하려면 다중 상속이 가능해야 한다.</li>
</ul>
</li>
<li>한 서브시스템에 퍼사드를 여러 개 만들어도 된다.</li>
<li>어댑터는 객체를 감싸서 인터페이스를 바꾸는 용도로, 데코레이터는 객체를 감싸서 새로운 행동을 추가하는 용도로, 퍼사드는 일련의 객체를 감싸서 단순하게 만드는 용도로 쓰인다.</li>
</ul>
</br>

<h2 id="객체지향-도구-상자">객체지향 도구 상자</h2>
<ul>
<li>객체지향의 기초(4요소)<ul>
<li>캡슐화</li>
<li>상속</li>
<li>추상화</li>
<li>다형성</li>
</ul>
</li>
<li>객체지향 원칙<ul>
<li>바뀌는 부분을 캡슐화한다.</li>
<li>상속보다는 구성을 활용한다.</li>
<li>구현이 아닌 인터페이스(super type)에 맞춰서 프로그래밍한다.</li>
<li>서로 상호작용을 하는 객체 사이에서는 가능하면 느슨하게 결합하는 디자인을 사용해야 한다.</li>
<li>클래스는 확장에 대해서는 열려 있지만 변경에 대해서는 닫혀 있어야 한다. (OCP)</li>
<li>추상화된 것에 의존하라. 구상 클래스에 의존하지 않도록 한다.</li>
<li><strong>진짜 절친에게만 이야기해야 한다.</strong></li>
</ul>
</li>
<li>객체지향 패턴<ul>
<li>스트레지티 패턴 : 알고리즘군을 정의하고 각각의 알고리즘을 정의하고 각각을 캡슐화하여 교환해서 사용할 수 있도록 만든다. 전략을 사용하면 알고리즘을 사용하는 클라이언트와는 독립적으로 알고리즘을 변경할 수 있다.</li>
<li>옵저버 패턴 : 한 객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체들한테 연락이 가고 자동으로 내용이 갱신되는 방식으로 일대다 의존성을 정의한다.</li>
<li>데코레이터 패턴: 객체에 추가 요소를 동적으로 더할 수 있습니다. 데코레이터를 사용하면 서브 클래스를 만드는 경우에 비해 훨씬 유연하게 기능을 확장할 수 있습니다.</li>
<li>추상 팩토리 패턴 : 서로 연관된, 또는 의존적인 객체들로 이루어진 제품군을 생성하기 위한 인터페이스를 제공한다. 구상 클래스는 서브 클래스에 의해 만들어진다.</li>
<li>팩토리 메소드 패턴 : 객체를 생성하기 위한 인터페이스를 만든다. 어떤 클래스의 인스턴스를 만들지는 서브클래스에서 결정하도록 한다. 팩토리 메소드를 이용하면 인스턴스를 만드는 일을 서브클래스로 미룰 수 있다.</li>
<li>싱글턴 패턴 : 클래스 인스턴스가 하나만 만들어지도록 하고, 그 인스턴스에 대한 전역 접근을 제공한다.</li>
<li>커맨드 패턴 : 요청 내역을 객체로 캡슐화해서 객체를 서로 다른 요청 내역에 따라 매개변수화할 수 있다. 이러면 요청을 큐에 저장하거나 로그로 기록하거나 작업 취소 기능을 사용할 수 있다.</li>
<li><strong>어댑터 패턴 : 특정 클래스 인터페이스를 클라이언트에서 요구하는 다른 인터페이스로 변환한다. 인터페이스가 호환되지 않아 같이 쓸 수 없었던 클래스를 사용할 수 있게 도와준다.</strong></li>
<li><strong>퍼사드 패턴 : 서브시스템에 있는 일련의 인터페이스를 통합 인터페이스로 묶어 준다. 또한 고수준 인터페이스도 정의하므로 서브시스템을 더 편리하게 사용할 수 있다.</strong></li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[디자인 패턴 - 커맨드 패턴]]></title>
            <link>https://velog.io/@ljo_0920/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-%EC%BB%A4%EB%A7%A8%EB%93%9C-%ED%8C%A8%ED%84%B4</link>
            <guid>https://velog.io/@ljo_0920/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-%EC%BB%A4%EB%A7%A8%EB%93%9C-%ED%8C%A8%ED%84%B4</guid>
            <pubDate>Wed, 27 Jul 2022 00:54:29 GMT</pubDate>
            <description><![CDATA[<h1 id="커맨드-패턴">커맨드 패턴</h1>
<p><a href="https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=582754">헤드 퍼스트 디자인 패턴</a>을 읽고 정리한 글입니다.</p>
<blockquote>
<p>커맨드 패턴을 이용하면 요구 사항을 객체로 캡슐화 할 수 있으며, 매개변수를 써서 여러 가지 다른 요구 사항을 집어넣을 수도 있다. 또한 요청 내역을 큐에 저장하거나 로그로 기록할 수도 있으며, 작업 취소 기능도 지원 가능하다.</p>
</blockquote>
<ul>
<li>호출 캡슐화</li>
<li>한 차원 높은 단계의 캡슐화인 메소드 호출을 캡슐화하는 것을 배워보자</li>
<li>메소드 호출을 캡슐화 하면 계산 과정의 각 부분들을 결정화시킬 수 있끼 때문에, 계산하는 코드를 호출한 객체에서는 어떤 식으로 일을 처리해야 하는지에 대해 전혀 신경쓰지 않아도 된다.</li>
<li>그 외에도 캡슐화된 메소드 호출을 로그 기록용으로 저장을 한다거나 취소 기능을 구현하기 위해 재사용하는 것과 같은 작업을 할 수 도 있다.</li>
</ul>
<aside>
💡 요청 내역을 객체로 캡슐화해서 객체를 서로 다른 요청 내역에 따라 매개변수화할 수 있다. 이러면 요청을 큐에 저장하거나 로그로 기록하거나 작업 취소 기능을 사용할 수 있다.

</aside>

<ul>
<li>요청하는 객체와 요청을 수행하는 객체를 분리하고 싶다면 커맨드 패턴을 사용하자</li>
</ul>
</br>

<h2 id="개요---홈-오토메이션-리모콘">개요 - 홈 오토메이션 리모콘</h2>
<p>리모컨 API 디자인을 해보자. 해당 리모컨에는 일곱 가지 프로그래밍이 가능한 슬롯과 각 슬롯에 대한 온오프 스위치가 있다. 각 슬롯은 서로 다른 가정용 기기에 연결할 수 있다. 리모컨에는 작업 취소 버튼도 장착되어 있다.</p>
<p>조명, 팬, 욕조, 오디오를 비롯한 각종 홈 오토메이션 장비들을 제어하기 위한 용도로 다양한 업체에서 공급 받은 자바 클래스들을 같이 받았다.</p>
<p>각 슬롯을 한 가지 기기 또는 하나로 엮여 있는 일련의 기기들에 할당할 수 있도록 리모컨을 프로그래밍하기 위한 API를 제작해보자.</p>
</br>

<h2 id="제공-받은-클래스">제공 받은 클래스</h2>
<p><img src="https://velog.velcdn.com/images/ljo_0920/post/cf27af57-2f80-4225-9548-cd33f31b7237/image.png" alt=""></p>
<p><a href="https://faun.pub/head-first-design-patterns-using-go-5-encapsulating-invocation-the-command-pattern-2f8c0a79d1c7">https://faun.pub/head-first-design-patterns-using-go-5-encapsulating-invocation-the-command-pattern-2f8c0a79d1c7</a></p>
<p>제공 받은 클래스들을 살펴보자 리모컨에서 제어해야 하는 객체의 인터페이스에 대한 정보를 얻을 수 있을 것이다.</p>
<p>공통적인 인터페이스가 있는 것 같진 않다. 리모컨에는 on, off 버튼만 있지만, 가전제품 클래스에는 여러 메서드가 존재하고 더 큰 문제는 앞으로 이런 클래스들이 더 추가될 수 있다는 점이다.</p>
<p>따라서 리모컨 버튼을 누르면 자동으로 해야할 일을 처리할 수 있도록 하고 리모컨에서 제품 업체에게 전달받은 클래스에 대해 자세히 알 필요가 없도록 디자인을 진행해야 할 것 같다.</p>
<h3 id="해결책--커맨드-패턴">해결책 : 커맨드 패턴</h3>
<p>이를 해결하기 위해 어떻게 해야할까??</p>
<ul>
<li>작업을 요청한 쪽과 작업을 처리하는 쪽을 분리시킨다.</li>
<li>여기서는 리모컨이 작업을 요청하는 쪽, 업체에서 공급한 클래스의 인스턴스는 작업을 처리하는 쪽</li>
<li>이를 위해  <code>커맨드 객체</code>를 추가하여 분리시킬수 있다.<ul>
<li>커맨드 객체는 특정 객체에 대한 특정 작업 요청을 캡슐화한다.</li>
<li>따라서 버튼마다 커맨드 객체를 저장해 두면 사용자가 버튼을 눌렀을 때 커맨드 객체를 통해서 작업을 처리하도록 만든다.</li>
<li>리모컨은 작업이 무엇인지 전혀 모르고 작업을 완료하기 위한 객체와 상호작용하는 방법을 알고 있는 커맨드 객체만 존재하므로, 리모컨은 벤더쪽 클래스와 분리된다.</li>
</ul>
</li>
<li>이러한 패턴을 <code>커맨드 패턴</code> 이라고 한다.</li>
</ul>
</br>

<h2 id="커맨드-패턴-다이어그램">커맨드 패턴 다이어그램</h2>
<p><img src="https://velog.velcdn.com/images/ljo_0920/post/c4d3192a-4829-45a2-8481-cee4c8d84791/image.png" alt=""></p>
<p><a href="https://faun.pub/head-first-design-patterns-using-go-5-encapsulating-invocation-the-command-pattern-2f8c0a79d1c7">https://faun.pub/head-first-design-patterns-using-go-5-encapsulating-invocation-the-command-pattern-2f8c0a79d1c7</a></p>
<p>클라이언트</p>
<ul>
<li>클라이언트는 커맨드 객체를 생성해야 한다.</li>
<li>커맨드 객체는 리시버에 전달할 일련의 행동으로 구성된다.</li>
</ul>
<p>리시버</p>
<ul>
<li>커맨드 객체에는 행동과 리시버에 대한 정보가 같이 들어 있다.</li>
</ul>
<p>커맨드</p>
<ul>
<li>커맨드 객체에서 제공하는 메소드는 excute() 하나 뿐이다.</li>
<li>이 메소드에는 행동을 캡슐화하며, 리시버에 있는 특정 행동을 처리하기 위한 메소드를 호출하기 위한 메서드이다.</li>
</ul>
<p>인보커</p>
<ul>
<li>클라이언트에서는 인보커 객체의 setCommand() 메소드를 호출하는데, 이 때 커맨드 객체를 넘겨줍니다.</li>
<li>그 커맨드 객체는 나중에 쓰으기 전까지 인보커 객체에 보관됩니다.</li>
<li>인보커에서 커맨드 객체의 excute() 메소드를 호출하면  리시버에 있는 특정 행동을 하는 메소드가 호출된다.</li>
</ul>
<h3 id="인보커-로딩">인보커 로딩</h3>
<ol>
<li>클라이언트에서 커맨드 객체 생성</li>
<li>setCommand()를 호출하여 인보커에 커맨드 객체를 저장</li>
<li>나중에 클라이언트에서 인보커한테 그 명령을 실행시켜 달라는 요청을 함</li>
</ol>
<h3 id="커맨드-패턴과-식당-비유해보기">커맨드 패턴과 식당 비유해보기</h3>
<ul>
<li>클라이언트 == 손님</li>
<li>주문서 == 커맨드 객체</li>
<li>주문받기(takeOrder()) == setCommand()</li>
<li>웨이트리스 == 인보커 객체</li>
<li>주문(orderUp()) == execute()</li>
<li>주방장 == 리시버 객체</li>
</ul>
</br>

<h2 id="커맨드-객체">커맨드 객체</h2>
<p>이제 첫 커맨드 객체를 만들어 보자.</p>
<h3 id="command-인터페이스-만들기">Command 인터페이스 만들기</h3>
<ul>
<li>커맨드 객체는 모두 같은 인터페이스를 구현해야 한다.</li>
<li>해당 인터페이스에는 메소드가 하나 밖에 없다.</li>
<li>일반적으로 execute() 이름을 많이 사용한다.</li>
</ul>
<pre><code class="language-java">public interface Command {

    void execute();

}</code></pre>
<p>이제 전등을 켜기 위한 커맨드 클래스를 구현해보자. 벤더사에서 제공한 클래스를 보니 Light 클래스에는 on(), off() 두 개의 메소드가 있다. </p>
<p><img src="https://velog.velcdn.com/images/ljo_0920/post/1bfdd2f4-9c83-40dd-b40d-0755c74ca2d1/image.png" alt=""></p>
<pre><code class="language-java">public class LightOnCommand implements Command {

    Light light;

    public LightOnCommand(Light light) {
        this.light = light;
    }

    @Override
    public void execute() {
        light.on();
    }
}</code></pre>
<ul>
<li>생성자에 이 커맨드 객체로 제어할 특정 전등에 대한 정보가 전달된다.</li>
<li>해당 객체는 light라는 인스턴스 변수에 저장이 되며, execute() 메소드가 호출되면 light 객체가 바로 그 요청에 대한 <code>리시버</code>가 된다.</li>
<li>execute() 메소드에서는 리시버 객체에 있는 on() 메서드를 호출한다.</li>
</ul>
</br>

<h2 id="커맨드-객체-사용하기">커맨드 객체 사용하기</h2>
<p>이제 커맨드 객체를 써서 가정요 기기를 조작하기 위해 버튼이 하나 밖에 없는 리모콘이 있다고 가정하고 코드를 작성해보자.</p>
<pre><code class="language-java">public class SimpleRemoteControl { // 인보커

    Command slot;

    public SimpleRemoteControl() {
    }

    public void setCommand(Command command) {
        slot = command;
    }

    public void buttonWasPressed() {
        slot.execute();
    }
}</code></pre>
<pre><code class="language-java">class SimpleRemoteControlTest { // 1

    public static void main(String[] args) {
        SimpleRemoteControl remoteControl = new SimpleRemoteControl(); // 2
        Light light = new Light(); // 3
        LightOnCommand lightOn = new LightOnCommand(light); // 4

        remoteControl.setCommand(lightOn); // 5
        remoteControl.buttonWasPressed();
    }

}</code></pre>
<ul>
<li>1 : 클라이언트에 해당하는 부분(SimpleRemoteControlTest)</li>
<li>2 : remoteControl 변수가 <code>인보커</code> 역할을 한다.<ul>
<li>필요한 작업을 요청할 때 사용할 커맨드 객체를 인자로 전달받는다.</li>
</ul>
</li>
<li>3 : 요청을 받아서 처리할 <code>리시버</code>인 Light 객체를 생성한다.</li>
<li>4 : 커맨드 객체를 생성한다.<ul>
<li>이 때 리시버를 전달해 준다.</li>
</ul>
</li>
<li>5 : 커맨드 객체를 인보커한테 전달해 준다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/ljo_0920/post/92ed4ad3-4f35-4ca8-89fe-f90bbe9eb654/image.png" alt=""></p>
</br>

<h2 id="커맨드-패턴의-정의">커맨드 패턴의 정의</h2>
<p>이제 커맨드 패턴의 정의를 알아보고 더 자세히 살펴보자.</p>
<aside>
💡 커맨드 패턴을 이용하면 요구 사항을 객체로 캡슐화 할 수 있으며, 매개변수를 써서 여러 가지 다른 요구 사항을 집어넣을 수도 있다. 또한 요청 내역을 큐에 저장하거나 로그로 기록할 수도 있으며, 작업 취소 기능도 지원 가능하다.

</aside>

<ul>
<li>커맨드 객체는 일련의 행동을 특정 리시버하고 연결시킴으로써 요구 사항을 캡슐화한 것이다.<ul>
<li>이렇게 하기 위해서 행동과 리시버를 한 객체에 집어넣고 execute()라는 메소드 하나만 외부에 공개하는 방법을 쓴다.</li>
<li>이 메소드 호출에 의해서 리시버에서 일련의 작업이 처리된다.</li>
<li>외부에서 볼 때는 어떤 객체가 리시버 역할을 하는지, 그 리시버에서 실제로 어떤 일을 하는지 알 수 없다. 그냥  execute() 메소드를 호출하면 요구 사항이 처리된다는 것만 알 수 있을 뿐이다.</li>
<li>즉, <code>커맨드는 캡슐화된 요구사항</code>이다.<ul>
<li>리시버 : action</li>
<li>execute() : recevier.action();</li>
</ul>
</li>
</ul>
</li>
<li>명령을 통해서 객체를 매개변수화하는 예도 몇 가지 볼 수 있었다.<ul>
<li>리모컨 입장에서는 특정 인터페이수만 구현이 되어 있다면 그 커맨드 객체에서 실제로 어떤일을 하는지 신경 쓸 필요가 없다.</li>
<li>인보커(리모컨에 있는 슬롯 등)에 매개변수를 써서 여러가지 요구사항을 전달할 수도 있다.</li>
</ul>
</li>
<li>커맨드 객체들을 써서 큐나 로그를 구현하거나 작업 취소를 할 수 있으며, 메타 커맨드 패턴이라는 것을 이용하여 명령들로 이루어진 매크로를 만들어서 여러 개의 명령을 한 번에 실행할 수도 있다.</li>
</ul>
</br>

<h2 id="커맨드-패턴-클래스-다이어그램">커맨드 패턴 클래스 다이어그램</h2>
<p><img src="https://velog.velcdn.com/images/ljo_0920/post/12c82bcd-3808-41cb-b5c1-7b767c9f01d5/image.png" alt=""></p>
<p><a href="https://faun.pub/head-first-design-patterns-using-go-5-encapsulating-invocation-the-command-pattern-2f8c0a79d1c7">https://faun.pub/head-first-design-patterns-using-go-5-encapsulating-invocation-the-command-pattern-2f8c0a79d1c7</a></p>
<p>클라이언트</p>
<ul>
<li>ConcreteCommand를 생성하고 Receiver를 설정한다.</li>
</ul>
<p>인보커</p>
<ul>
<li>인보커에는 명령이 들어 있으며, execute() 메소드를 호출함으로써 커맨드 객체에게 특정 작업을 수행해 달라는 요구를 하게 된다.</li>
</ul>
<p>리시버</p>
<ul>
<li>리시버는 요구 사항을 수항해기 위해 어떤 일을 처리해야 하는지 알고 있는 객체이다.</li>
</ul>
<p>커맨드 인터페이스</p>
<ul>
<li>모든 커맨드 객체에서 구현해야 하는 인터페이스</li>
<li>모든 명령은 execute() 메소드 호출을 통해 수행되며, 이 메소드에서는 리시버에 특정 작업을 처리하라는 지시를 전달한다.</li>
<li>이 인터페이스를 보면 undo() 메소드도 들어있는데, 잠시 후에 알아보자</li>
</ul>
<p>구상 커맨드</p>
<ul>
<li>구상 커맨드는 특정 행동과 리시버 사이를 연결해 준다.</li>
<li>인보커에서 execute() 호출을 통해 요청을 하면 구상 커맨드 객체에서 리시버에 있는 메소드를 호출함으로써 그 작업을 처리한다.</li>
<li>execute() → receiver.action();</li>
</ul>
</br>

<h2 id="슬롯에-명령-할당하기">슬롯에 명령 할당하기</h2>
<p>이제 리모컨의 각 슬롯에 명령을 할당해보자. 이제 리모컨이 인보커가 되는 것이다.</p>
<p>사용자가 버튼을 누르면 그 버튼에 상응하는 커맨드 객체의 execute() 메소드가 호출되고, 그러면 리시버(vendor class)에서 특정 행동을 하는 메소드가 실행될 것이다.</p>
<p><img src="https://velog.velcdn.com/images/ljo_0920/post/67513e28-b392-4228-ab27-f433acb74132/image.png" alt=""></p>
<ul>
<li>각 슬롯마다 커맨드 객체가 할당된다.</li>
<li>사용자가 버튼을 누르면 해당 커맨드 객체의 execute() 메소드가 호출된다.</li>
<li>execute() 메소드에서는 리시버로 하여금 특정 작업을 처리하도록 지시한다.</li>
</ul>
<pre><code class="language-java">public class RemoteControl {

    private static final int SLOT_SIZE = 7;
    Command[] onCommands;
    Command[] offCommands;

    public RemoteControl() {
        offCommands = new Command[SLOT_SIZE];
        onCommands = new Command[SLOT_SIZE];

        Command noCommand = new NoCommand();
        for (int i = 0; i &lt; SLOT_SIZE; i++) {
            onCommands[i] = noCommand;
            offCommands[i] = noCommand;

        }
    }

    public void setCommand(int slot, Command onCommand, Command offCommand) {
        onCommands[slot] = onCommand;
        offCommands[slot] = offCommand;
    }

    public void onButtonWasPushed(int slot) {
        onCommands[slot].execute();
    }

    public void offButtonWasPushed(int slot) {
        offCommands[slot].execute();
    }

    public String toString() {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(&quot;\n------ Remote Control -------\n&quot;);
        for (int i = 0; i &lt; onCommands.length; i++) {
            stringBuilder
                .append(&quot;[slot &quot;)
                .append(i)
                .append(&quot;] &quot;)
                .append(onCommands[i].getClass().getName())
                .append(&quot;    &quot;)
                .append(offCommands[i].getClass().getName())
                .append(&quot;\n&quot;);
        }
        return stringBuilder.toString();
    }
}</code></pre>
<pre><code class="language-java">public class RemoteLoader {

    public static void main(String[] args) {
        RemoteControl remoteControl = new RemoteControl();

        Light livingRoomLight = new Light(&quot;Living Room&quot;);
        Light kitchenLight = new Light(&quot;Kitchen&quot;);
        CeilingFan ceilingFan = new CeilingFan(&quot;Living Room&quot;);
        GarageDoor garageDoor = new GarageDoor(&quot;Garage&quot;);
        Stereo stereo = new Stereo(&quot;Living Room&quot;);

        LightOnCommand livingRoomLightOn = new LightOnCommand(livingRoomLight);
        LightOffCommand livingRoomLightOff = new LightOffCommand(livingRoomLight);
        LightOnCommand kitchenLightOn = new LightOnCommand(kitchenLight);
        LightOffCommand kitchenLightOff = new LightOffCommand(kitchenLight);

        CeilingFanOnCommand ceilingFanOn = new CeilingFanOnCommand(ceilingFan);
        CeilingFanOffCommand ceilingFanOff = new CeilingFanOffCommand(ceilingFan);

        GarageDoorUpCommand garageDoorUp = new GarageDoorUpCommand(garageDoor);
        GarageDoorDownCommand garageDoorDown = new GarageDoorDownCommand(garageDoor);

        StereoOnWithCDCommand stereoOnWithCD = new StereoOnWithCDCommand(stereo);
        StereoOffCommand stereoOff = new StereoOffCommand(stereo);

        remoteControl.setCommand(0, livingRoomLightOn, livingRoomLightOff);
        remoteControl.setCommand(1, kitchenLightOn, kitchenLightOff);
        remoteControl.setCommand(2, ceilingFanOn, ceilingFanOff);
        remoteControl.setCommand(3, stereoOnWithCD, stereoOff);

        System.out.println(remoteControl);

        remoteControl.onButtonWasPushed(0);
        remoteControl.offButtonWasPushed(0);
        remoteControl.onButtonWasPushed(1);
        remoteControl.offButtonWasPushed(1);
        remoteControl.onButtonWasPushed(2);
        remoteControl.offButtonWasPushed(2);
        remoteControl.onButtonWasPushed(3);
        remoteControl.offButtonWasPushed(3);
    }
}</code></pre>
<h3 id="nocommand-객체">NoCommand 객체</h3>
<p>NoCommand 객체는 일종의 널 객체이다. 딱히 리턴할 객체는 없지만 클라이언트 쪽에서 null을 처리하지 않아도 되도록 하고 싶을 때 널 객체를 활용하면 좋다. 특정 슬롯을 쓰려고 할 때 마다 거기에 뭔가가 로딩되어 있는지 확인하려면 좀 귀찮기 때문이다.</p>
</br>

<h2 id="완성된-리모컨-api-디자인">완성된 리모컨 api 디자인</h2>
<p><img src="https://velog.velcdn.com/images/ljo_0920/post/7056eaa9-c70b-4053-9721-d8e2d99ad0ae/image.png" alt=""></p>
<p><a href="https://blog.yevgnenll.me/posts/what-is-command-pattern">https://blog.yevgnenll.me/posts/what-is-command-pattern</a></p>
<ul>
<li>리모컨 코드를 최대한 단순하게 만들어서 협력 업체가 새로운 벤더 클래스를 공급하더라도 리모컨 코드를 고치지 않도록 하는 것에 중점을 두었다.</li>
<li>커맨트 패턴을 도입해서 RemoteControl 클래스와 협력 업체로부터 제공되는 클래스를 논리적으로 분리했다.</li>
<li>이러한 디자인은 리모컨의 유지보수 비용을 줄이는데 굉장히 도움이 된다.</li>
<li>커맨드 클래스는 람다 표현식을 사용하여 커맨드 객체를 생성하는 단계를 건너뛰고 인스턴스를 생성하는 대신 그 자리에 함수 객체를 사용할 수 있다.<ul>
<li>물론 추상 메서드가 하나일 때만 가능</li>
</ul>
</li>
</ul>
<pre><code class="language-java">//        remoteControl.setCommand(0, livingRoomLightOn, livingRoomLightOff);
        remoteControl.setCommand(0, () -&gt; livingRoomLight.on(), () -&gt; livingRoomLight.off());
        remoteControl.setCommand(1, kitchenLightOn, kitchenLightOff);
        remoteControl.setCommand(2, ceilingFanOn, ceilingFanOff);
        remoteControl.setCommand(3, stereoOnWithCD, stereoOff);</code></pre>
</br>

<h2 id="작업-취소-기능-추가하기">작업 취소 기능 추가하기</h2>
<p>커맨드에서 작업 취소 기능을 지원하려면 execute() 메소드와 비슷한 undo() 메소드가 있어야 한다.</p>
<p>excute() 메소드에서 했던 작업과 정반대의 작업을 처리하면 된다. 커맨드 클래스에 작업 취소 기능을 추가하기 전에 우선 Command 인터페이스에 undo() 메소드를 추가해야 한다.</p>
<pre><code class="language-java">public interface Command {
        void excete();
    void undo();
}</code></pre>
<ul>
<li>그리고 RemoteControl 클래스에 사용자가 마지막으로 누른 버튼을 기록하고, UNDO 버튼을 눌렀을 때 필요한 작업을 처리하는 코드를 추가해야 한다.</li>
</ul>
<pre><code class="language-java">package command.client;

import command.cmd.Command;
import command.cmd.NoCommand;

public class RemoteControl {

    private static final int SLOT_SIZE = 7;
    Command[] onCommands;
    Command[] offCommands;
    Command undoCommand;

    public RemoteControl() {
        offCommands = new Command[SLOT_SIZE];
        onCommands = new Command[SLOT_SIZE];

        Command noCommand = new NoCommand();
        for (int i = 0; i &lt; SLOT_SIZE; i++) {
            onCommands[i] = noCommand;
            offCommands[i] = noCommand;

        }
        // 다른 슬롯과 마찬가지로 사용자가 다른 버튼을 한 번도 누르지 않은 상태에서 undo 버튼을 누르더라도 별 문제가 없도록 한다.
        undoCommand = noCommand;
    }

    public void setCommand(int slot, Command onCommand, Command offCommand) {
        onCommands[slot] = onCommand;
        offCommands[slot] = offCommand;
    }

    public void onButtonWasPushed(int slot) {
        onCommands[slot].execute();
        // 사용자가 버튼을 누르면 해당 커맨드 객체의 execute() 메서드를 호출한 다음
        // 그 객체의 레퍼런스를 undoCommand 인스턴스 변수에 저장한다.
        // on과 off 버튼을 처리할 때도 같은 방법 사용
        undoCommand = onCommands[slot];
    }

    public void offButtonWasPushed(int slot) {
        offCommands[slot].execute();
        undoCommand = offCommands[slot];
    }

    public void undoButtonWasPushed() {
        undoCommand.undo();
    }

    public String toString() {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(&quot;\n------ Remote Control -------\n&quot;);
        for (int i = 0; i &lt; onCommands.length; i++) {
            stringBuilder
                .append(&quot;[slot &quot;)
                .append(i)
                .append(&quot;] &quot;)
                .append(onCommands[i].getClass().getSimpleName())
                .append(&quot;    &quot;)
                .append(offCommands[i].getClass().getSimpleName())
                .append(&quot;\n&quot;);
        }
        return stringBuilder
            .append(&quot;[undo]&quot;)
            .append(&quot;    &quot;)
            .append(undoCommand.getClass().getSimpleName())
            .toString();
    }
}</code></pre>
<pre><code class="language-java">public class RemoteLoader {

    public static void main(String[] args) {
        RemoteControl remoteControl = new RemoteControl();

        Light livingRoomLight = new Light(&quot;Living Room&quot;);

        LightOnCommand livingRoomLightOn = new LightOnCommand(livingRoomLight);
        LightOffCommand livingRoomLightOff = new LightOffCommand(livingRoomLight);

        remoteControl.setCommand(0, livingRoomLightOn, livingRoomLightOff);

        remoteControl.onButtonWasPushed(0);
        remoteControl.offButtonWasPushed(0);
        System.out.println(remoteControl);
        remoteControl.undoButtonWasPushed();
        remoteControl.offButtonWasPushed(0);
        remoteControl.onButtonWasPushed(0);
        System.out.println(remoteControl);
        remoteControl.undoButtonWasPushed();

    }
}</code></pre>
<pre><code class="language-java">Living Room light is on
Living Room light is off

------ Remote Control -------
[slot 0] LightOnCommand    LightOffCommand
[slot 1] LightOnCommand    LightOffCommand
[slot 2] CeilingFanOnCommand    CeilingFanOffCommand
[slot 3] StereoOnWithCDCommand    StereoOffCommand
[slot 4] NoCommand    NoCommand
[slot 5] NoCommand    NoCommand
[slot 6] NoCommand    NoCommand
[undo]    LightOffCommand &gt;&gt;&gt;&gt;&gt;&gt; undoCmd에 마지막으로 호출되었던 커맨드 저장
Living Room light is on &gt;&gt;&gt;&gt;&gt;&gt; 사용자가 undo 버튼 클릭
Living Room light is off
Living Room light is on

------ Remote Control -------
[slot 0] LightOnCommand    LightOffCommand
[slot 1] LightOnCommand    LightOffCommand
[slot 2] CeilingFanOnCommand    CeilingFanOffCommand
[slot 3] StereoOnWithCDCommand    StereoOffCommand
[slot 4] NoCommand    NoCommand
[slot 5] NoCommand    NoCommand
[slot 6] NoCommand    NoCommand
[undo]    LightOnCommand &gt;&gt;&gt;&gt;&gt;&gt; undoCmd에 마지막으로 호출되었던 커맨드 저장
Living Room light is off &gt;&gt;&gt;&gt;&gt;&gt; 사용자가 undo 버튼 클릭

Process finished with exit code 0</code></pre>
</br>

<h2 id="작업-취소-기능을-구현할-때-상태를-사용하는-방법">작업 취소 기능을 구현할 때 상태를 사용하는 방법</h2>
<p>작업 취소 기능을 구현하다 보면 간단한 상태를 저장해야 하는 상황도 종종 생긴다.</p>
<p>CeilingFan 클래스로 간단한 속도와 관련된 상태를 저장해보자.</p>
<pre><code class="language-java">public class CeilingFan {

    String location;
    int speed; // 속도를 나타내는 상태를 저장
    public static final int HIGH = 3;
    public static final int MEDIUM = 2;
    public static final int LOW = 1;
    public static final int OFF = 0;

    public CeilingFan(String location) {
        this.location = location;
        speed = OFF;
    }

    public void high() {
        // turns the ceiling fan on to high
        speed = HIGH;
        System.out.println(location + &quot; ceiling fan is on high&quot;);

    }

    public void medium() {
        // turns the ceiling fan on to medium
        speed = MEDIUM;
        System.out.println(location + &quot; ceiling fan is on medium&quot;);
    }

    public void low() {
        // turns the ceiling fan on to low
        speed = LOW;
        System.out.println(location + &quot; ceiling fan is on low&quot;);
    }

    public void off() {
        // turns the ceiling fan off
        speed = OFF;
        System.out.println(location + &quot; ceiling fan is off&quot;);
    }

    public int getSpeed() {
        return speed;
    }
}</code></pre>
<pre><code class="language-java">package command.cmd;

import command.vendor.CeilingFan;

public class CeilingFanHighCommand implements Command {

    CeilingFan ceilingFan;
    int prevSpeed; // 상태 지역 변수로 선풍기의 속도를 저장

    public CeilingFanHighCommand(CeilingFan ceilingFan) {
        this.ceilingFan = ceilingFan;
    }

    public void execute() {
              // 속도를 변경하기 전에 작업을 취소해야 할 때를 대비해서 이전 속도를 저장
        prevSpeed = ceilingFan.getSpeed();
        ceilingFan.high();
    }

    @Override
    public void undo() {
        if (prevSpeed == CeilingFan.HIGH) {
            ceilingFan.high();
        } else if (prevSpeed == CeilingFan.MEDIUM) {
            ceilingFan.medium();
        } else if (prevSpeed == CeilingFan.LOW) {
            ceilingFan.low();
        } else if (prevSpeed == CeilingFan.OFF) {
            ceilingFan.off();
        }
    }
}</code></pre>
</br>

<h2 id="여러-동작을-한-번에-처리하기">여러 동작을 한 번에 처리하기</h2>
<pre><code class="language-java">package command.cmd;

public class MacroCommand implements Command {

    Command[] commands;

    public MacroCommand(Command[] commands) {
        this.commands = commands;
    }

    @Override
    public void execute() {
        for (int i = 0; i &lt; commands.length; i++) {
            commands[i].execute();
        }
    }

    @Override
    public void undo() { // 역순으로 undo
        for (int i = commands.length - 1; i &gt;= 0; i--) {
            commands[i].undo();
        }
    }
}</code></pre>
<pre><code class="language-java">public class RemoteLoader {

    public static void main(String[] args) {
        RemoteControl remoteControl = new RemoteControl();
        Light light = new Light(&quot;Living Room&quot;);
        Stereo stereo = new Stereo(&quot;Living Room&quot;);

        LightOnCommand lightOnCommand = new LightOnCommand(light);
        LightOffCommand lightOffCommand = new LightOffCommand(light);
        StereoOnCommand stereoOnCommand = new StereoOnCommand(stereo);
        StereoOffCommand stereoOffCommand = new StereoOffCommand(stereo);
        Command[] partyOn = {lightOnCommand, stereoOnCommand};
        Command[] partyOff = {lightOffCommand, stereoOffCommand};
        MacroCommand partyOnMacro = new MacroCommand(partyOn);
        MacroCommand partyOffMacro = new MacroCommand(partyOff);

        remoteControl.setCommand(0, partyOnMacro, partyOffMacro);
        System.out.println(remoteControl);
        System.out.println(&quot;---- macro on ------&quot;);
        remoteControl.onButtonWasPushed(0);
        System.out.println(&quot;---- macro off ------&quot;);
        remoteControl.offButtonWasPushed(0);
    }
}</code></pre>
<pre><code class="language-java">------ Remote Control -------
[slot 0] MacroCommand    MacroCommand
[slot 1] NoCommand    NoCommand
[slot 2] NoCommand    NoCommand
[slot 3] NoCommand    NoCommand
[slot 4] NoCommand    NoCommand
[slot 5] NoCommand    NoCommand
[slot 6] NoCommand    NoCommand
[undo]    NoCommand
---- macro on ------
Living Room light is on
Living Room stereo is on
Living Room stereo is set for CD input
Living Room stereo volume set to 11
---- macro off ------
Living Room light is off
Living Room stereo is off

Process finished with exit code 0</code></pre>
<h3 id="q-항상-리시버가-필요할까-커맨드-객체에서-execute를-구현하면-안될까">Q) 항상 리시버가 필요할까?? 커맨드 객체에서 execute()를 구현하면 안될까??</h3>
<p>A) 일반적으로 리시버에 있는 행동을 호출하는 ‘더미’  커맨드 객체를 만든다. 하지만 요구 사항의 전부는 아니더라도 대부분을 구현하는 ‘스마트’ 커맨드 객체를 만드는 경우도 자주 볼 수 있다. 물론 커맨드 객체에서 대부분의 행동을 처리해도 됩니다. 하지만 그러면 인보커와 리시버를 분리하기 어렵고, 리시버로 커맨드를 매개변수화할 수 없다는 점을 염두하자.</p>
<h3 id="q--작업-취소를-할-때-히스토리-기능은-어떻게-구현할-수-있을까-즉-undo-버튼을-여러-번-누를-수-있도록-하려면-어떻게-해야-할까">Q)  작업 취소를 할 때 히스토리 기능은 어떻게 구현할 수 있을까? 즉, undo 버튼을 여러 번 누를 수 있도록 하려면 어떻게 해야 할까??</h3>
<p>A) 사실 그리 어려운 일은 아니다. 앞에서는 마지막으로 실행한 커맨드의 레퍼런스만 저장했었는데, 그 대신 전에 실행한 커맨드 자체를 스택에 넣으면 됩니다. 그리고 나서 사용자가 undo 버튼을 누를 때마다 인보커에서 스택 맨 위에 있는 항목을 꺼내서 undo() 메소드를 호출하도록 만들면 된다.</p>
<h3 id="q-파티-모드를-구현할-때-partycommand의-execute-메소드에서-다른-커맨드-객체의-excute를-호출하는-방법을-써도-될까">Q) 파티 모드를 구현할 때 PartyCommand의 execute() 메소드에서 다른 커맨드 객체의 excute()를 호출하는 방법을 써도 될까?</h3>
<p>A) 그렇게 해도 되지만, 그러면 PartyComman에 파티 모드 코드를 직접 넣어야 하는데, 나중에 문제가 생길 수도 있습니다. MacroCommand를 사용하면 PartyCommand에 넣을 커맨드를 동적으로 결정할 수 있기에 유연성이 훨씬 좋아진다. 일반적으로  MacroCommand 만들어서 쓰는 방법이 더 우아한 방법이며, 추가해야 할 코드를 줄이는데도 도움이 된다.</p>
</br>

<h2 id="커맨트-패턴-활용하기">커맨트 패턴 활용하기</h2>
<aside>
💡 요청을 큐에 저장해보자

</aside>

<p>커맨드로 컴퓨테이션의 한 부분(리시버와 일련의 행동)을 패키지로 묶어서 일급 객체 형태로 전달할 수도 있다. 그러면 클라이언트 애플리케이션에서 커맨드 객체를 생성 한 뒤 오랜 시간이 지나도 그 컴퓨테이션을 호출할 수 있다. 심지어 다른 스레드에서 호출할 수도 있다. 이점을 활용해서 커맨드 패턴을 스케줄러나 스레드 풀, 작업 큐와 같은 다양한 작업에 적용할 수 있다.</p>
<p>작업 큐를 떠올려 보자. 큐 한 쪽 끝은 커맨드를 추가할 수 있도록 되어 있고, 다른 쪽 끝에는 커맨드를 처리하는 스레드들이 대기하고 있다. 각 스레드는 우선 execute() 메소드를 호출하고 호출이 완료되면 커맨드 객체를 버리고 새로운 커맨드 객체를 가져옵니다.</p>
<p><img src="https://velog.velcdn.com/images/ljo_0920/post/cc7acc86-d059-4a75-b4ce-437e73246b30/image.png" alt=""></p>
<p>작업 큐 </p>
<ul>
<li>커맨드 인터페이스를 구현하는 객체를 큐에 추가한다.</li>
</ul>
<p>컴퓨테이션(?)</p>
<ul>
<li>컴퓨테이션을 고정된 개수의 스레드로 제한할 수 있다.</li>
</ul>
<p>작업 처리 스레드</p>
<ul>
<li>스레드는 큐에서 커맨드를 하나씩 제겋면서 커맨드의 execute() 메소드를 호출한다.</li>
<li>메소드 실행이 끝나면 다시 큐에서 새로운 커맨드 객체를 가져간다.</li>
</ul>
<p>작업 큐 클래스는 계산 작업을 하는 객체들과 완전히 분리되어 있다. 한 스레드가 한동안 금융 관련 계산을 하다가 잠시 후에는 네트워크로 뭔가를 내려받을 수도 있다. 작업 큐 객체는 전혀 신경쓸 필요가 없다. 큐에 커맨드 패턴을 구현하는 객체를 넣으면 그 객체를 처리하는 스레드가 생기고 자동으로 execute() 메소드가 호출된다.</p>
</br>

<h2 id="커맨드-패턴-더-활용하기">커맨드 패턴 더 활용하기</h2>
<p>어떤 애플리케이션은 모든 행동을 기록해 두었다가 애플리케이션이 다운되었을 때 그 행동을 다시 호출해서 복구할 수 있어야 한다. 커맨드 패턴을 사용하면 <code>store()</code>와 <code>load()</code> 메소드를 추가해서 이런 기능을 구현할 수 있다. 자바에서는 이런 메소드를 객체 직렬화로 구현할 수도 있지만, 직렬화와 관련된 제약 조건 때문에 쉽지 않다.</p>
<p>로그 기록은 어떤 명령을 실행하면서 디스크에 실행 히스토리를 기록하고, 애플리케이션이 다운되면 커맨드 객체를 다시 로딩해서 execute() 메소드를 자동으로 순서대로 실행하는 방식으로 작동한다.</p>
<p>지금까지 예로 든 리모컨에는 이런 로그 기록이 무의미하다. 하지만 데이터가 변경될 때마다 매번 저장할 수 없는 방대한 자료구조를 다루는 애플리케이션에 로그를 사용해서 마지막 체크 포인트 이후로 진행한 모든 작업을 저장한 다음 시스템이 다운되었을 때 최근 수행된 작업을 다시 적용하는 방법으로 사용할 수 있다.</p>
<p>스프레드시트 애플리케이션을 예를 들어 볼까요? 매번 데이터가 변경될 때마다 디스크에 저장하지 않고, 특정 체크 포인트 이후의 모든 행동을 로그에 기록하는 방식으로 복구 시스템을 구축할 수 있다. 더 복잡한 애플리케이션에는 이런 테크닉을 확장해서 일련의 작업에 트랜잭션을 활용해서 모든 작업이 완변하게 처리되도록 하거나, 아무것도 처리되지 않게 롤백되도록 할 수 있다.</p>
</br>

<h2 id="실전-커맨드-패턴">실전 커맨드 패턴</h2>
<p>자바의 스윙 라이브러리에는 사용자 인터페이스 구성 요소에서 발생하는 이벤트에 귀를 기울이는 ActionListener 형태의 옵저버가 어마어마하게 많다는 걸 배웠습니다. 그런데 ActionListener 는 Observer 인터페이스이자 Command 인터페이스이기도 하며, AngelListener와 DevilListenr 클래스는 그냥 Observer가 아니라 구상 Command 클래스이다. 즉, 두 패턴이 한꺼번에 들어가 있는 예제이다.</p>
<pre><code class="language-java">public class SwingObserverEx { // 클라이언트
        JButton button = new JButton(&quot;할까 말까&quot;); // 인보커
    button.addActionListener(new AngelListener());
    button.addActionListener(new DevilListener());
}
    class AngelListener implements ActionListenr { // ActionListenr 커맨드 인터페이스
                public void actionPerformed(ActionEvent event) {
                        System.out.println(&quot;하지마!&quot;) // System 리시버
                }
    }
    class DevlilListener implements ActionListenr { // Angel, DevlilListener  구상 커맨드
                public void actionPerformed(ActionEvent event) {
                        System.out.println(&quot;해!&quot;)
                }
    }</code></pre>
</br>

<h2 id="핵심-정리">핵심 정리</h2>
<ul>
<li>커맨드 패턴을 사용하면 요청하는 객체와 요청을 수행하는 객체를 분리할 수 있다.</li>
<li>이렇게 분리하는 과정의 중심에는 커맨드 객체가 있으며, 이 객체가 행동이 들어있는 리시버를 캡슐화한다.</li>
<li>인보커는 무언가 요청할 때 커맨드 객체의 execute() 메소드를 호출하면 된다.</li>
<li>커맨드는 인보커를 매개변수화할 수 있다. 실행 중에 동적으로 매개변수화를 설정할 수도 있다.</li>
<li>execute() 메소드가 마지막으로 호출되기 전의 상태로 되돌리는 작업 취소 메소드를 구현하면 커맨드 패턴으로 작업 취소 기능을 구현할 수도 있다.</li>
<li>매크로 커맨드는 커맨드를 확장해서 여러 개의 커맨드를 한 번에 호출할 수 있게 해주는 가장 간편한 방법이다. 매크로 커맨드로도 어렵지 않게 작업 취소 기능을 구현할 수 있다.</li>
<li>프로그래밍을 하다 보면 요청을 스스로 처리하는 ‘스마트’ 커맨드 객체를 사용하는 경우도 종종 있다.</li>
<li>커맨드 패턴을 활용해서 로그 및 트랜잭션 시스템을 구현할 수 있다.</li>
</ul>
</br>

<h2 id="객체지향-도구-상자">객체지향 도구 상자</h2>
<ul>
<li>객체지향의 기초(4요소)<ul>
<li>캡슐화</li>
<li>상속</li>
<li>추상화</li>
<li>다형성</li>
</ul>
</li>
<li>객체지향 원칙<ul>
<li>바뀌는 부분을 캡슐화한다.</li>
<li>상속보다는 구성을 활용한다.</li>
<li>구현이 아닌 인터페이스(super type)에 맞춰서 프로그래밍한다.</li>
<li>서로 상호작용을 하는 객체 사이에서는 가능하면 느슨하게 결합하는 디자인을 사용해야 한다.</li>
<li>클래스는 확장에 대해서는 열려 있지만 변경에 대해서는 닫혀 있어야 한다. (OCP)</li>
<li>추상화된 것에 의존하라. 구상 클래스에 의존하지 않도록 한다.</li>
</ul>
</li>
<li>객체지향 패턴<ul>
<li>스트레지티 패턴 : 알고리즘군을 정의하고 각각의 알고리즘을 정의하고 각각을 캡슐화하여 교환해서 사용할 수 있도록 만든다. 전략을 사용하면 알고리즘을 사용하는 클라이언트와는 독립적으로 알고리즘을 변경할 수 있다.</li>
<li>옵저버 패턴 : 한 객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체들한테 연락이 가고 자동으로 내용이 갱신되는 방식으로 일대다 의존성을 정의한다.</li>
<li>데코레이터 패턴: 객체에 추가 요소를 동적으로 더할 수 있습니다. 데코레이터를 사용하면 서브 클래스를 만드는 경우에 비해 훨씬 유연하게 기능을 확장할 수 있습니다.</li>
<li>추상 팩토리 패턴 : 서로 연관된, 또는 의존적인 객체들로 이루어진 제품군을 생성하기 위한 인터페이스를 제공한다. 구상 클래스는 서브 클래스에 의해 만들어진다.</li>
<li>팩토리 메소드 패턴 : 객체를 생성하기 위한 인터페이스를 만든다. 어떤 클래스의 인스턴스를 만들지는 서브클래스에서 결정하도록 한다. 팩토리 메소드를 이용하면 인스턴스를 만드는 일을 서브클래스로 미룰 수 있다.</li>
<li>싱글턴 패턴 : 클래스 인스턴스가 하나만 만들어지도록 하고, 그 인스턴스에 대한 전역 접근을 제공한다.</li>
<li><strong>커맨드 패턴 : 요청 내역을 객체로 캡슐화해서 객체를 서로 다른 요청 내역에 따라 매개변수화할 수 있다. 이러면 요청을 큐에 저장하거나 로그로 기록하거나 작업 취소 기능을 사용할 수 있다.</strong></li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[싱글턴 패턴]]></title>
            <link>https://velog.io/@ljo_0920/%EC%8B%B1%EA%B8%80%ED%84%B4-%ED%8C%A8%ED%84%B4</link>
            <guid>https://velog.io/@ljo_0920/%EC%8B%B1%EA%B8%80%ED%84%B4-%ED%8C%A8%ED%84%B4</guid>
            <pubDate>Sun, 26 Jun 2022 09:48:51 GMT</pubDate>
            <description><![CDATA[<h1 id="싱글턴-패턴">싱글턴 패턴</h1>
<p><a href="https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=582754">헤드 퍼스트 디자인 패턴</a>을 읽고 정리한 글입니다.</p>
<blockquote>
<p>싱글턴 패턴은 해당 클래스의 인스턴스가 하나만 만들어지고, 어디서든지 그 인스턴스에 접근할 수 있도록 하기 위한 패턴이다.</p>
</blockquote>
</br>

<h2 id="고전적인-싱글턴-패턴-구현법">고전적인 싱글턴 패턴 구현법</h2>
<pre><code class="language-java">package singleton.before;

public class Singleton {

    private static Singleton uniqueInstance;

    private Singleton() {}

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

}</code></pre>
</br>

<h2 id="초콜릿-공장">초콜릿 공장</h2>
<p>초콜릿 공장에서는 초콜릿을 끓이는 장치인 초콜릿 보일러를 컴퓨터로 제어한다.</p>
<p>이 보일러에서는 초콜릿과 우유를 받아서 끓이고 초코바를 만드는 단계로 넘겨준다. 여기에 초콜릿 보일러를 제어하기 위한 클래스가 나와 있다. </p>
<pre><code class="language-java">public class ChocolateBoiler {

    private boolean empty;
    private boolean boiled;

    public ChocolateBoiler() {
        this.empty = true;
        this.boiled = false;
    }

    public void fill() {
        if (isEmpty()) {
            empty = false;
            boiled = false;
        }
    }

    public void drain() {
        if (!isEmpty() &amp;&amp; !isBoiled()) {
            empty = true;
        }
    }

    public void boil() {
        if (!isEmpty() &amp;&amp; !isBoiled()) {
            boiled = true;
        }
    }

    public boolean isEmpty() {
        return this.empty;
    }

    public boolean isBoiled() {
        return this.boiled;
    }

}</code></pre>
<ul>
<li>코드를 보면 실수를 하지 않도록 주의를 기울여져 있다.</li>
<li>하지만 두 개의 ChocolateBoiler 인스턴스가 따로 돌아가게 되면 상당히 안 좋은 상황이 일어날 수 있다는 것을 알 수 있다.</li>
<li>만약 애플리케이션에서 ChocolateBoiler 인스턴스가 두 개 이상 만들어지게 되면 어떤 문제가 생길까??<ul>
<li>자원을 불필요하게 잡아먹고, 애플리케이션의 동작이 이상하게 돌아가는 결과에 일관성이 없어지는 심각한 문제가 발생할 것이다.</li>
<li>ChocolateBoiler 클래스를 싱글턴으로 업그레드해보자</li>
</ul>
</li>
</ul>
<pre><code class="language-java">public class ChocolateBoiler {

    private boolean empty;
    private boolean boiled;

    private static ChocolateBoiler uniqueInstance;

    private ChocolateBoiler() {
        this.empty = true;
        this.boiled = false;
    }

    public static ChocolateBoiler getInstance() {
        if (uniqueInstance == null) {
            uniqueInstance = new ChocolateBoiler();
        }
        return uniqueInstance;
    }

    public void fill() {
        if (isEmpty()) {
            empty = false;
            boiled = false;
        }
    }

    public void drain() {
        if (!isEmpty() &amp;&amp; !isBoiled()) {
            empty = true;
        }
    }

    public void boil() {
        if (!isEmpty() &amp;&amp; !isBoiled()) {
            boiled = true;
        }
    }

    public boolean isEmpty() {
        return this.empty;
    }

    public boolean isBoiled() {
        return this.boiled;
    }

}</code></pre>
</br>

<h2 id="싱글턴-패턴의-정의">싱글턴 패턴의 정의</h2>
<p>싱글턴의 고전적인 구현법을 배웠다. 그렇다면 싱글턴 패턴의 정의는 무엇이고, 실제로 어떤 식으로 싱글턴 패턴을 적용해야할까?? </p>
<aside>
💡 싱글턴 패턴은 해당 클래스의 인스턴스가 하나만 만들어지고, 어디서든지 그 인스턴스에 접근할 수 있도록 하기 위한 패턴이다.

</aside>

<ul>
<li>클래스에서 자신의 단 하나뿐인 인스턴스를 관리하도록 만들면 된다.<ul>
<li>그리고 다른 어떤 클래스에서도 자신의 인스턴스를 추가로 만들지 못하도록 해야 한다.</li>
<li>따라서 인스턴스가 필요하면 반드시 클래스 자신을 거치도록 해야 될 것이다.</li>
</ul>
</li>
<li>어디서든 그 인스턴스를 접근할 수 있도록 만들어야 한다.<ul>
<li>다른 객체에서 이 인스턴스가 필요하면 언제든지 클래스한테 요청을 할 수 있게 만들고, 요청이 들어오면 그 하나뿐인 인스턴스를 건네주도록 만들어야 한다.</li>
<li>앞에서 보았듯이, 싱글턴이 Lazy하게 생성되도록 구현할 수도 있다. 싱글턴 클래스의 객체가 자원을 많이 잡아먹는 경우에는 유용하다.</li>
</ul>
</li>
</ul>
</br>    

<h3 id="클래스-다이어그램">클래스 다이어그램</h3>
<p><img src="https://velog.velcdn.com/images/ljo_0920/post/2f405ead-0b09-4418-b3be-e8b589967fbd/image.png" alt=""></p>
<p><a href="https://www.hanbit.co.kr/channel/category/category_view.html?cms_code=CMS8616098823">https://www.hanbit.co.kr/channel/category/category_view.html?cms_code=CMS8616098823</a></p>
</br>

<h2 id="골칫덩어리-스레드">골칫덩어리 스레드</h2>
<p>고전적인 싱글턴을 이용해서 코드를 고쳤음에도 ChocolateBoiler에 있는 fill() 메소드에서 아직 초콜릿이 끓고 있는데 재료를 집어넣고 말았다. 무슨 일이 일어난 것일까??</p>
<ul>
<li>조금 전에 <code>다중 스레드</code>를 사용하도록 ChocolateBoiler 컨트롤러를 최적화시킨 것이 문제일까??</li>
<li>스레드가 추가된 것 때문에 이런 문제가 생긴 것일까??</li>
</ul>
</br>

<h3 id="jvm의-입장">JVM의 입장</h3>
<p>두 개의 스레드에서 여기에 있는 코드를 실행시킨다고 가정해보고 두 스레드가 다른 보일러 객체를 사용하게 될 가능성이 있는지 따져보자.</p>
<pre><code class="language-java">ChocolateBoiler boiler = ChocolateBoiler.getInstacne();
boiler.fill();
boiler.boil();
boiler.drain();</code></pre>
<pre><code class="language-java">public static ChocolateBoiler getInstance() {
      if (uniqueInstance == null) {
          uniqueInstance = new ChocolateBoiler();
      }
      return uniqueInstance;
}</code></pre>
<ul>
<li>바로 두 스레드가 동시에 getInstance() 메소드를 수행하게 되면 uniqueInstance null 상태라 각 스레드마다 ChocolateBoiler 인스턴스를 생성하여 리턴하여 결국 서로 다른 두 인스턴스가 만들어진다.</li>
</ul>
</br>

<h2 id="멀티스레딩-문제-해결-방법">멀티스레딩 문제 해결 방법</h2>
<p>문제를 해결하는 방법은 간단한데 바로 getInstance()를 동기화시키기만 하면 된다.</p>
<pre><code class="language-java">public static synchronized ChocolateBoiler getInstance() {
        if (uniqueInstance == null) {
            uniqueInstance = new ChocolateBoiler();
        }
        return uniqueInstance;
}</code></pre>
<ul>
<li>synchronized 키워드를 추가하면 한 스레드가 메소드 사용을 끝내기 전까지 다른 스레드는 기다려야 한다.</li>
<li>즉, 두 스레드가 이 메소드를 동시에 실행시키는 일은 일어나지 않는다.</li>
<li>하지만 이렇게 하면 동기화로 인한 속도 문제가 생긴다.<ul>
<li>사실 동기화가 꼭 필요한 시점은 이 메소드가 시작되는 때 뿐이다.</li>
<li>즉, 일단 uniqueInstance 변수에 Singleton 인스턴스를 대입하고 나면 굳이 이 메소드를 동기화된 상태로 유지시킬 필요가 없다.</li>
<li>불필요한 오버헤드만 증가시킬뿐인 것</li>
</ul>
</li>
</ul>
</br>

<h2 id="더-효율적인-방법은-없을까">더 효율적인 방법은 없을까?</h2>
<p>대부분의 자바 애플리케이션에서 싱글턴이 다중 스레드 환경에서 돌아갈 수도 있도록 만들어야 한다. 하지만 getInstance() 메소드를 동기화시키려면 대가를 치뤄야 한다. 다른 방법은 없을까??</p>
<h3 id="1-getinstance의-속도가-중요하지-않다면-그냥-둔다">1. getInstance()의 속도가 중요하지 않다면 그냥 둔다.</h3>
<p>만약 getInstance() 메소드가 애플리케이션에 큰 부담을 주지 않는다면 그냥 놔둬도 된다. getInstance()를 동기화시키는게 굉장히 쉽고, 효율 면에서도 나쁘지 않을 수있다. </p>
<p>하지만 메소드를 동기화하면 성능이 100배 정도 저하된다는 것을 기억하자. 만약 getInstance()가 애플리케이션에서 병목으로 작용한다면 다른 방법을 생각해야 한다.</p>
<h3 id="2-인스턴스를-필요할-때-생성하지-말고-처음부터-만들어-버린다">2. 인스턴스를 필요할 때 생성하지 말고, 처음부터 만들어 버린다.</h3>
<p>애플리케이션에서 반드시 Singleton의 인스턴스를 생성하고, 그 인스턴스를 항상 사용한다면, 또는 인스턴스를 실행중에 수시로 만들고 관리하기가 성가시다면 다음과 같은 식으로 처음부터 Singleton 인스턴스를 만들어버리는 것도 괜찮은 방법이다.</p>
<pre><code class="language-java">public class Singleton {

    private static Singleton uniqueInstance = new Singleton();

    private Singleton() {}

    public static Singleton getInstance() {
        return uniqueInstance;
    }

}</code></pre>
<ul>
<li>이런 접근법을 사용하면 클래스가 로딩될 때, JVM에서 Singleton의 유일한 인스턴스를 생성해 주고, JVM에서 유일한 인스턴스를 생성하기 전에는 그 어떤 스레드도 uniqueInstance 정적 변수에 접근할 수 없다.</li>
</ul>
<h3 id="3-dcldouble-checking-locking을-써서-getinstance에서-동기화되는-부분일-줄인다">3. DCL(Double Checking Locking)을 써서 getInstance()에서 동기화되는 부분일 줄인다.</h3>
<p>DCL을 사용하면 , 일단 인스턴스가 생성되어 있는지 확인한 다음, 생성되어 있지 않았을 때만 동기화를 할 수 있다. 이렇게 하면 처음에만 동기화를 하고 나중에는 동기화를 하지 않도록 동작하여, 바로 원하던 동작이 수행된다.</p>
<pre><code class="language-java">public class Singleton {

        // 자바 5 이전 버전은 동기화 x
    private volatile static Singleton uniqueInstance = new Singleton();

    private Singleton() {}

    public static Singleton getInstance() {
        if(uniqueInstance == null) { // 인스턴스가 있는지 확인하고, 없으면 동기화된 블럭으러 진입
            synchronized (Singleton.class) {
                if(uniqueInstance == null) { // 블록으로 들어온 후레도 다시 한번 널체크한 후, 인스턴스를 생성한다.
                    uniqueInstance = new Singleton();
                }
            }
        }
        return uniqueInstance;
    }

}</code></pre>
</br>

<h2 id="싱글턴-관련-qa">싱글턴 관련 Q&amp;A</h2>
<p>Q) 모든 메서드와 변수가 static으로 선언된 클래스를 만들어도 결과적으로는 같지 않을까?</p>
<p>A) 맞다. 하지만 필요한 내용이 클래스에 다 들어있고, 복잡한 초기화가 필요없는 경우에만 해당 방법을 사용할 수 있다. 그리고 자바에서 정적 초기화를 처리하는 방법 때문에 복잡해질 수 있다. 특히 여러 클래스가 얽혀 있는 경우에는 지저분하고, 초기화 순서와 관련된 버그는 찾기 어렵기 때문에 해당 방식으로 싱글턴 비슷한 걸 만들어야 한다면 좋지 않을 수 있다.</p>
<p>Q) 클래스 로더와 관련된 문제는 없을까?</p>
<p>A) 클래스 로더마다 서로 다른 네임스페이스를 정의하기 때문에 클래스 로더가 두 개 이상이라면 같은 클래스를 여러 번 로딩할 수도 있다. 만약 싱글턴을 그런 식으로 로딩하면 인스턴스가 여러 개 만들어지는 문제가 발생할 수 있다. 따라서 클래스 로더를 여러 개 사용하면서 싱글턴을 사용한다면 조심해야 하고, 클래스 로더를 직접 지정해서 문제를 회피할 수도 있다.</p>
<p>Q) 전역 변수가 싱글턴보다 나쁜 이유는 무엇일까??</p>
<p>A) 자바의 전역 변수는 기본적으로 객체에 대한 정적 레퍼런스다. 전역 변수를 이런 식으로 사용한다면 게으른 인스턴스를 사용할 수 없는 단점과 싱글턴 패턴을 쓰는 두 가지 이유 중, 클래스의 인스턴스가 하나만 있을 수 있도록 할 수 없다. 전역 변수를 사용한다면 간단한 객체에 대한 전역 레퍼런스를 자꾸 만들게 도면서 네임스페이스를 지저분한게 만드는 경향이 생긴다. </p>
</br>

<h2 id="핵심-정리">핵심 정리</h2>
<ul>
<li>어떤 클래스를 싱글턴 패턴을 적용하면 애플리케이션에 그 클래스의 인스턴스가 최대 한 개 까지만 있도록 할 수 있다.</li>
<li>싱글턴 패턴을 이용하면 유일한 인스턴스를 어디서든지 접근할 수 있도록 할 수 있다.</li>
<li>자바에서 싱글턴 패턴을 구현할 때는 private 생성자와 정적 메소드, 정적 변수를 사용한다.</li>
<li>다중 스레드를 사용하는 애플리케이션에서는 속도와 자원 문제를 파악해보고 적절한 구현법을 사용해야 한다.<ul>
<li>사실상 멀티스레딩을 기본으로 가정해야한다.</li>
</ul>
</li>
<li>DCL을 사용하는 방법은 자바 2 버전5보다 전에 나온 버전에서는 쓸 수 없다.</li>
<li>클래스 로더가 여러 개 있으면 싱글턴이 제대로 작동하지 않고, 여러 개의 인스턴스가 생길 수 있다.</li>
<li>1.2 버전보다 전에 나온 JVM을 사용하는 경우에는 가바지 컬렉터 관련 버그 때문에 싱글턴 레지스트리를 사용해야 할 수도 있다.</li>
</ul>
</br>

<h2 id="객체지향-도구-상자">객체지향 도구 상자</h2>
<ul>
<li>객체지향의 기초(4요소)<ul>
<li>캡슐화</li>
<li>상속</li>
<li>추상화</li>
<li>다형성</li>
</ul>
</li>
<li>객체지향 원칙<ul>
<li>바뀌는 부분을 캡슐화한다.</li>
<li>상속보다는 구성을 활용한다.</li>
<li>구현이 아닌 인터페이스(super type)에 맞춰서 프로그래밍한다.</li>
<li>서로 상호작용을 하는 객체 사이에서는 가능하면 느슨하게 결합하는 디자인을 사용해야 한다.</li>
<li>클래스는 확장에 대해서는 열려 있지만 변경에 대해서는 닫혀 있어야 한다. (OCP)</li>
<li>추상화된 것에 의존하라. 구상 클래스에 의존하지 않도록 한다.</li>
</ul>
</li>
<li>객체지향 패턴<ul>
<li>스트레지티 패턴 : 알고리즘군을 정의하고 각각의 알고리즘을 정의하고 각각을 캡슐화하여 교환해서 사용할 수 있도록 만든다. 전략을 사용하면 알고리즘을 사용하는 클라이언트와는 독립적으로 알고리즘을 변경할 수 있다.</li>
<li>옵저버 패턴 : 한 객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체들한테 연락이 가고 자동으로 내용이 갱신되는 방식으로 일대다 의존성을 정의한다.</li>
<li>데코레이터 패턴: 객체에 추가 요소를 동적으로 더할 수 있습니다. 데코레이터를 사용하면 서브 클래스를 만드는 경우에 비해 훨씬 유연하게 기능을 확장할 수 있습니다.</li>
<li>추상 팩토리 패턴 : 서로 연관된, 또는 의존적인 객체들로 이루어진 제품군을 생성하기 위한 인터페이스를 제공한다. 구상 클래스는 서브 클래스에 의해 만들어진다.</li>
<li>팩토리 메소드 패턴 : 객체를 생성하기 위한 인터페이스를 만든다. 어떤 클래스의 인스턴스를 만들지는 서브클래스에서 결정하도록 한다. 팩토리 메소드를 이용하면 인스턴스를 만드는 일을 서브클래스로 미룰 수 있다.</li>
<li><strong>싱글턴 패턴 : 클래스 인스턴스가 하나만 만들어지도록 하고, 그 인스턴스에 대한 전역 접근을 제공한다.</strong></li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[팩토리 패턴 - 추상 팩토리]]></title>
            <link>https://velog.io/@ljo_0920/%ED%8C%A9%ED%86%A0%EB%A6%AC-%ED%8C%A8%ED%84%B4-%EC%B6%94%EC%83%81-%ED%8C%A9%ED%86%A0%EB%A6%AC</link>
            <guid>https://velog.io/@ljo_0920/%ED%8C%A9%ED%86%A0%EB%A6%AC-%ED%8C%A8%ED%84%B4-%EC%B6%94%EC%83%81-%ED%8C%A9%ED%86%A0%EB%A6%AC</guid>
            <pubDate>Mon, 20 Jun 2022 20:04:31 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=582754">헤드 퍼스트 디자인 패턴</a>을 읽고 정리한 글입니다.</p>
<ul>
<li>느슨한 결합을 이용하는 객체지향 디자인, 객체의 인스턴스를 만드는 작업이 항상 공개되어 있어야 하는 것은 아니며, 오히려 결합과 관련된 문제가 생길 수 있다. 팩토리 패턴을 이용하여 불필요한 의존성을 없애보자</li>
</ul>
<blockquote>
<p>추상 팩토리 패턴에서는 인터페이스를 이용하여 서로 연관된, 또는 의존하는 객체를 구상 클래스를 지정하지 않고도 생성할 수 있다.</p>
</blockquote>
</br>

<h2 id="원재료군">원재료군</h2>
<p>뉴옥과 시카고에서 사용하는 재료 종류는 서로 다르다. PizzaStore 분점이 점점 생기면서 해당 분점들은 또 다른 자신들만의 재료들을 사용해야 될 것이다.</p>
<p>이렇게 서로 다른 종유릐 재료들을 제공하기 위해 원재료군을 처리할 방법을 생각해야한다.</p>
</br>

<h2 id="원재료-공장-만들기">원재료 공장 만들기</h2>
<p>따라서 이제 원재료를  생산하기 위한 공장을 만들어보자.</p>
<p>원재료 공장에서는 원재료군에 들어있는 반죽, 소스, 치즈 같은 각각의 원재료를 생산한다.</p>
<p>우선 모든 원재료를 생산할 팩토리를 위한 인터페이스를 정의해보자</p>
<pre><code class="language-java">public interface PizzaIngredientFactory {

    Dough createDough();

    Sauce createSauce();

    Cheese createCheese();

    Veggies[] createVeggies();

    Pepperoni createPepperoni();

    Clams createClam();

}</code></pre>
<pre><code class="language-java">public class NYPizzaIngredientFactory implements PizzaIngredientFactory {

    @Override
    public Dough createDough() {
        return new ThinCrustDough();
    }

    @Override
    public Sauce createSauce() {
        return new MarinaraSauce();
    }

    @Override
    public Cheese createCheese() {
        return new ReggianoCheese();
    }

    ...
}</code></pre>
</br>

<h2 id="피자-클래스-변경">피자 클래스 변경</h2>
<p>원재료 팩토리가 준비되고 Pizza 클래스에서 팩토리에서 생산한 원재료만 사용하도록 코드를 수정한다.</p>
<pre><code class="language-java">public abstract class Pizza {

    String name;
    Dough dough;
    Sauce sauce;
    Cheese cheese;
        ...

    public void box() {
        System.out.println(&quot;포장&quot;);
    }

    public void cut() {
        System.out.println(&quot;커팅&quot;);
    }

    public void bake() {
        System.out.println(&quot;굽기&quot;);
    }

    abstract void prepare();
}</code></pre>
<ul>
<li>prepare() 메서드를 제외한 다른 메서드들은 바뀌지 않는다.</li>
</ul>
</br>

<pre><code class="language-java">public class CheesePizza extends Pizza {

    private final PizzaIngredientFactory pizzaIngredientFactory;

    public CheesePizza(PizzaIngredientFactory pizzaIngredientFactory) { // 생성자를 통해 원재료를 제공하는 팩토리를 주입받는다.
        this.pizzaIngredientFactory = pizzaIngredientFactory;
    }

    @Override
    void prepare() { // 팩토리가 작동하는 부분
        dough = pizzaIngredientFactory.createDough();
        sauce = pizzaIngredientFactory.createSauce();
        cheese = pizzaIngredientFactory.createCheese();
    }
}</code></pre>
<p>앞선 예제에서 <code>NYStyleCheesePizza</code> , <code>ChicagoStyleCheesePizza</code> 클래스를 기억해보자. 그 두 클래스를 살펴보면 지역별로 다른 재료를 사용한다는 것만 빼면 다른 점이 없다. </p>
<ul>
<li>따라서 피자마다 클래스를 지역별로 따로 만들 필요가 없다. 지역별로 다른 점은 원재료 공장에서 처리하기 때문</li>
</ul>
<p>이제 피자 코드에서는 팩토리를 이용하여 피자에서 쓰이는 재료를 만든다.</p>
<ul>
<li>만들어지는 재료는 어떤 팩토리를 쓰는지에 따라 달라지며 피자 클래스에서는 전혀 신경을 쓰지 않는다.</li>
<li>이제 피자 클래스와 지역별 재료가 분리되어 있기 때문에 어떤 지역의 재료 팩토리를 사용하든 피자 클래스는 그대로 재사용할 수 있다.</li>
</ul>
</br>

<h2 id="마찬가지로-피자-가게를-수정해보자">마찬가지로 피자 가게를 수정해보자</h2>
<pre><code class="language-java">public class NYPizzaStore extends PizzaStore {

    @Override
    protected Pizza createPizza(String type) {
        Pizza pizza = null;
        PizzaIngredientFactory ingredientFactory = new NYPizzaIngredientFactory();

        if (type.equals(&quot;cheese&quot;)) {
            pizza = new CheesePizza(ingredientFactory);
        } else if (type.equals(&quot;greek&quot;)) {
            pizza = new GreekPizza(ingredientFactory);
        }
        return pizza;
    }
}</code></pre>
<ul>
<li>뉴욕 피자 가게에서는 뉴욕 피자 원재료 공장을 주입시켜 준다.</li>
</ul>
</br>

<h2 id="정리">정리</h2>
<p>기존 팩토리 패턴에서 추상 팩토리라고 부르는 새로운 형식의 팩토리를 도입해서 서로 다른 피자에서 필요로 하는 원재료군을 생산하기 위한 방법을 구축했다.</p>
<ul>
<li>추상 팩토리를 통해서 제품군을 생성하기 위한 인터페이스를 제공할 수 있다.</li>
<li>이 인터페이스를 이용하는 코드를 만들면 코드를 제품을 생산하는 실제 팩토리와 분리시킬 수 있다.</li>
<li>이렇게 함으로써 서로 다른 상황별로 적당한 제품을 생산할 수 있는 다양한 팩토리를 구현할 수 있게 된다.</li>
</ul>
</br>

<h2 id="추상-팩토리-패턴-정의">추상 팩토리 패턴 정의</h2>
<p>제품군을 만들 때 쓸 수 있는 추상 팩터리 패턴에서는 인터페이스를 이용하여 서로 연관된, 또는 의존하는 객체를 구상 클래스를 지정하지 않고도 생성할 수 있다.</p>
<ul>
<li>추상 팩토리 패턴을 사용하면 클라이언트에서 추상 인터페이스를 통해서 일련의 제품들을 공급받을 수 있다.</li>
<li>이 때, 실제로 어떤 제이품이 생산되는지 전혀 알 필요가 없다.</li>
<li>따라서 클라이언트와 팩토리에서 생산되는 제품을 분리시킬 수 있다.</li>
</ul>
</br>

<h3 id="클래스-다이어그램">클래스 다이어그램</h3>
<p><img src="https://velog.velcdn.com/images/ljo_0920/post/34fde5d8-4cad-4b17-933a-aa91932ec1e1/image.png" alt="">
<img src="https://velog.velcdn.com/images/ljo_0920/post/5316b0dd-5269-468c-b0eb-eb65b14fb40c/image.png" alt=""></p>
</br>

<h2 id="추상-팩토리-패턴과-팩토리-메서드-패턴의-차이">추상 팩토리 패턴과 팩토리 메서드 패턴의 차이</h2>
<p>추상 팩토리 패턴에 있는 <code>createDough()</code>, <code>createSauce()</code> 같은 메서드는 전부 팩토리 메서드 같이 보인다.</p>
<p>그렇다면 추상 팩토리 패턴 뒤에는 팩토리 메서드 패턴이 숨어져 있는 것일까?</p>
<ul>
<li>각 메서드는 추상 메서드로 선언되어 있고, 서브 클래스에서 메소드를 오버라이드해서 객체를 만드는 방식이기 때문</li>
<li>추상 팩터리가 일련의 제품들을 생성하는데 쓰일 인터페이스를 정의하기 위해 만들어진 것이므로, 해당 인터페이스에 있는 메서드는 구상 제품을 만드는 일을 맡고 있고, 추상 팩토리의 서브클래스를 만들어서 각 메서드의 구현을 제공한다.</li>
<li>따라서 추상 팩토리 패턴에서 제품을 생성하기 위한 메서드를 구현하는데 있어서 팩토리 메서드를 사용하는것은 자연스러운 일이다.</li>
</ul>
<p>하지만 팩토리 메서드 패턴은 상속을 통해 객체를 생성하고 추상 팩토리 패턴은 객체 구성을 통해 객체를 생성한다.</p>
<p>또한 추상 팩토리 패턴에서는 제품군에 제품을 추가하는 식으로 관련 제품들을 확대해야 하는 경우에 인터페이스를 수정해야 하지만 팩토리 메서드 패턴에서는 한 가지 제품만 생산하므로 복잡한 인터페이스도 필요하지 않고, 메서드도 하나만 있으면 된다.</p>
<ul>
<li>추상 팩토리 패턴은 클라이언트에서 서로 연관된 제품군을 만들어야 할 때</li>
<li>팩토레 메소드 패턴은 클라이언트 코드와 인스턴스를 만들어야 할 구상 클래스를 분리시켜야하거나, 어떤 구상 클래스를 필요로 하게 될지 미리 알 수 없는 경우에 매우 유용하다.</li>
</ul>
</br>

<h2 id="핵심-정리">핵심 정리</h2>
<ul>
<li>팩토리를 쓰면 객체 생성을 캡슐화할 수 있다.</li>
<li>간단한 팩토리는 엄밀히 디자인 패턴은 아니지만, 클라이언트와 구상 클래스를 분리시키기 위한 간단한 기법으로 활용 가능하다.</li>
<li>팩토리 메소드 패턴에서는 상속을 활용한다. 객체 생성이 서브클래스에게 위임된다. 서브 클래스에서는 팩토리 메소드를 구현하여 객체를 생산한다.</li>
<li>추상  팩토리 패턴에서는 객체 구성을 활용한다. 객체 생성이 팩토리 인터페이스에서 선언한 메소스들에서 구현된다.</li>
<li>모든 팩토리 패턴에서는 애플리케이션의 구상 클래스에 대한 의존성을 줄여줌으로써 느슨한 결합을 도와준다.</li>
<li>추상 팩토리 패턴은 구상 클래스에 직접 의존하지 않고도 서로 관련된 객체들로 이루어진 제품군을 만들기 위한 용도로 쓰인다.</li>
<li>DIP에 따르면 구상 형식에 대한 의존을 피하고 추상화를 지향할 수 있다.</li>
<li>팩토리는 구상 클래스가 아닌 추상 클래스, 인터페이스에 맞춰 코딩할 수 있게 해주는 강력한 기법이다.</li>
</ul>
</br>

<h2 id="객체지향-도구-상자">객체지향 도구 상자</h2>
<ul>
<li>객체지향의 기초(4요소)<ul>
<li>캡슐화</li>
<li>상속</li>
<li>추상화</li>
<li>다형성</li>
</ul>
</li>
<li>객체지향 원칙<ul>
<li>바뀌는 부분을 캡슐화한다.</li>
<li>상속보다는 구성을 활용한다.</li>
<li>구현이 아닌 인터페이스(super type)에 맞춰서 프로그래밍한다.</li>
<li>서로 상호작용을 하는 객체 사이에서는 가능하면 느슨하게 결합하는 디자인을 사용해야 한다.</li>
<li>클래스는 확장에 대해서는 열려 있지만 변경에 대해서는 닫혀 있어야 한다. (OCP)</li>
<li><strong>추상화된 것에 의존하라. 구상 클래스에 의존하지 않도록 한다.</strong></li>
</ul>
</li>
<li>객체지향 패턴<ul>
<li>스트레지티 패턴 : 알고리즘군을 정의하고 각각의 알고리즘을 정의하고 각각을 캡슐화하여 교환해서 사용할 수 있도록 만든다. 전략을 사용하면 알고리즘을 사용하는 클라이언트와는 독립적으로 알고리즘을 변경할 수 있다.</li>
<li>옵저버 패턴 : 한 객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체들한테 연락이 가고 자동으로 내용이 갱신되는 방식으로 일대다 의존성을 정의한다.</li>
<li>데코레이터 패턴: 객체에 추가 요소를 동적으로 더할 수 있습니다. 데코레이터를 사용하면 서브 클래스를 만드는 경우에 비해 훨씬 유연하게 기능을 확장할 수 있습니다.</li>
<li><strong>추상 팩토리 패턴 : 서로 연관된, 또는 의존적인 객체들로 이루어진 제품군을 생성하기 위한 인터페이스를 제공한다. 구상 클래스는 서브 클래스에 의해 만들어진다.</strong></li>
<li><strong>팩토리 메소드 패턴 : 객체를 생성하기 위한 인터페이스를 만든다. 어떤 클래스의 인스턴스를 만들지는 서브클래스에서 결정하도록 한다. 팩토리 메소드를 이용하면 인스턴스를 만드는 일을 서브클래스로 미룰 수 있다.</strong></li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[팩토리 패턴 - 팩토리 메서드]]></title>
            <link>https://velog.io/@ljo_0920/%ED%8C%A9%ED%86%A0%EB%A6%AC-%ED%8C%A8%ED%84%B4-%ED%8C%A9%ED%86%A0%EB%A6%AC-%EB%A9%94%EC%84%9C%EB%93%9C</link>
            <guid>https://velog.io/@ljo_0920/%ED%8C%A9%ED%86%A0%EB%A6%AC-%ED%8C%A8%ED%84%B4-%ED%8C%A9%ED%86%A0%EB%A6%AC-%EB%A9%94%EC%84%9C%EB%93%9C</guid>
            <pubDate>Sat, 28 May 2022 07:19:54 GMT</pubDate>
            <description><![CDATA[<h1 id="팩토리-패턴">팩토리 패턴</h1>
<p><a href="https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=582754">헤드 퍼스트 디자인 패턴</a>을 읽고 정리한 글입니다.</p>
<ul>
<li>느슨한 결합을 이용하는 객체지향 디자인, 객체의 인스턴스를 만드는 작업이 항상 공개되어 있어야 하는 것은 아니며, 오히려 결합과 관련된 문제가 생길 수 있다. 팩토리 패턴을 이용하여 불필요한 의존성을 없애보자</li>
</ul>
<blockquote>
<p>팩토리 메소드 패턴에서는 객체를 생성하기 위한 인터페이스를 정의하는데, 어떤 클래스의 인스턴스를 만들지는 서브클래스에서 결정하게 만든다. 팩토리 메소드 패턴을 이용하면 클래스의 인스턴스를 만드는 일을 서브클래스에게 맡기는 것</p>
</blockquote>
</br>

<h3 id="new는-구상-객체를-뜻한다">“new”는 “구상 객체”를 뜻한다.</h3>
<p>new를 사용하는 것은 구상 클래스의 인스턴스를 만드는 것이다. 당연히 인터페이스가 아닌 특정 구현을 사용하는 것, 앞에서 디자인 패턴을 공부하면서 구상 클래스를 바탕으로 코딩을 하게 되면 코드를 수정해야 할 가능성이 높아지고, 유연성이 떨어지는 것을 볼 수 있었다.</p>
<pre><code class="language-java">Duck duck = new MallardDuck();</code></pre>
<p>일련의 구상 클래스들이 존재하는 경우, 다음과 같은 코드를 만들어야 하는 경우가 있다.</p>
<pre><code class="language-java">Duck duck;

// 컴파일시에는 어떤 것의 인스턴스를 만들어야 할지 알 수 없다.
if(picnic) {
        duck = new MallardDuck();
} else if() {
        duck = new DecoyDuck();
} else if() {
        duck = new RubberDuck();
}</code></pre>
<p>이런 코드가 있다는 것은, 변경하거나 확장해야 할 때 코드를 다시 확인하고 추가 또는 제거해야 한다는 것을 뜻한다. 따라서 코드를 이런식으로 만들면 관리 및 갱신하기가 어려워지고, 오류가 생길 가능성이 높아지게 된다.</p>
<p>사실 “new” 자체에 문제가 있는 것은 아니다. 가장 문제가 되는 점은 “변경”이다. 뭔가 변경되는 것 때문에 new를 </p>
<p>사용하는데 있어서 조심해야 하는 것이다.</p>
<p>그렇기 때문에 인터페이스에 맞춰서 코딩을 하면 다형성 덕분에 어떤 클래스든 특정 인터페이스만 구현하면 사용할 수 있기 때문에 여러 변경에 대해 유연함을 가질 수 있는 것이다. 반대로 구상 클래스를 많이 사용하면 새로운 구상 클래스가 추가될 때마다 코드를 고쳐야 하기 때문에 많은 문제가 생길 수 있다. 즉 OCP 원칙</p>
<aside>
💡 어떻게 하면 애플리케이션에서 구상 클래스의 인스턴스를 만드는 부분을 전부 찾아내서 애플리케이션의 나머지 부분으로부터 분리 및 캡슐화 시킬 수 있을까?

</aside>

</br>

<h3 id="바뀌는-부분을-찾아보자">바뀌는 부분을 찾아보자</h3>
<p>피자 가게를 운영하고 있다고 생각해보자. </p>
<p>피자의 종류가 다양할 것이나, 새로운 피자 신메뉴를 출시하거나 메뉴가 사라질 수 있을 것이다.</p>
<p>따라서 oderPizza() 메소드에서 가장 문제가 되는 부분은 바로 인스턴스를 만들 구상 클래스를 선택하는 부분이다. 해당 부분 때문에 변화에 따라 코드를 변경할 수 밖에 없다. 이제 바뀌는 부분과 바뀌지 않는 부분을 파악했으니 캡슐화할 차례이다.</p>
<pre><code class="language-java">Pizza orderPizza(String type) {
        Pizza pizza = null; // 인터페이스

                // 바뀌는 부분
        if (type.equals(&quot;cheese&quot;)) {
            pizza = new CheesePizza();
        } else if (type.equals(&quot;greek&quot;)) {
            pizza = new GreekPizza();
        } else if (type.equals(&quot;pepperoni&quot;)) {
            pizza = new PepperoniPizza();
        }

                // 바뀌지 않는 부분
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();

        return pizza;
    }</code></pre>
</br>

<h3 id="객체-생성-부분을-캡슐화해보자">객체 생성 부분을 캡슐화해보자</h3>
<p>이제 객체를 생성하는 부분을 메소드에서 뽑아내어 피자 객체를 만드는 일만 전담하는 다른 객체에 집어넣어 보자.</p>
<p>피자를 만드는 일만 처리하는 객체를 집어 넣어 다른 객체에서 피자를 만들어야 하는 일이 있으면 해당 객체에게 부탁하는 객체 추가해보자. 새로 만들 객체에는 팩토리라는 이름을 붙이기로 하자. SimplePizzaFactory를 만들고 나면 orderPizza() 메소드는 새로 만든 객체의 클라이언트가 된다. 즉 새로 만든 객체를 호출하는 것. 피자 공장에 피자 하나 만들어 달라고 부탁한다고 생각하면 쉽다. </p>
</br>

<h3 id="simplepizzafactory를-추가하자">SimplePizzaFactory를 추가하자</h3>
<p>피자 객체 생성을 전달한 클래스를 정의한다.</p>
<pre><code class="language-java">// 해당 클래스에서 하는 일은 클라이언트를 위해 피자를 만들어 주는 일 뿐이다.
public class SimplePizzaFactory {

    public Pizza createPizza(String type) {
        Pizza pizza = null;

        if (type.equals(&quot;cheese&quot;)) {
            pizza = new CheesePizza();
        } else if (type.equals(&quot;greek&quot;)) {
            pizza = new GreekPizza();
        } else if (type.equals(&quot;pepperoni&quot;)) {
            pizza = new PepperoniPizza();
        }
        return pizza;
    }

}</code></pre>
<p><strong>Q) 이렇게 하면 어떤 장점이 있는 것일까?? 얼핏 보면 아까 문제를 다른 객체로 넘겨 버린 것 처럼 보일 수 있어 보인다.</strong></p>
<ul>
<li>SimplePizzaFactory를 사용하는 클라이언트가 매우 많을 수 있는  점을 생각 하자</li>
<li>피자 객체를 받아서 가격, 설명 등을 찾아서 활용하는 클래스 또는 피자 주문을 처리하는 클래스에서도 이 팩토리를 사용할 수 있을 것이다.</li>
<li>따라서 피자리를 생성하는 작업을 한 클래스에 캡슐화시켜 놓으면 구현을 변경해야 하는 경우에 여기저기 다 들어가서 고칠 필요 없이 팩토리 클래스 하나만 고치면 된다.</li>
<li>추후 클라이언트 코드에서 구상 클래스의 인스턴스를 만드는 코드를 없애는 작업 진행</li>
</ul>
<p><strong>Q) 비슷한 식으로 메소드를 정적 메소드를 선언한 디자인(정적 팩터리 메소드)과 차이점은 무엇일까?</strong></p>
<ul>
<li>정적 팩토리 메서드를 사용하면 객체를 생성하기 위한 메소드를 실행시키기 위해서 객체의 인스턴스를 만들지 않아도 되기 때문에 간단한 팩토리를 정적 메소드를 정의하는 기법도 일반적으로 많이 쓰인다.</li>
<li>하지만 서브클래스를 만들어서 객체 생성 메소드의 행동을 변경시킬 수 없다는 단점이 존재한다.</li>
</ul>
</br>

<h3 id="간단한-팩토리를-이용한-pizzastore-수정">간단한 팩토리를 이용한 PizzaStore 수정</h3>
<pre><code class="language-java">public class PizzaStore {

    SimplePizzaFactory simplePizzaFactory;

    public PizzaStore(SimplePizzaFactory simplePizzaFactory) {
        this.simplePizzaFactory = simplePizzaFactory;
    }

    Pizza orderPizza(String type) {
        Pizza pizza = simplePizzaFactory.createPizza(type);

        // 바뀌지 않는 부분
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();

        return pizza;
    }

}</code></pre>
<p><img src="https://velog.velcdn.com/images/ljo_0920/post/b48be19f-e81f-4d8f-aea3-84d3302b59c6/image.jpeg" alt=""></p>
<p>PizzaStore </p>
<ul>
<li>팩토리를 사용하는 클라이언트</li>
<li>팩토리를 통해 피자 인스턴스를 받게 된다.</li>
</ul>
<p>SimplePizzaFactory</p>
<ul>
<li>피자 객체를 만드는 팩토리</li>
<li>애플리케이션에서 유일하게 구상 Pizza 클래스를 직접 참조한다.</li>
</ul>
<aside>
💡 이렇게 간단한 팩토리를 이용해 보았다. 간단한 팩토리는 디자인 패턴이라고 할 수 없고, 관용구에 가깝다.  이제 팩토리에 해당하는 두 가지 강력한 패턴을 알아보자.

</aside>

</br>

<h2 id="팩토리-메서드">팩토리 메서드</h2>
<p>피자 가게가 큰 성공을 거두어 여러 지점을 가지게 되었고, 지역별로 조금씩 다른 차이점이 존재했고, 각각 지역의 특성을 반영하여 피자를 만들어야 했다. 이런 차이점을 어떤 식으로 적용해야 할까??</p>
<p>간단하게 생각하면 SimplePizzaFactory를 빼고 지역별 피자 팩토리(NYPizzaFactory, ChicagoPizzaFactory)를 만든 다음, PizzaStore에서 해당하는 팩토리를 사용하도록 하면 될 것이다.</p>
<pre><code class="language-java">NYPizzaFactory nyFactory = new NYPizzaFactory();
PizzaStore nyStore = new PizzaStore(nyFactory);
nyStore.orderPizza(&quot;Veggie&quot;);

ChicagoPizzaFactory chicagoFactory = new ChicagoPizzaFactory();
PizzaStore chicagoStore = new PizzaStore(chicagoFactory);
chicagoStore.orderPizza(&quot;Veggie&quot;);</code></pre>
<aside>
💡 해당 팩토리를 사용하여 피자를 만들었지만 지역별로 피자를 만들 때, 독자적인 방법들을 사용하기 시작했다. 이런 문제를 해결하기 위해 피자 가게와 피자 제작 과정 전체를 하나로 묶어주는 프레임워크의 필요성과 어떻게 하면 유연성을 잃지 않게 묶을 수 있을까?? **바로 서브클래스에서 결정하게 하는 것!!**

</aside>

</br>

<h3 id="피자-가게-프레임워크---팩토리-메소드-선언">피자 가게 프레임워크 - 팩토리 메소드 선언</h3>
<p>피자를 만드는 활동 자체를 전부 PizzaStore 클래스에 국한시키면서도 분점마다 고유의 스타일을 살릴 수 있도록 하기 위해서 createPizza() 메소드를 다시 PizzaStore에 집어 넣고 <strong>추상 메서드</strong>로 선언하고, 각 지역마다 고유의 스타일에 맞게 PizzaStore의 분점을 나타내는 서브클래스를 만들도록 할 것이다. 즉 피자의 스타일은 각 <strong>서브클래스에서 결정</strong>하는 것</p>
<pre><code class="language-java">public abstract class PizzaStore {

    Pizza orderPizza(String type) {

        Pizza pizza = createPizza(type); // 팩토리 객체가 아닌 메소드 호출

        // 바뀌지 않는 부분
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();

        return pizza;
    }

    // 팩토리 객체 대신 해당 &quot;메소드&quot; 사용
    // &quot;팩토리 메소드&quot;가 추상 메소드로 바뀌었다.
    abstract Pizza createPizza(String type);

}</code></pre>
</br>

<h3 id="서브클래스에서-결정되는-것">서브클래스에서 결정되는 것</h3>
<p><img src="https://velog.velcdn.com/images/ljo_0920/post/f8f491b9-6005-4de5-8c99-76456d25797f/image.png" alt=""></p>
<p>각 서브클래스에서 달라질 수 있는 것은 피자의 만들 때의 스타일 뿐이다. 이렇게 달라지는 점들을 createPizza() 메소드에 집어넣고 그 메소드에서 해당 스타일의 피자를 만드는 것을 모두 책임진다. PizzaStore의 서브클래스에서 createPizza() 메소드를 구현하도록 하면 PizzaStore 프레임워크에 충실하면서도 각각의 스타일을 제대로 구현할 수 있는 PizzaStore 서브클래스들을 구비할 수 있다.</p>
<ul>
<li>PizzaStore에서 정의한 메서드를 서브클래스에서 고쳐서 쓸 수 없게 하고 싶다면 메서드를 final로 선언할 수도 있다.</li>
<li>ex) final Pizza orderPizza(String type)</li>
</ul>
<pre><code class="language-java">public class NYPizzaStore extends PizzaStore {

    @Override
    Pizza createPizza(String type) {
        Pizza pizza = null;

        if (type.equals(&quot;cheese&quot;)) {
            pizza = new NYStyleCheesePizza();
        } else if (type.equals(&quot;greek&quot;)) {
            pizza = new NYStyleGreekPizza();
        }
        return pizza;
    }
}

public class ChicagoPizzaStore extends PizzaStore {

    @Override
    Pizza createPizza(String type) {
        Pizza pizza = null;

        if (type.equals(&quot;cheese&quot;)) {
            pizza = new ChicagoStyleCheesePizza();
        } else if (type.equals(&quot;greek&quot;)) {
            pizza = new ChicagoStyleGreekPizza();
        }
        return pizza;
    }
}</code></pre>
<ul>
<li>슈퍼클래스에 있는 orderPizza() 메소드에서는  Pizza 객체를 가지고 여러 작업을 하긴 하지만, Pizza는 추상 클래스기 때문에 어떤 구상 클래스에서 작업이 처리되고 있는지 전혀 알 수없다.<ul>
<li>즉 PizzaStore와 Pizza는 서로 완전히 분리되어 있다.</li>
<li>그렇다면 피자 종류를 결정하는 것은 누구일까??</li>
</ul>
</li>
<li>orderPizza() 입장에서 볼 때는 PizzaStore 서브클래스에서 피자 종류를 결정한다고 할 수 있을 것이다.<ul>
<li>따라서 서브 클래스에서 실제로  뭔가를 <strong>“결정”</strong>하는 것이 아니라, 우리가 선택하는 PizzaStore의 서브클래스 종류에 따라 결정되는 것이지만,  만들어지는 피자의 종류를 해당 PizzaStore 서브클래스에서 결정한다고 할 수 있다.</li>
</ul>
</li>
</ul>
</br>

<h3 id="팩토리-메소드">팩토리 메소드</h3>
<pre><code class="language-java">abstract Product factoryMethod(String type);</code></pre>
<p>팩토리 메소드는 객체 생성을 처리하며, 팩토리 메소드를 이용하면 객체를 생성하는 작업을 서브클래스에 캡슐화시킬 수 있다. 이렇게 하면 슈퍼 클래스에 있는 클라이언트 코드와 서브클래스에 있는 객체 생성 코드를 분리시킬 수 있다.</p>
<ul>
<li>abstract<ul>
<li>팩토리 메소드는 추상 메소드로 선언하여 서브클래스에서 객체 생성을 책임지도록 한다.</li>
</ul>
</li>
<li>Product<ul>
<li>팩토리 메소드에서는 특정 객체를 리턴하며, 그 객체는 보통 수퍼 클래스에서 정의한 메소드 내에서 쓰이게 된다.</li>
</ul>
</li>
<li>factoryMethod<ul>
<li>팩토리 메소드는 클라이언트 (ex : orderPizza method)에서 실제로 생성되는 구상 객체가 무엇인지 알 수 없게 만드는 역할도 한다.</li>
</ul>
</li>
<li>type<ul>
<li>팩토리 메소드를 만들 때 매개변수를 써서 만들어낼 객체 종류를 선택할 수도 있다.</li>
</ul>
</li>
</ul>
</br>

<h3 id="사용-code">사용 code</h3>
<pre><code class="language-java">public class Main {

    public static void main(String[] args) {
        PizzaStore nyStore = new NYPizzaStore();
        PizzaStore chicagoStore = new ChicagoPizzaStore();

        Pizza pizza = nyStore.orderPizza(&quot;cheese&quot;);
        pizza = chicagoStore.orderPizza(&quot;cheese&quot;);

    }

}</code></pre>
</br>

<h2 id="팩토리-메소드-패턴">팩토리 메소드 패턴</h2>
<blockquote>
<p>팩토리 메소드 패턴에서는 객체를 생성하기 위한 인터페이스를 정의하는데, 어떤 클래스의 인스턴스를 만들지는 서브클래스에서 결정하게 만든다. 팩토리 메소드 패턴을 이용하면 클래스의 인스턴스를 만드는 일을 서브클래스에게 맡기는 것</p>
</blockquote>
<ul>
<li>모든 팩토리 패턴에서는 객체 생성을 캡슐화한다.</li>
<li>팩토리 메소드 패턴에서는 서브 클래스에서 어떤 클래스를 만들지를 결정하게 함으로써 객체 생성을 캡슐화한다.</li>
<li><strong>즉 팩토리 메서드 패턴에서는 객체를 생성하기 위한 인터페이스를 정의하는데, 어떤 클래스의 인스턴스를 만들지는 서브클래스에서 결정하게 만든다.</strong></li>
<li><strong>팩토리 메소드 패턴을 이용하면 클래스의 인스턴스를 만드는 일을 서브클래스에게 맡기는 것</strong></li>
</ul>
<aside>
💡 여기에서 결정한다 라는 표현을 쓰는 이유는, 서브클래스에서 실행중에 어떤 클래스의 인스턴스를 만들지를 결정하기 때문이 아니라, 생산자 클래스 자체가 실제 생산될 제품에 대한 사전 지식이 전혀 없이 만들어지기 때문이다. 즉 사용하는 서브클래스에 따라 생산되는 객체 인스턴스가 결정되는 것이다.

</aside>

<p>클래스 다이어그램을 살펴보자</p>
<p><img src="https://velog.velcdn.com/images/ljo_0920/post/4ed27366-6a06-4cb4-a702-0cc668a98bba/image.png" alt=""></p>
<p>Creator : 생산자</p>
<ul>
<li>ex) PizzaStore Class</li>
<li>제품을 가지고 원하는 일을 하기 위한 모든 메소드들이 구현되어 있다.</li>
<li>하지만 제품을 만들어 주는 팩토리 메소드는 추상 메소드로 정의되어 있을 뿐, 구현되어 있진 않다.</li>
<li>Creator의 모든 서브 클래스에서 factoryMethod() 추상 메소드를 구현해야 한다.</li>
</ul>
<p>ConreteCreator : 구상 생산자</p>
<ul>
<li>실제로 제품을 생산하는 factoryMethod()를 구현한다.</li>
</ul>
<p>Product / ConreteProduct</p>
<ul>
<li>제품 클래스에서는 모두 똑같은 인터페이스 구현</li>
<li>그래야 제품을 사용할 클래스(클라이언트)에서 구상 클래스가 아닌 인터페이스에 대한 레퍼런스를 써서 객체를 참조할 수 있기 때문</li>
</ul>
<p>ConreteCreator → ConreteProduct</p>
<ul>
<li>구상 클래스 인스턴스를 만들어내는 일은 ConreteCreator가 책임진다.</li>
<li>실제 제품인 ConreteProduct 객체를 만들어내는 방법을 알고 있는 클래스는 ConreteCreator 클래스 뿐</li>
</ul>
<p>병렬 클래스 계층 구조</p>
<ul>
<li>Product, Creator 클래스 둘 다 추상 클래스로 시작하고, 그 클래스를 확장하는 구상 클래스들을 가지고 있다.</li>
<li>구체적인 구현은 구상 클래스들이 책임지고 있다.</li>
<li>구상 생산자 클래스에는 특정 구상 제품군에 대한 모든 지식이 캡슐화 되어 있으며 팩토리 메소는 이러한 지식을 캡슐화 시키는데 있어서 가장 핵심적인 역할을 맡고 있다.</li>
</ul>
</br>

<h3 id="simple-factory-vs-factory-method-pattern">Simple Factory vs Factory Method Pattern</h3>
<ul>
<li>뉴욕과 시카고 분점을 만들 때 간단한 팩토리를 사용했다고 할 수 있지 않을까??<ul>
<li>비슷하긴 하지만 방법이 조금 다르다.</li>
<li>구상 클래스를 만들 때 createPizza() 추상 메소드가 정의되어 있는 추상 클래스를 확장해서 만들었다는 점이 중요한 차이<ul>
<li>createPizza() 메소드에서 어떤 일을 할지는 각 분점에서 결정한다.</li>
</ul>
</li>
<li>간단한 팩토리를 사용할 때는 팩토리가 PizzaStore 안에 포함되는 별개의 객체였다는 큰 차이점이 존재한다.</li>
</ul>
</li>
<li>simple factory<ul>
<li>일회용 처방에 불과하다.</li>
<li>객체 생성을 캡슐화하는 방법을 사용하긴 하지만 생성하는 제품을 마음대로 변경할 수 없기 때문에 강력한 유연성을 제공하진 못한다.</li>
</ul>
</li>
<li>factory method pattern<ul>
<li>어떤 구현을 사용할지를 서브클래스에서 결정하는 프레임워크를 만들 수 있다.</li>
<li>강력한 유연성을 제공한다.</li>
</ul>
</li>
</ul>
</br>

<h3 id="의존적인-pizzastore">의존적인 PizzaStore</h3>
<ul>
<li>팩토리를 사용하지 않는 PizzaStore 클래스는 피자 객체가 의존하고 있는 구상 피자 객체의 개수만큼 의존하게 된다.</li>
<li>왜냐하면 객체 인스턴스를 직접 만들어 구상 클래스에 의존하기 때문이다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/ljo_0920/post/fd3f14c0-6a8c-4c0a-b0a4-d28dc2661e1f/image.png" alt=""></p>
<p><a href="https://msyu1207.tistory.com/entry/4%EC%9E%A5-%ED%97%A4%EB%93%9C%ED%8D%BC%EC%8A%A4%ED%8A%B8-%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-%ED%8C%A9%ED%86%A0%EB%A6%AC-%ED%8C%A8%ED%84%B4">https://msyu1207.tistory.com/entry/4장-헤드퍼스트-디자인-패턴-팩토리-패턴</a></p>
<ul>
<li>적용 전</li>
</ul>
<p><img src="https://velog.velcdn.com/images/ljo_0920/post/6bc8fb58-cd9d-4b59-8b56-cf6a17b395d5/image.png" alt=""></p>
<p><a href="https://msyu1207.tistory.com/entry/4%EC%9E%A5-%ED%97%A4%EB%93%9C%ED%8D%BC%EC%8A%A4%ED%8A%B8-%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-%ED%8C%A9%ED%86%A0%EB%A6%AC-%ED%8C%A8%ED%84%B4">https://msyu1207.tistory.com/entry/4장-헤드퍼스트-디자인-패턴-팩토리-패턴</a></p>
<ul>
<li>팩토리 메소드 패턴을 적용한 다이어 그램</li>
</ul>
</br>

<h2 id="dip--의존성-역전-원칙">DIP : 의존성 역전 원칙</h2>
<ul>
<li>구상 클래스에 대한 의존성을 줄이는 것이 좋다는 내용을 정리해 놓은 객체지향 디자인 원칙</li>
<li>이 원칙은 다음과 같이 일반화 시킬 수 있다.<ul>
<li>추상화된 것에 의존하도록 만들어라</li>
<li>구상 클래스에 의존하도록 만들지 않아야 한다.</li>
</ul>
</li>
<li>이 원칙하고 “특정 구현이 아닌 인터페이스에 맞춰서 프로그래밍한다”는 원칙이 똑같다고 생각할 수 있다.<ul>
<li>비슷하긴 하지만 dip에서는 추상화를 더 많이 강조한다.</li>
<li>해당 원칙에는 고수준 구성요소가 저수준 구성요소에 의존하면 안된다는 것이 내포되어 있다.</li>
<li>항상 추상화에 의존하도록 만들어야 한다.</li>
</ul>
</li>
<li>그럼 고수준, 저수준은 어떤 의미일까??<ul>
<li>고수준 구성요소<ul>
<li>고수준 구성요소는 다른 저수준 구성요소에 의해 정의되는 행동이 들어있는 구성요소를 뜻한다.</li>
<li>ex) PizzaStore</li>
<li>PizzaStore의 행동은 피자에 의해 정의되기 때문에 고수준 구성요소라고 할 수 있다.</li>
</ul>
</li>
<li>저수준 구성요소<ul>
<li>이 때 PizzaStore에서 사용하는 피자 객체들은 저수준 구성요소라고 할 수 있다.</li>
</ul>
</li>
</ul>
</li>
<li>팩토리 패턴을 사용하지 않은 기존의 PizzaStore 클래스는 구상 피자 클래스들에 의존하고 있다.</li>
<li>dip 원칙에 의하면, 구상 클래스처럼 구체적인 것이 아닌 추상 클래스나 인터페이스와 같이 추상적인 것에 의존하는 코드를 만들어야 한다.<ul>
<li>이 원칙은 고수준 모듈과 저수준 모듈에 모두 적용될 수 있다.</li>
</ul>
</li>
<li>dip 원칙을 지키는데 도움이 될만한 가이드라인<ol>
<li>어떤 변수에도 구상 클래스에 대한 레퍼런스를 저장하지 않는다.</li>
<li>구상 클래스에서 유도된 클래스를 만들지 않는다.</li>
<li>베이스 클래스에 이미 구현되어 있던 메소드를 오버라이드 하지 않는다.</li>
</ol>
<ul>
<li>해당 가이드들은 항상 지켜야 하는 규칙이 아니라 지향하는 바를 나타낸 것</li>
<li>이 가이드라인을 완벽하게 따를 순 없다.</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[데코레이터 패턴]]></title>
            <link>https://velog.io/@ljo_0920/%EB%8D%B0%EC%BD%94%EB%A0%88%EC%9D%B4%ED%84%B0-%ED%8C%A8%ED%84%B4</link>
            <guid>https://velog.io/@ljo_0920/%EB%8D%B0%EC%BD%94%EB%A0%88%EC%9D%B4%ED%84%B0-%ED%8C%A8%ED%84%B4</guid>
            <pubDate>Fri, 11 Mar 2022 04:55:56 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=582754">헤드 퍼스트 디자인 패턴</a>을 읽고 정리한 글입니다.</p>
<blockquote>
<p><strong>데코레이터 패턴</strong>을 이용하면 객체에 추가 요소를 동적으로 더할 수 있습니다. 데코레이터를 사용하면 서브 클래스를 만드는 경우에 비해 훨씬 유연하게 기능을 확장할 수 있습니다.</p>
</blockquote>
</br>

<h2 id="개요">개요</h2>
<p><img src="https://images.velog.io/images/ljo_0920/post/54546753-8e16-4e75-a638-1a5c26fe27b4/1..png" alt=""></p>
<p>OO커피는 단기간에 급속도로 성장한 대형 커피 전문점이다. 빠르게 성장한 만큼, 음료들을 모두 포괄하는 주문 시스템이 이제서야 개발되려고 하는 상황이다. 처음 시스템 시작할 무렵에 만들어진 클래스는 위의 사진과 같다.</p>
<p>Beverage : 음료를 나타내는 추상 클래스, 모든 음료는 해당 클래스의 서브클래스</p>
<ul>
<li>description : 해당 인스턴스 변수는 서브클래스에서 설정되며, 음료의 설명이 저장된다.</li>
<li>cost() : 추상 메서드이며, 서브 클래스에서 해당 메서드를 구현해야 한다.</li>
</ul>
</br>

<h3 id="하지만-음료에는-스팀-우유-두유-모카-휘핑-크림-등-옵션을-추가할-때마다-가격이-달라진다-이러한-경우를-모두-고려한다면">하지만 음료에는 스팀 우유, 두유, 모카, 휘핑 크림 등 옵션을 추가할 때마다 가격이 달라진다. 이러한 경우를 모두 고려한다면??</h3>
<p><img src="https://images.velog.io/images/ljo_0920/post/24ae79df-2701-431b-86f0-b9e827f1f29a/2.jpeg" alt=""></p>
<p>이처럼 클래스 개수가 폭발적으로 증가하게 된다. 만약 우유나 크림 가격이 인상된다면?? 한눈에 보기에도 이렇게 수많은 클래스를 관리하기는 힘들 것이다.</p>
</br>

<h3 id="그러면-슈퍼-타입인-beverage-클래스에-인스턴스-변수로-관리하면-안될까">그러면 슈퍼 타입인 Beverage 클래스에 인스턴스 변수로 관리하면 안될까??</h3>
<p><img src="https://images.velog.io/images/ljo_0920/post/ba841bfe-c74d-4d19-80c9-72b260e6d8b9/3.png" alt=""></p>
<ul>
<li>milk, soy, mocha, ... ,whip : 각 추가 요소에 해당하는 인스턴스 변수 추가</li>
<li>cost() : 각 음료 서브 클래스의 인스턴스마다 추가 사항에 해당하는 추가 가격까지 포함할 수 있도록 기본 음료 값을 가져와서 오버라이드 하기위해 추상 메소드가 아닌 구현 메소드로 수정</li>
<li>부울 인스턴스 변수를 위한 게터, 세터</li>
</ul>
<pre><code class="language-java">public class Beverage {

    String description;
    boolean hasMilk, hasSoy, hasMocha;
    double milkCost, soyCost, mochaCost;

    public double cost() {
        double condimentCost = 0;
        if (getHasMilk()) {
            condimentCost += milkCost;
        }
        if (getHasSoy()) {
            condimentCost += soyCost;
        }
        if (getHasMocha()) {
            condimentCost += mochaCost;
        }
        return condimentCost;
    }

    // get, set..
    public boolean getHasMilk() {
        return hasMilk;
    }

    public boolean getHasSoy() {
        return hasSoy;
    }

    public boolean getHasMocha() {
        return hasMocha;
    }

}</code></pre>
<pre><code class="language-java">public class DarkRoast extends Beverage{

    public DarkRoast() {
        description = &quot;다크 로스트&quot;;
    }

    @Override
    public double cost() {
        return super.cost() + 3500;
    }
}</code></pre>
</br>

<h3 id="아까와-같은-클래스-폭발을-막게되었다-하지만-아직-확신이-서지-않는다-어떤-문제점이-있을-수-있을까">아까와 같은 클래스 폭발을 막게되었다. 하지만 아직 확신이 서지 않는다. 어떤 문제점이 있을 수 있을까??</h3>
<ul>
<li>음료에 추가되는 옵션(우유, 휘핑, 모카 등)의 가격이 바뀔때마다 코드를 수정해야 한다.</li>
<li>음료에 추가되는 옵션의 종류가 많아지면 그 때마다 메소드를 추가하고, 슈퍼 클래스의 cost() 메소드를 수정해야 한다.</li>
<li>새로운 음료가 추가되는 경우, 옵션이 없는 경우와 우유가 들어가지 않는 음료임에도 관련된 멤버들을 상속받게 된다.</li>
<li>만약 샷이라는 옵션이 추가되고 샷을 두번 추가한 음료는 어떻게 해야할까??</li>
</ul>
<p>상속은 객체지향 디자인의 강력한 요소 중 하나지만, 이처럼 상속을 사용한다고 해서 무조건 유연하고 관리하기 쉬운 디자인이 만들어지지 않는다. 그 이유는 서브 클래스를 만드는 방식으로 행동을 상속 받으면 해당 행동은 <strong>컴파일시에 완전히 결정</strong>되고 <strong>모든 서브클래스에서 슈퍼 클래스의 멤버들을 상속</strong> 받아야 하기 때문이다. 하지만 <strong>composite</strong>를 통해서 객체의 행동을 실행 중에 동적으로 설정하는 방법을 사용한다면, 즉 <strong>객체를 동적으로 구성</strong>하면, 기존 코드를 수정하는 대신 새로운 코드를 추가하는 방식으로 새로운 기능을 추가할 수 있다. 기존 코드는 수정되지 않으므로(변경에 대해서는 닫혀있으므로) 버그가 생기거나 사이드 이펙트를 방지하면서 새로운 기능을 추가(확장에 대해서는 열려있는)할 수 있는 것이다. </p>
</br>

<h2 id="데코레이터-해보자">데코레이터 해보자!</h2>
<p>이제 음료에서 추가되는 옵션이 있는 경우 해당 음료를 데코레이터 하는 방식으로 수정해보자. 만약 모카와 휘핑 크림을 추가한 다크 로스트 커피는 다음처럼 할 수 있을 것이다.</p>
<ol>
<li>DarkRoast 객체를 가져온다</li>
<li>Mocha 객체로 장식한다.</li>
<li>Whip 객체로 장식한다.</li>
<li>cost() 메소드를 호출한다. 이때 추가 옵션의 가격을 계산하는 일은 해당 객체들에게 위임한다.</li>
</ol>
<p>이 때 장식하고 위임하는 방법은 해당 객체를 래퍼 객체라고 생각하면 쉽다. </p>
<p><img src="https://images.velog.io/images/ljo_0920/post/24f79cc8-ba33-429f-b703-ac60758a60af/4.png" alt=""></p>
<p>이렇게 가장 바깥쪽에 있는 데코레이터 객체에서 cost()를 호출하고, 해당 객체가 장식하고 있는 객체에게 가격을 위임한다. 위임한 객체에게 가격의 값을 얻으면, 자신의 가격을 더한 다음 리턴하는 것이다.</p>
<p>여기서 중요한 점은 데코레이터는 <strong>자신이 장식하고 있는 객체에게 어떤 행동을 위임하는 것 외에 원하는 추가적인 작업을 수행</strong>할 수도 있다는 저이다.</p>
</br>

<h2 id="데코레이터-패턴">데코레이터 패턴</h2>
<p>데코레이터 패턴에서는 객체에 추가적인 요건을 동적으로 첨가한다. 데코레이터는 서브클래스를 만드는 것을 통해서 기느을 유연하게 확장할 수 있는 방법을 제공한다.</p>
<p><img src="https://images.velog.io/images/ljo_0920/post/77afda35-4c9a-42c3-8766-e70e14baef40/5.png" alt=""></p>
<p>Compent</p>
<ul>
<li>각 구성요소는 직접 사용할 수도 있고 데코레이터로 감싸져서 쓰일 수도 있다.</li>
<li>ex) Beverage 클래스</li>
</ul>
<p>ConcreteComponent</p>
<ul>
<li>해당 클래스에 새로운 행동을 동적으로 추가하게 된다.</li>
</ul>
<p>Decorator</p>
<ul>
<li>데코레이터는 자신이 장식할 Component와 같은 인터페이스 또는 추상 클래스를 구현한다.</li>
<li>각 데코레이터 안에는 Component 객체가 들어있다. 즉, 데코레이터에는 구성요소에 대한 레퍼런스가 들어있는 인스턴스 변수를 가진다.</li>
</ul>
<p>ConcreteDecorator</p>
<ul>
<li>ConcreteDecorator 에는 데코레이터가 감싸고 있는 Component 객체를 위한 인스턴스 변수가 있다.</li>
<li>Decorator는 Component의 상태를 확장할 수 있다.</li>
<li>Decorator에서 새로운 메소드를 추가할 수도 있다. 하지만 일반적으로 새로운 메소드를 추가하는 대신 Component의 메소드를 호출하기 전, 후에 별도의 작업을 처리하는 방식으로 새로운 기능을 추가한다.</li>
</ul>
</br>

<h3 id="beverage-클래스-다이어그램">Beverage 클래스 다이어그램</h3>
<p><img src="https://images.velog.io/images/ljo_0920/post/56550ba0-bac2-49f9-b145-2e87ab2dabc0/6.png" alt=""></p>
<pre><code class="language-java">public abstract class Beverage {

    String description = &quot;&quot;;

    public abstract double cost();

    public String getDescription() {
        return description;
    }
}

public class Espresso extends Beverage {

    public Espresso() {
        description = &quot;에스프레소&quot;;
    }

    @Override
    public double cost() {
        return 3500;
    }
}</code></pre>
<pre><code class="language-java">
public abstract class CondimentDecorator extends Beverage {
    public abstract String getDescription();
}

public class Mocha extends CondimentDecorator {

    Beverage beverage;

    public Mocha(Beverage beverage) {
        this.beverage = beverage;
    }

    @Override
    public double cost() {
        return 500 + beverage.cost();
    }

    @Override
    public String getDescription() {
        return beverage.getDescription() + &quot;, 모카&quot;;
    }
}</code></pre>
<pre><code class="language-java">public class StarbuzzCoffee {

    public static void main(String[] args) {
        Beverage beverage = new Espresso();
        System.out.println(beverage.getDescription() + &quot;: &quot; + beverage.cost() + &quot; won&quot;);

        Beverage beverage2 = new Espresso();
        beverage2 = new Mocha(beverage2);
        beverage2 = new Mocha(beverage2);
        System.out.println(beverage2.getDescription() + &quot;: &quot; + beverage2.cost() + &quot; won&quot;);
    }

}</code></pre>
<p><img src="https://images.velog.io/images/ljo_0920/post/63d52582-322f-492b-bdc5-0e0c478cce63/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202022-03-09%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%204.49.38.png" alt=""></p>
<p>데코레이터 패턴을 적용한 코드는 아까의 문제점이 사라진 오류의 코드지만 저런형태로 관리하게 될경우 마지막 Soy를 빼먹는다던가 실수로 두번넣는 경우가 생기게됩니다. <strong>팩토리 패턴과 빌더 패턴을 이용해서 더 쉽게 객체를 만드는 방법이 존재한다.</strong></p>
</br>

<h2 id="주의해야-할-점">주의해야 할 점</h2>
<ul>
<li>특정 ConcreteComponent 타입을 바탕으로 작업을 처리하는 코드에 데코레이터 패턴을 적용하면 제대로 작동하지 않는다. (ConcreteDecorator로 감싸져 있기 때문)</li>
<li>만약 여러 단계의 데코레이터를 파고 들어가서 어떤 작업을 해야 한다면, 데코레이터 패턴의 의의와 어긋나는 것이다.</li>
<li>데코레이터 패턴에서는 특정한 추상 구성요소를 지정할 필요가 없다. 인터페이스를 사용해도 무방하다.</li>
</ul>
</br>

<h2 id="마무리">마무리</h2>
<ul>
<li>상속을 통한 확장은 디자인의 유연성 면에서 좋지 않을 수 있다.</li>
<li>기존 코드를 수정하지 않고도 행동을 확장하는 방법이 필요하다 (OCP)</li>
<li>합성과 위임을 통해서 실행중에 새로운 행동을 추가할 수 있다.</li>
<li>상속 대신 데코레이터 패턴을 통해 행동을 확장할 수 있다.</li>
<li>데코레이터 패턴에서는 구상 구성요소를 감싸주는 데코레이터를 사용한다.</li>
<li>데코레이터 클래스의 형식은 그 클래스가 감싸고 있는 클래스의 형식을 반영한다.</li>
<li>데코레이터에서는 자기가 감싸고 있는 구성요소의 메소드를 호출한 결과에 새로운 기능을 더함으로써 확장한다.</li>
<li>구성요소를 감싸는 데코레이터의 개수에는 제한이 없다.</li>
<li>구성요소의 클라이언트 입장에서는 데코레이터의 존재를 알 수 없다.<ul>
<li>따라서 클라이언트에서 구성 요소의 구체적인 타입에 의존하게 되는 경우는 다시 생각해봐야한다.</li>
</ul>
</li>
<li>데코레이터 패턴을 사용하면 객체들이 많이 추가될 수 있고, 코드가 복잡해질 수 있다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[옵저버 패턴]]></title>
            <link>https://velog.io/@ljo_0920/%EC%98%B5%EC%A0%80%EB%B2%84-%ED%8C%A8%ED%84%B4-xzrb4hge</link>
            <guid>https://velog.io/@ljo_0920/%EC%98%B5%EC%A0%80%EB%B2%84-%ED%8C%A8%ED%84%B4-xzrb4hge</guid>
            <pubDate>Sun, 06 Mar 2022 16:39:46 GMT</pubDate>
            <description><![CDATA[<h1 id="옵저버-패턴">옵저버 패턴</h1>
<p><a href="https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=582754">헤드 퍼스트 디자인 패턴</a>을 읽고 정리한 글입니다.</p>
<blockquote>
<p>옵저버 패턴에서는 한 객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체들한테 연락이 가고 자동으로 내용이 갱신되는 방식으로 일대다 의존성을 정의한다.</p>
</blockquote>
</br>

<h2 id="개요">개요</h2>
<p><img src="https://images.velog.io/images/ljo_0920/post/231ac0e2-9930-4680-848d-0427f1de1d7d/1.png" alt=""></p>
<p>실제 기상 정보를 수집하는 장비인 기상 스테이션과 기상 스테이션으로부터 오는 데이터를 추적하는 객체인 WeatherData,  그리고 사용자에게 현재 기상 조건을 보여주는 디스플레이, 세 요소로 이루어진다.</p>
<p>WeatherData 객체에서는 기상 스테이션으로부터 데이터를 가져올 수 있다. 데이터를 가져온 후에는 디스플레이 장비에 세 가지 항목을 표시할 수 있다. </p>
<ul>
<li>현조 조건(온도, 습도, 압력)</li>
<li>기상 통계</li>
<li>기상 예보</li>
</ul>
<p>WeatherData 객체를 사용하여 디스플레이 장비에서 위의 3가지 요소를 갱신해 가면서 보여주는 애플리케이션을 만들어보자</p>
</br>

<h3 id="주어진-weatherdata-클래스와-상황">주어진 WeatherData 클래스와 상황</h3>
<p><img src="https://images.velog.io/images/ljo_0920/post/d9c667eb-b802-4684-8123-9acadc777cf5/2.png" alt=""></p>
<ul>
<li>세가지의 게터 메소드는 각각 가장 최근에 측정된 온도, 습도, 기압 값을 리턴하는 메소드</li>
<li><code>measurementsChanged</code> 메소드를 현재 조건, 기상 통꼐, 기상 예측 3가지 디스플레이를 갱신할 수 있도록 구현해야 한다.</li>
<li>시스템은 확장 가능해야 한다. 추후 디스플레이 항목들은 추가/제거될 수 있다.</li>
</ul>
</br>

<h3 id="초기-구현">초기 구현</h3>
<pre><code class="language-java">public class WeatherData {

    // 인스턴스 변수 선언

    public void measurementsChanged() {
        float temp = getTemperature();
        float humidity = getHumidity();
        float pressure = getPressure();

        currentConditionsDisplay.update(temp, humidity, pressure);
        statisticsDisplay.update(temp, humidity, pressure);
        forecastDisplay.update(temp, humidity, pressure);

    }

}</code></pre>
<p>위의 코드의 문제는 무엇일까??</p>
<ul>
<li>인터페이스가 아닌 구체적인 구현을 바탕으로 코딩하고 있다.</li>
<li>새로운 디스플레이 항목이 추가될 때마다 코드를 변경해야 한다.</li>
<li>실행중에 디스플레이 항목을 추가/제거할 수 없다.</li>
<li>바뀌는 부분을 캡슐화하지 않았다.</li>
</ul>
<p>구체적인 구현에 맞춰서 코딩되어 있기 때문에 코드를 고치지 않고는 다른 디스플레이 항목을 추가하거나 제거할 수 없고 디스플레이에 항목들을 갱신하여 업데이트하는 부분들은 바뀔 수 있는 부분이므로 캡슐화해야 한다.</p>
<p>이제 옵저버 패턴에 대해서 알아보자</p>
</br>

<h2 id="옵저버-패턴-1">옵저버 패턴</h2>
<p>쉽게 생각해서 신문 구독 메커니즘과 비슷한다. 즉 <code>출판사 + 구독자 = 옵저버 패턴</code> 인 것</p>
<p>출판사를 주제 or 주체(subject), 구독자를 옵저버(observer)라고 부른다.</p>
<ul>
<li>subject 객체에서 일부 데이터를 관리</li>
<li>subejct의 데이터가 달라지면 옵저버한테 소식과 데이터가 전달된다.</li>
<li>observer 객체들은 subject 객체를 구독(등록)하고 있으며 subject의 데이터가 바뀌면 갱신 내용을 전달받는다.</li>
</ul>
<p>옵저버 패턴에서는 한 객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체들한테 연락이 가고 자동으로 내용이 갱신되는 방식으로 일대다 의존성을 정의한다.</p>
<ul>
<li>일대다 관계는 subject와 observer에 의해 정의되고 observer는 subject에 의존한다.</li>
<li>옵저버 패턴을 구현하는 방법에는 여러가지가 있지만 대부분 subject 인터페이스와 observer 인터페이스를 사용한 클래스 디자인을 바탕으로 한다.</li>
</ul>
<p><img src="https://images.velog.io/images/ljo_0920/post/0648cf5f-3170-4c2c-9518-a18316c15d05/3.png" alt=""></p>
<p><strong>Subject 인터페이스</strong></p>
<ul>
<li>객체에서 옵저버로 등록하거나 옵저버 목록에서 탈퇴하고 싶을 때 이 인터페이스에 있는 메소드를 사용한다.</li>
</ul>
<p><strong>ConcreteSubject</strong></p>
<ul>
<li>Subject 역할을 하는 Concrete 클래스에서는 항상 subject 인터페이스를 구현해야 한다.</li>
<li>subject 클래스에서는 등록 및 해지를 위한 메소드 외에 상태가 바뀔때마다 모든 옵저버들에게 연락을 하기 위한 notifyObservers() 메소드도 구현해야 한다.</li>
<li>subject 클래스에는 상태를 설정하고 알아내기 위한 세터/게터 메소드가 있을 수도 있다.</li>
</ul>
<p><strong>Observer 인터페이스</strong></p>
<ul>
<li>observer가 될 가능성이 있는 객체에서는 반드시 observer 인터페이스를 구현해야 한다.</li>
<li>observer 인터페이스에는 subject의 상태가 바뀌었을 때 호출되는 update() 메소드 밖에 없다.</li>
</ul>
<p><strong>ConcreteObserver</strong></p>
<ul>
<li>Observer 인터페이스만 구현한다면 어떤 클래스든 옵저버 클래스가 될 수 있다.</li>
<li>각 옵저버는 특정 주제 객체에 등록을 해서 연락을 받을 수 있다.</li>
</ul>
<p>옵저버 패턴에서 상태를 저장하고 지배하는 것은 subject 객체이다. 따라서 상태가 들어있는 객체는 하나만 존재하고, 옵저버는 반드시 상태를 가지고 있어야 하는 것은 아니기 때문에 옵저버는 여러 개가 있을 수 있으며 subject 객체에서 상태가 바뀌었다는 알려주기를 기다리는, subject에 의존적인 성질을 가진다.</p>
<p>따라서 하나의 subject와 여러개의 observer가 연관된 일대다 관계가 성립하고 해당 의존을 통해 여러 객체에서 동일한 데이터를 제어하도록 할 수 있다.</p>
</br>

<h2 id="느슨한-결합">느슨한 결합</h2>
<p>추가로 옵저버 패턴의 장점은 subject와 observer가 느슨하게 결합되어 있는 디자인을 제공하는 것이다. 두 객체가 느슨하게 결합되어 있다는 것은, 해당 객체들이 상호작용을 하지만 서로에 대해 잘 모른다는 것을 의미한다.</p>
<p><strong>subject가 observer에 대해 아는 것은 해당 옵저버가 observer 인터페이스를 구현한다는 것 뿐이다</strong></p>
<ul>
<li>옵저버의 구상 클래스, 어떤 행동을 하는지 등, 나머지는 알 필요가 없다.</li>
</ul>
<p><strong>observer는 언제든지 추가 가능하다</strong></p>
<ul>
<li>subject는 observer 인터페이스를 구현하는 객체 목록에먼 의존하기 때문에 실행중에 한 옵저버를 다른 옵저버로 바꿔도 되고, 언제든지 새로운 옵저버를 추가하거나 삭제할 수 있다.</li>
</ul>
<p><strong>새로운 형식의 observer를 추가하려고 할 때도 subject를 변경할 필요가 없다.</strong></p>
<ul>
<li>observer 인터페이스에 의조하기 때문에 새로운 옵저버 구상 클래스가 생겨도 문제 없다.</li>
</ul>
<p><strong>subject와 observer는 서로 독립적으로 재사용할 수 있다.</strong></p>
<ul>
<li>다른 용도로 활용할 일이 있어도 문제 없다. 느슨하게 결합되어 있기 때문</li>
</ul>
<p><strong>subject와 observer에 변경이 생겨도 서로에게 영향이 미치지 않는다.</strong></p>
<ul>
<li>마찬가지로 느슨한 결합 덕분</li>
</ul>
</br>

<h2 id="옵저버-패턴-적용">옵저버 패턴 적용</h2>
<p><img src="https://images.velog.io/images/ljo_0920/post/b7726bc4-c1a3-4c19-bbf7-2bbe8f5f6174/4.png" alt=""></p>
<ul>
<li>Subject, Observer 인터페이스 생성</li>
<li>WeatherData는 Subject 인터페이스를 구현하도록 수정</li>
<li>모든 기상 구성요소에서 Observer 인터페이스를 구현하도록 수정<ul>
<li>subject 객체에서 갱신된 데이터를 전달할 수 있도록 메소드 제공</li>
</ul>
</li>
<li>모든 디스플레이 항목에서 구현하는 DisplayElement 인터페이스를 하나 더 추가<ul>
<li>측정값을 바탕으로 각각 다른 내용을 표시하는 메소드인 display() 메소드 추가</li>
</ul>
</li>
</ul>
<pre><code class="language-java">public interface Subject {
    void registerObserver(Observer o);
    void removeObserver(Observer o);
    void notifyObserver();
}

public interface Observer {
    void update(float temperature, float humidity, float pressure);
}

public interface DisplayElement {
    void display();
}</code></pre>
<pre><code class="language-java">import java.util.ArrayList;
import java.util.List;

public class WeatherData implements Subject {

    private List&lt;Observer&gt; observers;
    private float temperature;
    private float humidity;
    private float pressure;

    public WeatherData() {
        this.observers = new ArrayList&lt;&gt;();
    }

    @Override
    public void registerObserver(Observer o) {
        observers.add(o);
    }

    @Override
    public void removeObserver(Observer o) {
        int i = observers.indexOf(o);
        if(i &gt;= 0) {
            observers.remove(i);
        }
    }

    @Override
    public void notifyObserver() {
        observers.forEach(observer -&gt; observer.update(temperature, humidity, pressure));
    }

    public void measurementsChanged() {
        notifyObserver();
    }

    public void setMeasurements(float temperature, float humidity, float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        measurementsChanged();
    }
}</code></pre>
<pre><code class="language-java">public class CurrentConditionsDisplay implements Observer, DisplayElement {

    private float temperature;
    private float humidity;
    private Subject weatherData;

    public CurrentConditionsDisplay(Subject weatherData) {
        this.weatherData = weatherData;
        this.weatherData.registerObserver(this);
    }

    @Override
    public void update(float temperature, float humidity, float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        display();
    }

    @Override
    public void display() {
        System.out.println(&quot;Current conditions: &quot; + temperature + &quot;F degrees and &quot; + humidity + &quot;% humidity&quot;);
    }
}</code></pre>
<pre><code class="language-java">public class WeatherStation {

    public static void main(String[] args) {
        WeatherData weatherData = new WeatherData();
        CurrentConditionsDisplay conditionsDisplay = new CurrentConditionsDisplay(weatherData);
        weatherData.setMeasurements(70, 60, 30.4f);
        weatherData.setMeasurements(55, 50, 15.4f);
        weatherData.setMeasurements(90, 70, 40.4f);
    }

}</code></pre>
<p><img src="https://images.velog.io/images/ljo_0920/post/3ec44aea-2a4c-4007-9311-209aef1a1293/5.png" alt=""></p>
</br>

<h2 id="데이터-전달-시-두가지-방식">데이터 전달 시 두가지 방식</h2>
<p>현재는 subject의 상태가 변경될 때마다 observer에게 알려주고(push) 있다. 옵저버 입장에서는 필요한 상황에서만 주체의 상태를 가져오는 방식(pull)이 더 편할 수도 있지 않을까??</p>
<p>이처럼  옵저버 패턴은 PUSH 방식과 PULL 방식으로 구분할 수 있다.</p>
<p><code>PUSH 방식</code> : 주제의 내용이 변경될 때마다 구독자에게 알려주는 방식</p>
<p><code>PULL 방식</code> : 구독자가 필요할 때마다 주제에게 정보를 요청하는 방식</p>
<p>또한 자바에서 몇 가지 API를 통해 자체적으로 옵저버 패턴을 지원한다. 일반적으로 java.util 패키지에 들어있는 Observer 인터페이스와 Observable 클래스이다. 해당 내장 클래스들은 푸시 방식과 풀 방식 모두 가능하다.</p>
<p>그렇다면 자바 내장 클래스를 이용하여 풀 방식으로 수정해보자</p>
<h2 id="pull-방식으로-수정with-자바-내장-옵저버-패턴">Pull 방식으로 수정(with 자바 내장 옵저버 패턴)</h2>
<p><img src="https://images.velog.io/images/ljo_0920/post/2d098a72-2b8f-43f8-97bf-4c0f7a7d2679/6.png" alt=""></p>
<ul>
<li>Observable은 인터페이가 아니라 클래스이므로 WeatherData 클래스에서 해당 클래스를 상속하면서 메소드들을 상속받는다.</li>
<li>setChange() 제공<ul>
<li>해당 메소드는 상태가 바뀌었다는 것을 밝히기 위한 용도로 사용된다. setChange() 메소드가 호출되지 않은 상태에서 notifiyObservers()가 호출되면 옵저버들에게 연락이 가지 않는다. 해당 메소드를 조건에 따라서 적절히 호출하여 옵저버들에게 연락이 가는 것을 제어할 수 있다.</li>
</ul>
</li>
<li>Observer 인터페이스는 앞에서 만든 클래스와 거의 똑같다.</li>
</ul>
<h3 id="바뀐-weatherdata">바뀐 WeatherData</h3>
<pre><code class="language-java">import java.util.ArrayList;
import java.util.List;
import java.util.Observable;
import observer.after.Observer;
import observer.after.Subject;

public class WeatherDataObservable extends Observable {

    private float temperature;
    private float humidity;
    private float pressure;

    public WeatherDataObservable() {
    }

    public void measurementsChanged() {
        setChanged();
        notifyObservers(); // pull 방식, push 방식인 경우 notifyObservers(Object arg);
    }

    public void setMeasurements(float temperature, float humidity, float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        measurementsChanged();
    }

    // pull 방식이므로 옵저버가 주체 객체의 상태를 알아야하므로 필요하다.
    public float getTemperature() {
        return temperature;
    }

    public float getHumidity() {
        return humidity;
    }

    public float getPressure() {
        return pressure;
    }
}</code></pre>
<pre><code class="language-java">import java.util.Observable;
import java.util.Observer;
import observer.after.DisplayElement;
import observer.after2.WeatherDataObservable;

public class CurrentConditionsDisplayObserver implements Observer, DisplayElement {

    private float temperature;
    private float humidity;
    Observable observable;

    public CurrentConditionsDisplayObserver(Observable observable) {
        this.observable = observable;
        observable.addObserver(this);
    }

    @Override
    public void update(Observable o, Object arg) {
        if(o instanceof WeatherDataObservable) {
            WeatherDataObservable weatherData = (WeatherDataObservable) o;
            this.temperature = weatherData.getTemperature();
            this.humidity = weatherData.getHumidity();
            display();
        }
    }

    @Override
    public void display() {
        System.out.println(&quot;Current conditions: &quot; + temperature + &quot;F degrees and &quot; + humidity + &quot;% humidity&quot;);
    }
}</code></pre>
</br>

<h2 id="javautilobservable의-단점">java.util.Observable의 단점</h2>
<p><strong>Observer와 Observable은 <a href="https://docs.oracle.com/javase/9/docs/api/java/util/Observable.html">Java SE 9</a> 버전부터 Deprecated 되었다. 그 이유는 무엇일까?</strong></p>
<ul>
<li>Observer와 Observable이 제공하는 이벤트 모델이 제한적이다.</li>
<li>Observable의 notify는 순서를 보장할 수 없으며, 상태 변경은 1:1로 일치하지 않는다.</li>
<li>더 풍부한 이벤트 모델은 <code>java.beans</code> 패키지가 제공하고 있다.</li>
<li>멀티 스레드에서의 신뢰할 수 있고 순서가 보장된 메시징은 <code>java.util.concurrent</code> 패키지의 concurrent 자료 구조들 중 하나를 골라 쓰는 편이 낫다.</li>
<li>reactive stream 스타일 프로그래밍은 <code>Flow</code> API를 쓰기를 권한다.</li>
</ul>
<p><strong>Observable의 문제는 헤드 퍼스트 디자인 패턴에서도 지적하고 있다</strong></p>
<ul>
<li>Observable이 interface가 아니라 class이다.<ul>
<li>다른 클래스를 상속하는 클래스가 Observable을 상속할 수 없다. 따라서 재사용성에 제약이 생긴다.</li>
<li>내장된 Observer API하고 잘 맞는 클래스를 직접 구현하는 것이 불가능하다.</li>
</ul>
</li>
<li>Obserable 클래스의 핵심 메소드를 외부에서 호출할 수 없다.<ul>
<li>setChanged() 메소드가 protected으로 선언되어 있기 때문이다.</li>
<li>상속보다 구성을 사용한다는 디자인 원칙을 위배한다.</li>
</ul>
</li>
</ul>
</br>

<h2 id="마무리-핵심-정리">마무리 핵심 정리</h2>
<ul>
<li>옵저버 패턴에서는 객체들 사이에 일대다 관계를 정의한다.</li>
<li>주체 객체는 동일한 인터페이스를 써서 옵저버에 연락한다.</li>
<li>주체와 옵저버는 서로 느슨한 결합</li>
<li>주체에서 데이터를 보내는 푸시방식과 옵저버가 데이터를 가져오는 풀방식이 있다.<ul>
<li>풀 방식 추천</li>
</ul>
</li>
<li>옵저버들한테 연락을 돌리는 순서에 절대로 의존하면 안 된다.<ul>
<li>만약 의존하도록 했다면 잘못된 것, 느슨한 결합이라고 볼 수 없다.</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[자바 제네릭스]]></title>
            <link>https://velog.io/@ljo_0920/%EC%9E%90%EB%B0%94-%EC%A0%9C%EB%84%A4%EB%A6%AD%EC%8A%A4</link>
            <guid>https://velog.io/@ljo_0920/%EC%9E%90%EB%B0%94-%EC%A0%9C%EB%84%A4%EB%A6%AD%EC%8A%A4</guid>
            <pubDate>Thu, 10 Feb 2022 06:42:15 GMT</pubDate>
            <description><![CDATA[<h2 id="제네릭스generics">제네릭스(Generics)</h2>
<p>제네릭스란 JDK 1.5부터 도입한 클래스 내부에서 사용할 데이터 타입을 외부에서 지정하는 기법이다. 다양한 타입의 객체들을 다루는 <code>메서드나</code> 컬렉션 <code>클래스</code>에 <code>컴파일 시의 타입 체크</code>를 해주는 기능이다.</p>
<p>제네릭스가 필요한 이유는 객체의 타입을 컴파일 시에 체크하기 때문에 객체의 타입 안정성을 높이고 형변환의 번거로움이 줄어든다. 타입 안정성을 높인다는 것은 의도하지 않은 타입의 객체가 저장되는 것을 막고, 저장된 객체를 꺼내올 때 원래의 타입과 다른 타입으로 잘못 형변환되어 발생할 수 있는 오류를 줄여준다는 뜻이다.</p>
<h3 id="장점">장점</h3>
<ul>
<li>타입 안정성을 제공한다.</li>
<li>타입체크와 형변환을 생략할 수 있으므로 코드가 간결해 진다.</li>
</ul>
<p>즉 객체의 타입을 미리 명시해줌으로써 번거로운 형변환을 줄여준다는 이야기이다.</p>
</br>

<h3 id="지네릭-클래스의-선언">지네릭 클래스의 선언</h3>
<p>지네릭 타입은 클래스와 메서드에 선언할 수 있다.</p>
<pre><code class="language-java">class Box&lt;T&gt; {
        T item;

        void setItem(T item) { this.item = item; }
        T getItem( return item; )
}</code></pre>
<p>지네릭 클래스가 된 클래스의 객체를 생성할 때는 다음과 같이 참조변수와 생성자에 타입 T대신에 사용될 실제 타입을 지정해주어야 한다. </p>
<pre><code class="language-java">Box&lt;String&gt; b = new Box&lt;String&gt;(); // 타입 T 대신, 실제 타입을 지정
b.setItem(new Object())  // 에러, String이외에 타입은 지정불가
b.setItem(&quot;ABC&quot;)  // ok
String item = b.getItem(); // (String)b.getItem(); 처럼 형변환이 필요없음 
</code></pre>
<p>컴파일 후에 Box<String> 과 Box<Integer> 들은 이들의 &#39;원시타입&#39;인 Box로 바뀐다.</p>
<p>즉 지네릭 타입이 제거된다. 지네릭 타입의 제거에서 자세히 다뤄보자</p>
<p>지네릭이 도입되기 이전의 코드와 호환을 위해, 지네릭 클래스인데도 예전의 방식으로 객체를 생성하는 것이 허용된다. 다만 지네릭 타입을 지정하지 않아서 경고가 발생한다.</p>
<pre><code class="language-java">Box b = new Box(); // ok, T는 Object로 간주
b.setItem(&quot;ABC&quot;); // 경고, uncheck or unsafe operation
b.setItem(new Object()); // 경고, uncheck or unsafe operation</code></pre>
</br>


<h2 id="타입-파라미터-컨벤션"><strong>타입 파라미터 컨벤션</strong></h2>
<p>제네릭에서 사용하는 타입 파라미터에 자주 봤던 T 같은 문자가 아니고 아무런 문자나 넣어도 코드가 동작하는 데는 문제가 없다.</p>
<p>하지만 타입 파라미터에도 컨벤션이 존재한다. 컨벤션을 왜 지켜야 하는지는 다들 잘 아실 것이다. 기억이 안 난다면 <a href="https://www.oracle.com/java/technologies/javase/codeconventions-introduction.html#16712">Code Conventions for the Java Programming Language</a> 글의 Why Have Code Conventions 부분을 보자.</p>
<p>그래서 타입 파라미터 컨벤션은 아래와 같다.</p>
<p><img src="https://tecoble.techcourse.co.kr/static/b268c4f8f30a082779a188af838767eb/3e3fe/2020-11-09-generics-2.png" alt="https://tecoble.techcourse.co.kr/static/b268c4f8f30a082779a188af838767eb/3e3fe/2020-11-09-generics-2.png"></p>
<p>제네릭 클래스나 메서드를 구현할 일이 있다면 컨벤션에 맞춰서 구현하자!</p>
</br>

<h2 id="지네릭스의-제한">지네릭스의 제한</h2>
<p>제네릭스 클래스의 객체를 생성할 때, 객체별로 다른 타입을 지정하는 것은 적절하다. 하지만 모든 객체에 대해 동일하게 동작해야하는 static 멤버에 타입 변수 T를 사용할 수 없다. T는 인스턴스 변수로 간주되고, static 멤버는 타입 변수에 지정된 타입, 즉 대입된 타입의 종류에 관계없이 동일한 것이어야 하기 때문이다.</p>
<pre><code class="language-java">class Box&lt;T&gt; {
        T[] itemArr; // ok, T타입의 배열을 위한 참조변수
    T[] toArray() {
                T[] tmpArr = new T[itemArr.length]; // error, 지네릭 배열 생성불가
                ...
        }

}</code></pre>
<p>또, 지네릭 배열 타입의 배열을 생성하는 것도 허용되지 않는다. </p>
<p>지네릭 배열 타입의 참조변수를 선언하는 것은 가능하지만 new T[10]과 같이 배열을 생성하는 것은 안된다. 생성할 수 없는 이유는 new 연산자 때문, 이 연산자는 컴파일 시점에 타입 T가 뭔지 정확히 알아야 한다. </p>
<p>그런데 위의 코드는 정의된 Box<T> 클래스를 컴파일 하는 시점에서는 T가 어떤 타입이 될지 전혀 알 수 없다. intanceof 연산자도 마찬가지이다. </p>
<p>꼭 지네릭 배열을 생성해야할 필요가 있을 때는 new 연산자 대신 Reflection API의 newInstance()와 같이 동적으로 객체 생성하거나 Object 배열을 생성해서 복사한 다음에 T[]로 형변환 하는 방법이 존재한다.</p>
<h3 id="지네릭-클래스의-객체-생성과-사용">지네릭 클래스의 객체 생성과 사용</h3>
<pre><code class="language-java">class Box&lt;T&gt; {
    ArrayList&lt;T&gt; list = new ArrayList&lt;T&gt;();

    void add(T item) { list.add(item); }
    T get(int i)     { return list.get(i); }
    ArrayList&lt;T&gt; getList() { return list; }
    int size()       { return list.size(); }
    public String toString() { return list.toString(); }
}</code></pre>
<p>Box<T>의 객체를 생성할 때는 다음과 같이 참조변수와 생성자에 대입된 타입(매개변수화된 타입)이 일치해야 한다. 일치하지 않으면 에러가 발생</p>
<pre><code class="language-java">Box&lt;Apple&gt; appleBox = new Box&lt;Apple&gt;(); // ok
Box&lt;Apple&gt; appleBox = new Box&lt;Grape&gt;(); // error</code></pre>
<p>Apple이 Fruit의 자손이라고 가정해보자 하지만 그럼에도 error이다.</p>
<pre><code class="language-java">Box&lt;Fruit&gt; appleBox = new Box&lt;Apple&gt;(); // error, 대입된 타입이 다르다.</code></pre>
<p>단, 두 지네릭 클래스의 타입이 상속관계에 있고, 대입된 타입이 같은 것은 괜찮다. FruitBox는 Box의 자손이라고 가정</p>
<pre><code class="language-java">Box&lt;Apple&gt; appleBox = new FruitBox&lt;Apple&gt;(); // ok, 다형성</code></pre>
 </br>

<h2 id="와일드-카드">와일드 카드</h2>
<p>제네릭 클래스가 아닌 클래스에 static 메서드의 매개변수로 특정 타입을 지정해줬을 때, 제네릭 타입을 특정 타입으로 고정해 놓으면 다른 타입의 객체가 메서드의 매개변수가 될 수 없으므로 여러 가지 타입의 매개변수를 갖는 메서드를 만들어야 한다.</p>
<p>그러나 이와 같이 오버로딩하면, 컴파일 에러가 발생한다. <strong>제네릭 타입이 다른 것만으로는 오버로딩이 성립하지 않기 때문이다.</strong> 제네릭 타입은 컴파일러가 컴파일할 때만 사용하고 제거해버린다. 따라서 위 설명과 같은 경우에 메서드들은 오버로딩이 아니라 ‘메서드 중복 정의’가 된다.</p>
<p>이럴 때 사용하기 위해 고안된 것이 ‘와일드 카드’이다. 와일드 카드는 기호 <code>?</code>로 표현하며, 어떠한 타입도 될 수 있다.</p>
<p><code>?</code> 만으로는 Object타입과 다를 게 없으므로, 다음과 같이 상한(upper bound)과 하한(lower bound)을 제한할 수 있다.</p>
<pre><code class="language-java">&lt; ? extends T &gt;      와일드 카드의 상한 제한. T와 그 자손들만 가능
&lt; ? super T &gt;      와일드 카드의 하한 제한. T와 그 조상들만 가능
&lt; ? &gt;      제한 없음. 모든 타입이 가능. &lt; ? extends Object &gt; 와 동일(raw type)</code></pre>
 </br>

<h2 id="제네릭-메서드">제네릭 메서드</h2>
<p><strong>제네릭 메소드</strong>는  메서드의 선언부에 제네릭 타입이 선언된 메서드를 제네릭 메서드라 하며 선언된 제네릭으로 리턴 타입, 파라미터의 타입이 정해지는 메소드이다. </p>
<p><img src="https://images.velog.io/images/ljo_0920/post/1762041a-b6cc-4b54-9711-da221260759c/2.png" alt=""></p>
<p>그리고 중요한 점이 제네릭 클래스가 아닌 일반 클래스 내부에도 제네릭 메서드를 정의할 수 있다. 그 말은, 클래스에 지정된 타입 파라미터와 제네릭 메서드에 정의된 타입 파라미터는 상관이 없다는 것이다.</p>
<p> 즉, 제네릭 클래스에 <T> 를 사용하고, 같은 클래스의 제네릭 메서드에도 <T> 로 같은 이름을 가진 타입파라미터를 사용하더라도 둘은 <code>전혀 상관이 없다</code>는 것을 의미한다.</p>
<pre><code class="language-java"> class GenericClass&lt;T&gt; {
      ...
    static &lt;T&gt; void sort(List&lt;T&gt; list, Comparator&lt;? super T&gt; c) {
      ...
    }
  }</code></pre>
<p>위 코드에서 제네릭 클래스에 선언된 타입 매개변수 T와 제네릭 메서드 sort()에 선언된 타입 매개변수 T는 타입 문자만 같고 서로 다른 것이다. sort()가 static메서드이므로 타입 매개변수를 사용할 수 없지만, 메서드에 제네릭 타입을 선언하고 사용하는 것은 가능하다.</p>
<p>메서드에 선언된 제네릭 타입은 지역 변수를 선언한 것과 같다고 생각하면 된다. 이 타입 매개변수는 메서드 내에서만 지역적으로 사용될 것이므로 메서드가 static이건 아니건 상관이 없다.</p>
<p><strong>| 참고 | 같은 이유로 내부 클래스에 선언된 타입 문자가 외부 클래스의 타입 문자와 같아도 구별될 수 있다.</strong></p>
<p>제네릭 메서드를 호출할 때는 타입 변수에 타입을 대입해야 한다. 대부분의 경우 컴파일러가 타입을 추정할 수 있기 때문에 생략할 수 있다.</p>
</br>

<h2 id="erasure">Erasure</h2>
<p>제네릭은 타입의 안정성을 보장하며 실행시간에 오버헤드가 발생하지 않도록 하기 위해 추가 되었다. 컴파일러는 컴파일 시점에 제네릭에 대하여 <code>type erasure(타입 이레이저)</code>라고 부르는 프로세스를 적용한다. </p>
<p>이렇게 하는 주된 이유는 지네릭이 도입되기 이전의 소스 코드와의 <code>호환성을</code> 유지하기 위해서이다.</p>
<p>타입 이레이저는 모든 타입의 파라미터들을 제거하고 나서 그 자리를 제한하고 있는 타입으로 변경하거나 타입 파라미터의 제한 타입이 지정되지 않았을 경우에는 Object로 대체한다. 따라서 컴파일 후에 바이트 코드는 새로운 타입이 생기지 않도록 보장하는 일반 클래스들과 인터페이스, 메소드들만 포함한다. Object 타입도 컴파일 시점에 적절한 캐스팅이 적용된다.</p>
<pre><code class="language-java">public &lt;T&gt; List&lt;T&gt; genericMethod(List&lt;T&gt; list) {
    return list.stream().collect(Collectors.toList());
}</code></pre>
<p>타입 이레이저가 적용되면서 특정 타입으로 제한되지 않은 T는 Object로 대체된다.</p>
<pre><code class="language-java">public List&lt;Object&gt; withErasure(List&lt;Object&gt; list) {
    return list.stream().collect(Collectors.toList());
}

public List withErasure(List list) {
    return list.stream().collect(Collectors.toList());
}</code></pre>
<p>타입이 제한되어 있을 경우 그 타입은 컴파일 시점에 제한된 타입으로 교체된다.</p>
<pre><code class="language-java">public &lt;T extends Building&gt; void genericMethod(T t) {
    ...
}</code></pre>
<p>위 코드는 컴파일 후 다음과 같이 변경된다.</p>
<pre><code class="language-java">public void genericMethod(Building t) {
    ...
}</code></pre>
</br>

<h3 id="브릿지-메서드">브릿지 메서드</h3>
<p>java compiler는 제네릭의 타입안정성을 위해 Bridge Method도 만들어낼 수있다. </p>
<p>Bridge Method는 java 컴파일러가 컴파일 할 때 메서드 시그니처가 조금 다르거나 애매할 경우에대비하여 작성된 메서드이다. 이 경우는 파리미터화된 클래스나 인터페이스를 확장한 클래스를 컴파일 할 때 생길 수 있다.타입안정성을 위해 Bridge Method를 만들수도 있다.</p>
<pre><code class="language-java">public class IntegerStack extends Stack&lt;Integer&gt; {
    public Integer push(Integer value) {
        super.push(value);
        return value;
    }
}</code></pre>
<p>Java 컴파일러는 다형성을 제네릭 타입 소거에서도 지키기 위해, <code>IntegerStack</code>의 <code>push(Integer)</code> 메서드와 Stack의 <code>push(Object)</code> 메서드 시그니처 사이에 불일치가 없어야 했다. 따라서 컴파일러는 런타임에 해당 제네릭 타입의 타입소거를 위한 Bridge 메서드를 만드는데 아래와같은 방식으로 만든다.</p>
<pre><code class="language-java">public class IntegerStack extends Stack {
// Bridge method generated by the compiler

    public Integer push(Object value) {
        return push((Integer) value);
    }

    public Integer push(Integer value) {
        return super.push(value);
    }
}</code></pre>
<p>즉 <code>extends Stack&lt;Integer&gt;</code> -&gt; <code>Stack</code> 으로  변경한 것을 볼 수 있으며, <code>push</code>에 parameter를 <code>Object</code>가 아닌 <code>Integer</code>로 맞추기 위한 도우미 메서드가 늘어났다는 것을 알 수 있다. 결과적으로 <code>Stack</code> 클래스의 push method는 타입소거를 진행한 후에, <code>IntegerStack</code> 클래스의 원본 push 방법을 사용하게 한다.</p>
</br>

<h2 id="참고-출처">참고 출처</h2>
<ul>
<li><a href="https://yadon079.github.io/2021/java%20study%20halle/week-14">https://yadon079.github.io/2021/java study halle/week-14</a></li>
<li><a href="https://tecoble.techcourse.co.kr/post/2020-11-09-generics-basic/">https://tecoble.techcourse.co.kr/post/2020-11-09-generics-basic/</a></li>
<li><a href="https://yaboong.github.io/java/2019/01/19/java-generics-1/">https://yaboong.github.io/java/2019/01/19/java-generics-1/</a></li>
<li><a href="https://devlog-wjdrbs96.tistory.com/201">https://devlog-wjdrbs96.tistory.com/201</a></li>
<li><a href="https://devlog-wjdrbs96.tistory.com/201">https://devlog-wjdrbs96.tistory.com/201</a></li>
<li><a href="https://jyami.tistory.com/99">https://jyami.tistory.com/99</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[전략 패턴]]></title>
            <link>https://velog.io/@ljo_0920/%EC%A0%84%EB%9E%B5-%ED%8C%A8%ED%84%B4</link>
            <guid>https://velog.io/@ljo_0920/%EC%A0%84%EB%9E%B5-%ED%8C%A8%ED%84%B4</guid>
            <pubDate>Thu, 10 Feb 2022 05:37:00 GMT</pubDate>
            <description><![CDATA[<h1 id="전략-패턴">전략 패턴</h1>
<p><a href="https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=582754">헤드 퍼스트 디자인 패턴</a>을 읽고 정리한 글입니다.</p>
<blockquote>
<p><strong>전략 패턴</strong> 은 알고리즘군을 정의하고 각각의 알고리즘을 캡슐화하며 교환해서 사용할 수 있도록 만든다. 전략을 사용하면 알고리즘을 사용하는 클라이언트와는 독립적으로 알고리즘을 변경할 수 있다.</p>
</blockquote>
</br>

<h2 id="개요">개요</h2>
<p>오리 연못 시뮬레이션 게임에서 오리는 헤엄도  치고 꽥꽥거리는 소리도 내는 매우 다양한 오리 종류를 보여줄 수 있다. 가장 처음에는 표준적인 객체지향 기법을 사용하여 Duck이라는 수퍼클래스를 만든 다음, 그 클래스를 확장하여 다른 모든 종류의 오리를 생성</p>
<h3 id="예제-코드">예제 코드</h3>
<pre><code class="language-java">public abstract class Duck {

    void quack() {
        System.out.println(&quot;duck quack&quot;);
    }

    void swim() {
        System.out.println(&quot;duck swim&quot;);
    }

    abstract void display();

}

class MallardDuck extends Duck {

    @Override
    void display() {
        System.out.println(&quot;MallardD Duck&quot;);
    }
}

class RedheadDuck extends Duck {

    @Override
    void display() {
        System.out.println(&quot;Redhead Duck&quot;);
    }
}</code></pre>
<p>이제 오리들이 날아다닐 수 있도록 해야한다고 가정했을 경우 어떻게 해야할까??</p>
</br>


<pre><code class="language-java">public abstract class Duck {

    void quack() {
        System.out.println(&quot;duck quack&quot;);
    }

    void swim() {
        System.out.println(&quot;duck swim&quot;);
    }

    abstract void display();

    void fly() {
        System.out.println(&quot;duck fly&quot;);
    }

}</code></pre>
<p>슈퍼클래스에 fly()메소드를 추가함으로써 모든 서브 클래스에서 fly()를 상속받도록 수정</p>
</br>


<pre><code class="language-java">class MallardDuck extends Duck {

    @Override
    void display() {
        System.out.println(&quot;Mallard Duck&quot;);
    }
}

class RedheadDuck extends Duck {

    @Override
    void display() {
        System.out.println(&quot;Redhead Duck&quot;);
    }
}

class RubberDuck extends Duck {

    @Override
    void quack() {
        System.out.println(&quot;rubber duck quack&quot;);
    }

    @Override
    void display() {
        System.out.println(&quot;Rubber Duck&quot;);
    }
}</code></pre>
<p>하지만 해당 변경사항은 모든 오리가 날 수 있지 않다는 것을 간과했다. 고무로 된 오리 인형 클래스에도 비행 기능하게 된 것이다. 결론적으로 Duck 슈퍼클래스에 fly() 메소드가 추가되면서 일부 서브클래스에는 적합하지 않은 행동이 추가될 수 있는 사이드 이펙트가 발생하게 된 것이다. </p>
<p>RubberDuck의 quack() 메소드처럼 fly() 오버라이드를 해서 아무것도 하지 않게 한다면 가능은 하지만 어떤 부작용이 있을까??</p>
</br>

<h3 id="상속의-문제점"><strong>상속의 문제점</strong></h3>
<p>이처럼 Duck 행동을 제공하는데 있어서 상속을 사용할 때의 단점은 많다.</p>
<ul>
<li>서브 클래스에서 코드가 중복</li>
<li>모든 서브클래스의 행동을 알기가 어려움</li>
<li>코드를 변경했을 때 원치 않는 서브클래스들에게 영향을 끼칠 수 있음</li>
<li>실행시에 특징 변경 어려움</li>
<li>모든 오리의 행동을 알기 힘듬</li>
</ul>
</br>

<h3 id="인터페이스">인터페이스?</h3>
<blockquote>
<p>그렇다면 상속 대신 Flyable, Quackable 인터페이스를 사용하는 방법은 어떨까??</p>
</blockquote>
<p>서브클래스에서 Flyable, Quackable 인터페이스를 구현함으로써 고무오리의 비행기능 같은 문제점을 해결할 수 있지만, 코드 재사용성과 관리측면에서 더욱 큰 문제점이 발생하게 된다. (Java 8이하라고 가정)</p>
</br>

<h2 id="문제를-명확하게-파악하기">문제를 명확하게 파악하기</h2>
<p><strong>디자인원칙1</strong> <code>달라지는 부분을 찾아내고, 달라지지 않는 부분으로부터 분리킨다.</code></p>
<p>결국 가장 중요한 것은 달라지는 부분을 찾아서 나머지 코드에 영향을 주지 않도록 캡슐화함으로써 나머지 부분에 영향을 미치지 않도록 한다면 시스템의 유연성을 향상시키는 것이다.</p>
<p>즉 확장에는 열려있고 수정에 대해서는 닫혀있도록 하는 것!!</p>
<p>모든 패턴은 시스템의 일부분을 다른 부분과 독립적으로 변화시킬 수 있는 방법을 제공하기 위한 것이다.</p>
</br>

<h2 id="스트레티지-패턴-straetegy-패턴-">*<em>스트레티지 패턴 (Straetegy 패턴) *</em></h2>
<h3 id="duck-예제에서-달라지는-부분-뽑아보기"><strong>Duck 예제에서 달라지는 부분 뽑아보기</strong></h3>
<p>Duck 클래스에서 나는 행동과 꽥꽥거리는 행동을 추출하여 행동을 나타낼 클래스 집합을 새로 추가</p>
<ul>
<li>최대한 유연하게 만들어야 한다.</li>
<li>오리의 행동을 동적으로 바꾸고싶다<ul>
<li>세터 메소드를  포함시켜서 동적으로 바꿀 수 있도록 한다면 좋을 것이다.</li>
</ul>
</li>
</ul>
<p><strong>디자인원칙2 <code>구현이 아닌 인터페이스에 맞춰서 프로그래밍한다</code></strong></p>
<p>각 행동들을 인터페이스 (FlyBehavior, QuackBehavior)로 표현하고 행동을 구현할 때 Duck 클래스에서 구현하느 것이 아닌 구체적인 행동 클래스에서 구현함으로써 특정 행동은 Duck 국한되지 않는다.</p>
<pre><code class="language-java">public interface FlyBehavior {
    void fly();
}

public class FlyNoWay implements FlyBehavior{

    @Override
    public void fly() {
        System.out.println(&quot;날 수 없다&quot;);
    }
}

public class FlyWithWings implements FlyBehavior{

    @Override
    public void fly() {
        System.out.println(&quot;나는 방법을 구현&quot;);
    }
}
</code></pre>
<pre><code class="language-java">public interface QuackBehavior {

    void quack();
}

public class Quack implements QuackBehavior {

    @Override
    public void quack() {
        System.out.println(&quot;꽥&quot;);
    }
}

public class MuteQuack implements QuackBehavior {

    @Override
    public void quack() {
        System.out.println(&quot;소리 낼 수 없어요&quot;);
    }
}

public class Squeak implements QuackBehavior {

    @Override
    public void quack() {
        System.out.println(&quot;삑삑&quot;);
    }
}</code></pre>
<p>이로써 비행과 꽥꽥거리는 행동들은 Duck 클래스 안에 숨겨져 있지 않으므로 다른 객체에서도 이러한 행동들을 재사용할 수 있다. 그리고 기존의 Duck 클래스들을 전혀 수정하지 않고도 새로운 행동을 추가할 수 있다.</p>
<p>상속의 단점을 해결하는 것 뿐만 아니라 재사용성을 가질 수 있다.</p>
</br>

<h2 id="결과">결과</h2>
<p>이제 Duck 클래스는 행동을 다른 클래스에 위임함으로써 특정 행동을 할 수 있게 되었다.</p>
<pre><code class="language-java">public abstract class Duck {

    private FlyBehavior flyBehavior;
    private QuackBehavior quackBehavior;

    public Duck(FlyBehavior flyBehavior, QuackBehavior quackBehavior) {
        this.flyBehavior = flyBehavior;
        this.quackBehavior = quackBehavior;
    }

    public void performFly() {
        flyBehavior.fly();
    }

    public void performQuack() {
        quackBehavior.quack();
    }

    public abstract void display();

    public void swim() {
        System.out.println(&quot;모든 오리는 수영한다.&quot;);
    }
}

class RubberDuck extends Duck {

    public RubberDuck(FlyBehavior flyBehavior, QuackBehavior quackBehavior) {
        super(flyBehavior, quackBehavior);
    }

    @Override
    public void display() {
        System.out.println(&quot;Rubber Duck&quot;);
    }
}

class MallardDuck extends Duck {

    public MallardDuck(FlyBehavior flyBehavior, QuackBehavior quackBehavior) {
        super(flyBehavior, quackBehavior);
    }

    @Override
    public void display() {
        System.out.println(&quot;Mallard Duck&quot;);
    }
}

class RedheadDuck extends Duck {

    public RedheadDuck(FlyBehavior flyBehavior, QuackBehavior quackBehavior) {
        super(flyBehavior, quackBehavior);
    }

    @Override
    public void display() {
        System.out.println(&quot;Redhead Duck&quot;);
    }
}</code></pre>
</br>

<h2 id="결론">결론</h2>
<p><img src="https://images.velog.io/images/ljo_0920/post/3f317e41-8275-45de-b7ae-5ba5c0697b51/1.png" alt=""></p>
<p>여태까지 사용했던 행동들을 일련의 행동이 아닌 알고리즘군으로 생각하고 똑같은 테크닉을 적용가능 하다.</p>
<p>ex) 지역별 세금 계산 방식 등등</p>
<p>각 오리에는 나는 행동과 꽥꽥하는 행동을 위임하기 위한 클래스들이 있다. 이처럼 두 클래스를 합치는 것을 구성(Composition)을 이용하는 것이라 한다. 오리 클래스에서는 행동을 상속받는 대신, 행동 객체로 구성됨으로써 행동을 부여받게 된다. 구성을 이용하면 유연성을 크게 향상시킬 수 있다. 캡슐화할 수 있도록 만들어주는 것 뿐만 아니라 실행시에 행동을 바꿀 수도 있게 해주기 때문이다.</p>
<p><strong>디자인 원칙3: 상속보다는 구성을 활용한다.</strong></p>
</br>

<h2 id="참고-출처">참고 출처</h2>
<ul>
<li><a href="https://ichi.pro/ko/dijain-paeteon-225739643040371">https://ichi.pro/ko/dijain-paeteon-225739643040371</a></li>
<li><a href="https://juneyr.dev/2019-04-17/design-pattern-strategy">https://juneyr.dev/2019-04-17/design-pattern-strategy</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[JVM 아키텍처 - 1]]></title>
            <link>https://velog.io/@ljo_0920/JVM-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-1</link>
            <guid>https://velog.io/@ljo_0920/JVM-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-1</guid>
            <pubDate>Thu, 03 Feb 2022 06:56:08 GMT</pubDate>
            <description><![CDATA[<h2 id="jvm이란-무엇인가">JVM이란 무엇인가</h2>
<p>JVM은 <code>Java virtual machine</code>을 줄인 것으로 자바를 실행하기 위한 가상 컴퓨터이다.</p>
<p>자바로 작성된 애플리케이션은 모두 JVM에서만 실행되기 때문에, 자바 애플리케이션을 실행하기 위해서는 JVM이 반드시 필요하다. <strong>컴파일러</strong>는 Java 파일을 <strong>.class</strong> 파일로 컴파일한 다음 해당 .class 파일을 JVM에 입력하여 클래스 파일을 로드하고 실행한다.</p>
<h3 id="worawrite-once-run-anywhere">WORA(Write once, run anywhere)</h3>
<p>이 과정을 통하여 Java 는 높은 이식성이라는 큰 장점을 얻을 수가 있었다.  어느 기기나 운영체제에 상관없이 JVM 이 설치 및 구동될 수 있는 환경이라면 Java 로 작성한 프로그램은 실행이 가능하기 때문에 다른 운영체제에 맞춰서 컴파일을 해줘야 하는 다른 언어보다 높은 이식성을 가질 수 있게 되었다.</p>
<p>단, JVM은 OS에 종속적이기 때문에 해당 OS에서 실행가능한 JVM이 필요하다.</p>
<blockquote>
<p>JVM은 바이트 코드를 이해하는 것이지 자바 코드를 이해하는 것이 아니다. 코틀린 또한 코틀린 코드를 바이트 코드로 컴파일해서 JVM 위에서 동작한다.</p>
</blockquote>
<h3 id="jvm의-특성">JVM의 특성</h3>
<ul>
<li>스택 기반의 가상 머신</li>
<li>단일 상속 형태의 객체 지향 프로그래밍을 가상 머신 수준에서 구현</li>
<li>포인터를 지원. 단, C와 같이 주소 값을 임의로 조작이 가능한 포인터 연산은 불가능</li>
<li>Garbage collection 수행</li>
<li>플랫폼의 독립성 보장</li>
<li>Data Flow Analysis에 기반한 <strong>자바 바이트코드 검증기</strong>를 통해 문제를 실행 전에 검증하여 실행 시 안전을 보장하고 별도의 부담을 줄여줌</li>
</ul>
</br>

<h2 id="바이트코드란-무엇인가">바이트코드란 무엇인가</h2>
<h3 id="바이너리-코드">바이너리 코드</h3>
<p>CPU가 이해하기 위한 기계어는 0과 1로 구성된 바이너리 코드(이진 코드)이다. 기계어가 이진 코드로 이루어졌을 뿐 모든 이진 코드가 기계어인 것은 아니다.</p>
<p><strong>바이너리 코드 != 기계어</strong></p>
<h3 id="바이트-코드">바이트 코드</h3>
<p>0과 1로 이루어진 이진 코드이지만 바이너리 코드와 달리 가상머신이 이해할 수 있는 코드이다. 사람에게 친숙한 고급 언어보다는 덜 추상적이지만 기계어보다는 추상적이다.</p>
<p>고급언어로 작성된 코드를 가상머신이 이해할 수 있도록 컴파일한 것이다. CPU에게 넘어가기 전에 <strong>실시간 번역기 또는 JIT(just-in-time) 컴파일러</strong>에 의해 바이너리 코드로 변환된다.</p>
<h3 id="정리">정리</h3>
<p>Java는 OS와 직접적으로 대화할 수 없다. 오로지 JVM하고만 상호작용을 한다. 자바는 JVM을 거쳐야만 OS와 대화할 수 있다.</p>
<p>바이너리 코드와 바이트 코드 둘 다 0과 1로 이루어져 있다. 바이너리 코드는 CPU가 이해할 수 있는 언어, 바이트 코드는 가상 머신이 이해할 수 있는 언어이다.</p>
<p>그 중에 JVM을 위한 바이트 코드를 <strong>“자바 바이트코드”</strong>라고 한다.</p>
</br>

<h2 id="jvm-구성-요소">JVM 구성 요소</h2>
<p>JVM은 크게 세 가지 구성요소로 볼 수 있다.</p>
<ul>
<li>Class Loader</li>
<li>Runtime Data Area</li>
<li>Execution Engine</li>
</ul>
<p><img src="https://images.velog.io/images/ljo_0920/post/0ec8f2f6-056e-47ce-b050-8bd9775dfadc/1.png" alt="">
출처: <a href="https://dzone.com/articles/jvm-architecture-explained">https://dzone.com/articles/jvm-architecture-explained</a></p>
<h2 id="class-loader">Class Loader</h2>
<p>JDK 에서 개발하고, JRE 를 통해서 환경을 제공받은 JVM 은 compile 된 바이트 코드를 탑재하여 로직을 실행하게 됩니다. 그렇다면 JVM 에 Class 는 어떻게 로드되는 것일까??</p>
<p>바로 그 역할을 하는 것이 자바 클래스로더이다. 클래스 로더는 자바 클래스를 JVM으로 동적 로드하는 JRE(자바 런타임 환경)의 일부이다. 클래스 파일을 로드하는데 사용되는 하위 시스템이다.</p>
<p>Compile time이 아닌, <code>Runtime</code>시 처음으로 한 번만 동적으로 클래스를 로드하며, jar 파일 내에 저장된 클래스들을 JVM 위에 탑재하고 사용하지 않는 클래스들은 메모리에서 삭제한다.변환된 바이트 코드 파일(.class)을 JVM이 운영체제로부터 할당 받은 메모리 영역인 Runtime Data Area로 <strong>“적재”</strong>하는 역할을 한다.</p>
<p>ClassLoader 는 클래스 파일을 찾아서 탑재하는 역할뿐만이 아니라 jvm 에 관련된 다른 일들도 같이 한다. </p>
<p>크게 <code>Loading</code>, <code>Linking</code>, 그리고 <code>Initialization</code> 3가지 역할을 맡게 된다.</p>
<ul>
<li>Loading 은 클래스 파일을 탑재하는 과정</li>
<li>Linking 은 클래스 파일을 사용하기 위해 검증하고, 기본 값으로 초기화하는 과정</li>
<li>Initialization 은 static field 의 값들을 정의한 값으로 초기화를 하는 과정</li>
</ul>
</br>

<h2 id="runtime-data-area">Runtime Data Area</h2>
<p>이렇게 탑재하는 클래스 파일들은 JVM 에서 어떤 영역을 차지하고 있을까? JVM 의 Run-Time Data Area는 프로그램을 수행하기 위해 OS에서 할당받은 메모리 공간이며, 크게 Method Area , Heap , Java Stacks , PC registers 그리고 Native Method Stacks 가 존재한다. </p>
<p><img src="https://images.velog.io/images/ljo_0920/post/99c7d46a-b521-4b3f-9176-a64fd473fe45/2.png" alt=""></p>
<p>출처: <a href="https://tecoble.techcourse.co.kr/post/2021-08-09-jvm-memory/">https://tecoble.techcourse.co.kr/post/2021-08-09-jvm-memory/</a></p>
<h3 id="method-area">Method Area</h3>
<ul>
<li>Method Area 에는 인스턴스 생성을 위한 객체 구조, 생성자, 필드 등이 저장된다. Runtime Constant Pool 과 static 변수, 그리고 메소드 데이터와 같은 Class 데이터들도 이곳에서 관리 된다.</li>
<li>즉, 정적 변수를 포함하여 모든 클래스 수준 데이터가 여기에 저장된다.</li>
<li>JVM당 하나의 메소드 영역만 있으며 공유 자원입니다.다른 스레드에서도 활용 가능한 공유자원이다.</li>
<li>다중 스레드에 대한 메모리를 공유하므로 저장된 데이터는 스레드에 안전하지 않다</li>
</ul>
<h3 id="heap">Heap</h3>
<ul>
<li>모든 객체와 해당 인스턴스(instance) 변수 및 배열, String pool이 여기에 저장됩니다.</li>
<li>JVM 당 역시 하나만 생성이 되고, 해당 영역이 가진 데이터는 모든 Java Stack 영역에서 참조되어, Thread 간 공유가 됩니다.</li>
<li>다중 스레드에 대한 메모리를 공유하므로 저장된 데이터는 스레드에 안전하지 않다</li>
<li>GC의 주 대상</li>
</ul>
<h3 id="native-method-stack">Native Method Stack</h3>
<ul>
<li>순수하게 Java 로 구성된 코드만을 사용할 수 없는 시스템의 자원이나 API 가 존재합니다.</li>
<li>다른 프로그래밍 언어로 작성된 메소드들을 Native Method 라고 합니다.</li>
<li>일반적인 메소드를 실행하는 경우 JVM Language Stack에 적재되지만, 네이티브 메소드 스택은 네이티브 라이브러리에 따라 네이티브 코드 명령(C언어와 같이 네이티브 방식으로 작성된 메소드)을 보관한다.</li>
</ul>
<h3 id="pc-register">PC Register</h3>
<ul>
<li>Java 에서 Thread 는 각자의 메소드를 실행하게 됩니다. 이때, Thread 별로 동시에 실행하는 환경이 보장되어야 하므로 최근에 실행 중인 JVM 에서는 명령어 주소값을 저장할 공간이 필요합니다.</li>
<li>이 부분을 PC Registers 영역이 관리하여 추적해주게 됩니다. Thread 들은 각각 자신만의 PC Registers 를 가지고 있습니다.</li>
<li>만약 실행했던 메소드가 네이티브하다면 undefined 가 기록이 됩니다. 실행했던 메소드가 네이티브하지 않다면, PC Registers 는 JVM 에서 사용된 명령의 주소 값을 저장하게 됩니다.</li>
</ul>
<h3 id="stack">Stack</h3>
<ul>
<li>프로그램 실행과정에서 임시로 할당되었다가 메소드를 빠져나가면 바로 소멸되는 특성의 데이터를 저장하기 위한 영역이다.</li>
<li>각종 형태의 변수나 임시 데이터, 스레드나 메소드의 정보를 저장하고 호출된 메서드의 매개변수, 지역변수, 리턴 값 및 연산 시 일어나는 값들을 임시로 저장한다.</li>
<li>각 스레드에는 자체 JVM 스택이 있고, 스레드가 생성될 때 동시에 생성된다.</li>
<li>각 Thread 별로 따로 할당되는 영역이므로 Heap 메모리 영역보다 비교적 빠르다는 장점이 있다. 또한, 각각의 Thread 별로 메모리를 따로 할당하기 때문에 동시성 문제에서 자유롭다.</li>
</ul>
</br>

<h2 id="execution-engine">Execution Engine</h2>
<p>런타임 데이터 영역에 할당된 바이트코드는 실행 엔진에 의해 실행된다. Execution Engine은 바이트코드를 읽고 자바 바이트 코드를 JVM 내부에서 컴퓨터가 실행할 수 있는 형태인 바이너리 코드로 변경하며 하나씩 실행한다.</p>
<p>변경하는 방식은 두가지가 있는데, 인터프리터 방식과 JIT 방식이 있다.</p>
<h3 id="인터프리터-방식">인터프리터 방식</h3>
<ul>
<li>기본 바이트 코드를 실행하는 방법은 인터프리터 방식이 기본이다. 자바 바이트 코드를 명령어 단위로 읽어서 실행하기 때문에 느리다.</li>
</ul>
<h3 id="jitjust-in-time-compiler">JIT(just-in-time) <strong>Compiler</strong></h3>
<ul>
<li>JIT 컴파일러는 인터프리터의 단점을 해결한다. 실행 엔진은 바이트 코드를 변환하는 데 인터프리터의 도움을 사용할 것이지만 반복되는 코드를 발견하면 전체 바이트코드를 컴파일하여 네이티브 코드로 변경하는 JIT 컴파일러를 사용한다. 이 네이티브 코드는 반복 메서드 호출에 직접 사용되어 시스템 성능을 향상시킨다.</li>
</ul>
<h3 id="garbage-collector"><strong>Garbage Collector</strong></h3>
<ul>
<li>참조되지 않은 객체를 수집하고 제거한다. JVM의 가비지 컬렉션은 생성된 객체를 수집한다.</li>
</ul>
<h2 id="jvm-동작-간단-정리">JVM 동작 간단 정리</h2>
<p>JVM 구성 요소는 다음과 같다.</p>
<ol>
<li>클래스 로더 컴파일러가 내부에 만든 .class(바이트 코드)를 런타임 데이터 공간에 <strong>“적재”</strong>한다.</li>
<li>런타임 데이터 공간 OS로부터 메모리를 할당받은 공간으로 스택, 힙, 메소드, 네이티브 메소드, PC 레지스터가 있다.</li>
<li>실행 엔진인터프리터 방식 또는 JIT 컴파일러를 이용하여 데이터 영역에 배치된 바이트 코드를 실행한다.</li>
</ol>
<ul>
<li>JIT 컴파일러는 바이트 코드를 바이너리 코드로 변환하는 속도가 느린 인터프리터 방식을 보완하기 위해 나온 것이다.</li>
<li>인터프리터 방식으로 기계어 코드를 생성하면서 그 코드를 캐싱하여, 같은 함수가 여러 번 불릴 때 매번 기계어 코드를 생성하는 것을 방지한다.</li>
<li>JVM 내부에서는 자바 컴파일러가 자바 프로그램 코드를 바이트 코드로 변환시킨 후 실제 바이트 코드가 실행하는 시점에서 JIT 컴파일러를 통해 기계어로 변환한다.</li>
</ul>
<ol>
<li>GC는 <em>JVM</em> 상에서 더 이상 사용되지 않는 데이터가 할당되어있는 메모리를 해제시킨다.</li>
</ol>
</br>

<h2 id="컴파일-하는-방법">컴파일 하는 방법</h2>
<h3 id="컴파일이란">컴파일이란?</h3>
<p>컴파일러는 특정 프로그래밍 언어로 쓰여 있는 문서를 다른 프로그래밍 언어로 옮기는 프로그램을 말한다. 기존 문서를 소스 코드 혹은 원시 코드라고 부르고, 출력된 문서를 목적 코드라고 부른다.원시 코드에서 목적 코드로 옮기는 과정을 컴파일이라고 한다.</p>
<h3 id="자바-컴파일-과정">자바 컴파일 과정</h3>
<ol>
<li>소스 파일 생성한다. (Hello.java)</li>
<li>자바 컴파일러(javac.exe)를 사용하여 컴파일한다. <code>$ javac Hello.java</code></li>
<li>컴파일이 정상적으로 완료되면 클래스 파일 생성된다. (Hello.class)</li>
</ol>
<h2 id="실행하는-방법">실행하는 방법</h2>
<p>자바 인터프리터(java.exe)로 실행한다. <code>$ java Hello</code>실행 시에는 확장자를 붙이지 않는다.</p>
<p>내부적인 진행순서는 다음과 같다.</p>
<ol>
<li>프로그램의 실행에 필요한 클래스(*.class파일)을 로드한다.</li>
<li>클래스파일을 검사한다.(파일형식, 악성코드 체크)</li>
<li>지정된 클래스(Hello)에서 <code>main(String[] args)</code>을 호출한다.</li>
</ol>
</br>

<h2 id="jdk와-jre의-차이">JDK와 JRE의 차이</h2>
<h3 id="jdk란">JDK란?</h3>
<p>JDK는 Java Development Kit으로 자바 프로그래밍 시 필요한 컴파일러 등을 포함한다. JDK는 JRE를 포함하며, 개발을 위해 필요한 도구(java, javac 등)를 포함한다.</p>
<h3 id="jre란">JRE란?</h3>
<p>JRE는 Java Runtime Enviroment로 컴파일된 자바 프로그램을 실행시킬 수 있는 자바 환경을 말한다. JRE는 JVM의 실행환경을 구현했다고 볼 수 있으며, JVM이 자바 프로그램을 동작시킬 때 필요한 라이브러리 파일들과 기타 파일들을 가지고 있다.</p>
<p><img src="https://images.velog.io/images/ljo_0920/post/07e195c1-79dc-4931-bc5c-8d98df4a9eb6/3.png" alt=""></p>
<p><img src="https://images.velog.io/images/ljo_0920/post/9011eb7a-74de-4d26-8abd-bbd9fa9904ef/4.png" alt=""></p>
</br>

<h2 id="javac-옵션">javac 옵션</h2>
<p>더 자세한 javac의 standard options은 <a href="https://docs.oracle.com/javase/8/docs/technotes/tools/windows/javac.html">공식 문서</a>에서 볼 수 있다.</p>
<p>  <code>$ javac &lt;options&gt; &lt;source files&gt;</code></p>
<h3 id="-cp-path-or--classpath-path">-<strong>cp <em>path</em> or -classpath <em>path</em></strong></h3>
<ul>
<li>컴파일러가 참조할 클래스 파일들을 찾기 위해서 컴파일 시 파일경로를 지정해주는 옵션이다.해당 옵션을 쓰지 않는 경우(classpath가 지정되지 않는 경우) 사용자 클래스 경로는 현재 디렉터리가 된다.</li>
</ul>
<h3 id="-d-directory">-<strong>d <em>directory</em></strong></h3>
<ul>
<li>클래스 파일의 대상 디렉터리를 설정한다. javac가 별도의 디렉터리를 만들지 않기 때문에 디렉터리는 미리 만들어둬야 한다.</li>
</ul>
<h3 id="-deprecation">-<strong>deprecation</strong></h3>
<ul>
<li>사용되지 않는 멤버 또는 클래스의 사용 또는 오버라이드에 대한 설명을 표시한다.해당 옵션이 없는 javac는 사용되지 않는 멤버나 클래스를 사용하거나 재정의하는 소스 파일의 요약을 보여준다.</li>
</ul>
<h3 id="-g">-<strong>g</strong></h3>
<ul>
<li>로컬 변수를 포함한 모든 디버깅 정보를 생성한다.<code>g:none</code> : 디버깅 정보를 생성하지 않는다.<code>g:{source, lines, vars}</code> : 소스파일 정보, 라인 정보, 지역변수의 디버깅 정보를 생성한다.</li>
</ul>
<h3 id="-source-release">-<strong>source <em>release</em></strong></h3>
<ul>
<li>소스 코드의 버전을 지정한다.</li>
</ul>
<h3 id="-target-version">-<strong>target <em>version</em></strong></h3>
<ul>
<li>가상 시스템의 지정된 릴리스를 대상으로 하는 클래스 파일을 생성한다. 클래스 파일은 지정된 대상 및 이후 릴리스에서 실행되지만 이전 릴리스의 JVM에서는 실행되지 않는다.</li>
</ul>
</br>

<h2 id="참고-출처">참고 출처</h2>
<ul>
<li><a href="https://ko.wikipedia.org/wiki/">https://ko.wikipedia.org/wiki/</a></li>
<li><a href="https://asfirstalways.tistory.com/158">https://asfirstalways.tistory.com/158</a></li>
<li><a href="https://jdm.kr/blog/188">https://jdm.kr/blog/188</a></li>
<li><a href="https://jojuim.tistory.com/entry/%EA%B8%B0%EB%B3%B8-%EB%B0%94%EC%9D%B4%ED%8A%B8%EC%BD%94%EB%93%9C%EC%99%80-%EB%B0%94%EC%9D%B4%EB%84%88%EB%A6%AC%EC%BD%94%EB%93%9C">https://jojuim.tistory.com/entry/기본-바이트코드와-바이너리코드</a></li>
<li><a href="https://docs.oracle.com/javase/8/docs/">https://docs.oracle.com/javase/8/docs/</a></li>
<li><a href="https://yadon079.github.io/2020/java%20study%20halle/week-01-feedback">https://yadon079.github.io/2020/java study halle/week-01-feedback</a></li>
<li><a href="https://tecoble.techcourse.co.kr/post/2021-07-12-jvm-jre-jdk/">https://tecoble.techcourse.co.kr/post/2021-07-12-jvm-jre-jdk/</a></li>
<li><a href="https://tecoble.techcourse.co.kr/post/2021-08-30-jvm-gc/">https://tecoble.techcourse.co.kr/post/2021-08-30-jvm-gc/</a></li>
<li><a href="https://yadon079.github.io/2020/java%20study%20halle/week-01">https://yadon079.github.io/2020/java study halle/week-01</a></li>
<li><a href="https://dzone.com/articles/jvm-architecture-explained">https://dzone.com/articles/jvm-architecture-explained</a></li>
<li><a href="https://tecoble.techcourse.co.kr/post/2021-08-09-jvm-memory/">https://tecoble.techcourse.co.kr/post/2021-08-09-jvm-memory/</a></li>
<li><a href="https://tecoble.techcourse.co.kr/post/2021-08-30-jvm-gc/">https://tecoble.techcourse.co.kr/post/2021-08-30-jvm-gc/</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[static inner vs non-static inner class ]]></title>
            <link>https://velog.io/@ljo_0920/static-inner-vs-non-static-class-inner</link>
            <guid>https://velog.io/@ljo_0920/static-inner-vs-non-static-class-inner</guid>
            <pubDate>Thu, 03 Feb 2022 05:48:51 GMT</pubDate>
            <description><![CDATA[<p>프로젝트를 진행하며 리턴하는 api의 요청 혹은 응답의 DTO 클래스들은 데이터의 형식에 따라 많은 수가 필요하곤 했는데 재사용성을 줄이는 대신 관리적인 측면을 위해서 dto 클래스를 매번 만들지 않고 static inner 클래스로 사용함으로써 하나의 dto만으로 완전히 관리될 수 있도록 사용하도록 컨벤션을 정하였다. 프로젝트가 끝난 후 조금 더 깊이 정리하기 위해 중첩 클래스(Nested Class)는 무엇이고 4가지 종류 중 <code>정적 내부 클래스</code>와 <code>비정적 내부 클래스</code>에 대해 다뤄보고자 한다.</p>
</br>

<h2 id="중첩-클래스란nested-class"><strong>중첩 클래스란(Nested Class)</strong></h2>
<p>먼저 중첩 클래스에 대해 설명을 해야하는데, 중첩 클래스란 말 그대로 다른 클래스의 내부에 존재하는 클래스를 의미한다. 중첩클래스를 포함하는 외부 클래스를 Outer class라고 하며 내부에 포함된 클래스를 nested class 또는 Inner class라고 한다. </p>
<p>중첩 클래스는 4가지 종류가 존재한다.</p>
<ul>
<li>정적 멤버 클래스(static inner class) : static 키워드를 이용해서 클래스가 정의된 경우</li>
<li>비정적 멤버 클래스(non-static inner class) : Outer 클래스의 멤버변수나 메소드처럼 클래스가 정의된 경우</li>
<li>익명 클래스(anonymous class) : 익명 클래스를 이용해서 클래스가 정의된 경우</li>
<li>지역 클래스(local class) : Outer 클래스의 특정 메소드 또는 초기화 블록에서 클래스가 정의된 경우</li>
</ul>
<pre><code class="language-java">class Outer {
        class InstanceInner {} // non static inner class
        static class StaticInner {}  // static inner class

        void myMethod() {      // local class
                class LocalInner {}
        }

        // anonymous class
    Ex ex = new Ex() { 
      public void getEx() {
        System.out.println(&quot;anonymous class&quot;);
      }
    };
  }
}

interface Ex {
  public void getEx();
}</code></pre>
<p>중첩 클래스는 특정 클래스를 자신의 클래스 내부적인 용도로만 사용하고자 할때 효율적이기 때문에  불필요한 노출을 줄이면서 캡슐화를 통해 유지 보수하기 좋은 코드를 작성하게 된다.</p>
<p>이펙티브 자바에서는 중첩 클래스는 자신을 감싼 바깥 클래스에서만 쓰여야 하며, 그 외의 쓰임새가 있다면 톱 레벨 클래스로 만들어야 한다고 권장하고 있으며 특히 <code>static</code> 으로 선언하도록 권장한다. (아이템 24) </p>
<p>그렇다면 왜인지 알아보자!!</p>
</br>

<h2 id="정적-멤버-클래스">정적 멤버 클래스</h2>
<ul>
<li>static이 붙는 중첩 클래스</li>
<li>동일한 static 멤버들을 사용 가능</li>
<li>static의 특징에 따라 외부 인스턴스 멤버의 직접참조가 불가능</li>
</ul>
<p><code>static</code> 예약어가 있음으로 인해 독립적으로 생성할 수 있다. </p>
<p>정적 멤버 클래스는 바깥 클래스의 private 멤버에도 접근할 수 있다는 점을 제외하고 일반 클래스와 쓰임새는 동일하다.</p>
<pre><code class="language-java">class Outer {
    static int x = 10;
    int y = 20;
    private static int z = 30;

    static class StaticInner {  // static inner class
        void get() {
            System.out.println(&quot;x: &quot; + x);
            System.out.println(&quot;z: &quot; + z);
        }
    }
}

Outer.StaticInner staticIneer = new Outer.StaticInner();
staticIneer.get();</code></pre>
<p><img src="https://images.velog.io/images/ljo_0920/post/5b18c53a-d468-4732-8d46-a8c206a82bfe/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202022-02-03%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%202.25.26.png" alt=""></p>
</br>

<h2 id="비정적-멤버-클래스">비정적 멤버 클래스</h2>
<ul>
<li>Inner class라고 하며 외부 인스턴스에 대한 참조가 유지된다.</li>
<li>외부 인스턴스는 내부 클래스를 new를 통한 인스턴스 할당으로 멤버변수처럼 사용할 수 있다.</li>
<li>외부에 대한 참조가 유지되므로 내부 클래스도 외부 클래스의 자원을 사용할 수 있다.</li>
</ul>
<pre><code class="language-java">class Outer {

    static int x = 10;
    int y = 20;
    public int z = 30;

    class InstanceInner {
        void get() {
            System.out.println(&quot;x: &quot; + x);
            System.out.println(&quot;y: &quot; + y);
            System.out.println(&quot;z: &quot; + z);
        }
    }
}

Outer outer = new Outer();
Outer.InstanceInner insttanceInner = outer.new InstanceInner();
insttanceInner.get();</code></pre>
<p>비정적 내부 클래스를 생성하는 경우에는 반드시 <code>Outer</code> 객체를 생성한 뒤 객체를 이용해서 생성해야 한다. 즉, 비정적 내부 클래스는 <code>Outer 클래스</code>에 대한 참조가 필요하다는 것이다.</p>
<p><img src="https://images.velog.io/images/ljo_0920/post/8d1ff5ff-f681-4158-9090-0a07ef4f36b8/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202022-02-03%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%202.28.03.png" alt=""></p>
</br>

<h2 id="그렇다면-왜-멤버-클래스는-static으로-선언하기를-권장할까">그렇다면 왜 멤버 클래스는 static으로 선언하기를 권장할까?</h2>
<p>바로 Outer 객체에 대한 참조 때문이다.</p>
<p>위의 예제처럼 <code>InstanceInner</code> 와 같은 중첩 클래스를 선언하면 인스펙터가 다음과 같이 경고를 해준다. </p>
<p>경고 주제는 <code>메모리 누수</code> 가능성이 있기 때문이다.</p>
<p><img src="https://images.velog.io/images/ljo_0920/post/5a71a5dc-c916-4a86-9a17-b806980a68db/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202022-02-03%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%202.36.05.png" alt=""></p>
</br>

<h2 id="인용-이펙티브-자바">인용: 이펙티브 자바</h2>
<p> 정적 멤버 클래스와 비정적 멤버 클래스의 구문상 차이는 단지 static이 붙어있고 없고 뿐이지만, 의미상 차이는 의외로 꽤 크다. 비정적 멤버 클래스의 인스턴스는 바깥 클래스의 인스턴스와 암묵적으로 연결되기 때문에 바깥 클래스는 더 이상 사용되지 않지만 <strong>내부 클래스의 참조로 인해 GC가 수거하지 못해서 바깥 클래스의 메모리 해제를 하지 못하는 경우가 발생</strong>할 수 있기 때문이다. 이 문제는 IDE에서 경고해주기 때문에 흔하게 볼 수 있으며, 조슈아 블로흐가 이펙티브 자바에서도 강조하고 있는 내용이기도 하다. </p>
<blockquote>
<p>비정적 멤버 클래스의 인스턴스와 바깥 인스턴스 사이의 관계는 멤버 클래스가 인스턴스화될 때 확립되며, 더 이상 변경할 수 없다. 이 관계는 바깥 클래스의 인스턴스 메서드에서 비정적 멤버 클래스의 생성자를 호출할 때 자동으로 만들어지는 게 보통이지만, 드물게는 직접 <code>바깥 인스턴스의 클래스.new MemberClass(args)</code>를 호출해 수동으로 만들기도 한다. 예상할 수 있듯, 이 관계 정보는 비정적 멤버 클래스의 인스턴스 안에 만들어져 메모리 공간을 차지하며, 생성 시간도 더 걸린다.</p>
</blockquote>
<blockquote>
<p>멤버 클래스에서 바깥 인스턴스에 접근할 일이 없다면 무조건 static을 붙여서 정적 멤버 클래스로 만들자. static을 생략하면 바깥 인스턴스로의 숨은 외부 참조를 갖게 된다. 앞서도 얘기했듯 이 참조를 저장하려면 시간과 공간이 소비된다. 더 심각한 문제는 가비지 컬렉션이 바깥 클래스의 인스턴스를 수거하지 못하는 메모리 누수가 생길 수 있다는 점이다(아이템 7). 참조가 눈에 보이지 않으니 문제의 원인을 찾기 어려워 때때로 심각한 상황을 초래하기도 한다.</p>
</blockquote>
</br>

<h2 id="결론">결론</h2>
<ul>
<li><code>static</code>이 아닌 멤버 클래스의 인스턴스는 바깥 클래스의 인스턴스와 암묵적으로 연결된다.</li>
<li>왜냐하면 <code>static</code>이 아닌 멤버 클래스는 바깥 인스턴스 없이는 생성할 수 없기 때문이다.</li>
<li>두 클래스의 관계는 멤버 클래스의 인스턴스 안에 만들어지며, 메모리를 차지한다. 생성도 느리다.</li>
<li>바깥 클래스 인스턴스의 참조를 멤버 클래스가 갖고 있으므로, 바깥 클래스 인스턴스가 쓰레기 수거 대상에서 빠지게 된다.</li>
<li>이는 메모리 누수를 불러일으킬 수 있는 치명적인 위험요소</li>
</ul>
<p>결국 외부 인스턴스에 대한 참조가 필요하지 않고 내부 클래스가 독립적으로 사용된다면 static nested class로 만드는 것이 낫다.</p>
</br>

<h2 id="참고-출처">참고 출처</h2>
<ul>
<li><a href="https://yhmane.tistory.com/225">https://yhmane.tistory.com/225</a></li>
<li><a href="https://www.geeksforgeeks.org/difference-between-static-and-non-static-nested-class-in-java/">https://www.geeksforgeeks.org/difference-between-static-and-non-static-nested-class-in-java/</a></li>
<li><a href="https://jobjava00.github.io/language/java/basic/nested-class/">https://jobjava00.github.io/language/java/basic/nested-class/</a></li>
<li><a href="https://tecoble.techcourse.co.kr/post/2020-11-05-nested-class/">https://tecoble.techcourse.co.kr/post/2020-11-05-nested-class/</a></li>
<li><a href="https://johngrib.github.io/wiki/java-inner-class-may-be-static/">https://johngrib.github.io/wiki/java-inner-class-may-be-static/</a></li>
<li><a href="https://velog.io/@agugu95/%EC%99%9C-Inner-class%EC%97%90-Static%EC%9D%84-%EB%B6%99%EC%9D%B4%EB%8A%94%EA%B1%B0%EC%A7%80">https://velog.io/@agugu95/왜-Inner-class에-Static을-붙이는거지</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring AOP]]></title>
            <link>https://velog.io/@ljo_0920/Spring-AOP</link>
            <guid>https://velog.io/@ljo_0920/Spring-AOP</guid>
            <pubDate>Thu, 27 Jan 2022 07:30:44 GMT</pubDate>
            <description><![CDATA[<h2 id="aopaspect-oriented-programming">AOP<strong>(Aspect Oriented Programming)</strong></h2>
<p>AOP는 관점 지향 프로그래밍. Spring의 핵심 개념중 하나인 DI가 애플리케이션 모듈들 간의 결합도를 낮춰준다면, AOP는 <strong>애플리케이션 전체에 걸쳐 사용되는 기능을 재사용</strong>하도록 지원하는 것</p>
<p>쉽게 말해 <strong>어떤 로직을 기준으로 핵심적인 관점, 부가적인 관점으로 나누어서 보고 그 관점을 기준으로 각각 모듈화하겠다는 것이다</strong>.</p>
<p>예로들어 핵심적인 관점은 결국 우리가 적용하고자 하는 핵심 비즈니스 로직이 된다. 또한 부가적인 관점은 핵심 로직을 실행하기 위해서 행해지는 데이터베이스 연결, 로깅, 파일 입출력 등을 예로 들 수 있다.</p>
<p><strong>AOP</strong>에서 각 관점을 기준으로 로직을 모듈화한다는 것은 코드들을 부분적으로 나누어서 모듈화하겠다는 의미다. 이때, 소스 코드상에서 다른 부분에 계속 반복해서 쓰는 코드들을 발견할 수 있는 데 이것을 <strong>흩어진 관심사 (Crosscutting Concerns)</strong>라 부른다.</p>
<p>위와 같이 흩어진 관심사를 <strong>Aspect로 모듈화하고 핵심적인 비즈니스 로직에서 분리하여 재사용하겠다는 것이 AOP의 취지</strong>다.</p>
<p><img src="https://user-images.githubusercontent.com/56240505/123369146-27997800-d5b8-11eb-9be7-dfd7a34a4f86.png" alt="https://user-images.githubusercontent.com/56240505/123369146-27997800-d5b8-11eb-9be7-dfd7a34a4f86.png"></p>
<br>

<h2 id="기존-핵심-비즈니스-로직">기존 핵심 비즈니스 로직</h2>
<pre><code class="language-java">@Service
@RequiredArgsConstructor
public class UserService {

    private final PlatformTransactionManager transactionManager;

    public void someSevice() {
            TransactionStatus transaction = transactionManager.getTransaction(new DefaultTransactionDefinition());
        try {
        // 부가 기능 - 로깅, 보안 등등
        // 핵심 기능
        someServiceMethod();
        // 부가 기능
        transactionManager.commit(transaction);
        } catch (RuntimeException runtimeException) {
            transactionManager.rollback(transaction);
            throw runtimeException;
        }
    }
}</code></pre>
<p>서비스 로직의 원자성 보장을 위해 내부적으로 트랜잭션을 적용한 코드입니다. 문제는 UserService의 클래스에는 <code>someServiceMethod()</code> 핵심 비즈니스 로직 이외에도 트랜잭션 경계 설정이라는 <code>부가 기능</code> 관심사들이 존재한다.</p>
<p>현재 예제 코드는 부가 기능 관심사가 트랜잭션 하나 뿐이지만, 또 다른 부가 기능의 관심사가 추가된다면 부가 기능이 필요한 메서드마다 비슷한 코드를 중복해서 작성해야 한다.</p>
<p>가장 큰 문제는 UserService와 비슷하게 수행해야 하는 클래스가 100개가 더 있을 수 있기 때문에 필요한 클래스 마다 UserService와 같이 중복되는 코드를 반복해서 작성해야 함을 의미한다.</p>
<p>만약 코드를 변경한다면 클래스를 변경하는 이유는 비즈니스 로직의 변경 및 부가 기능 코드 또한 변경해야 하기 때문에 서비스 클래스의 응집도가 떨어지고 가독성 또한 나빠지며, 변경할 부분이 명확하게 드러나지 않게 되는등 유지보수 측면에서 아쉬운 점이 많아진다.</p>
<br>

<h2 id="proxy를-사용하여-개선">Proxy를 사용하여 개선</h2>
<p><img src="https://images.velog.io/images/ljo_0920/post/819adfe0-7411-4412-a2bd-a6a13e2e3662/1.png" alt=""></p>
<p>프록시 객체에 트랜잭션 등 부가 기능 관련 로직을 위치시키고, 클라이언트 요청이 발생하면 실제 타깃 객체는 프록시로부터 요청을 위임받아 핵심 비즈니스 로직을 실행합니다. 이를 <strong>데코레이터 패턴</strong>이라고 한다.</p>
<pre><code class="language-java">public interface UserService {

    void someSevice();
}</code></pre>
<pre><code class="language-java">@Service
@Primary
@RequiredArgsConstructor
public class SimpleUserService implements UserService {

    @Override
    public void someSevice() {
                ...
    }
}</code></pre>
<pre><code class="language-java">@Service
@RequiredArgsConstructor
public class UserServiceProxy implements UserService {

    private final UserService target;
    private final PlatformTransactionManager transactionManager;

    @Override
    public void someSevice() {
            TransactionStatus transaction = transactionManager.getTransaction(new DefaultTransactionDefinition());
        try {
        // 부가 기능 - 로깅, 보안 등등
        // 핵심 기능
        someServiceMethod();
        // 부가 기능
        transactionManager.commit(transaction);
        } catch (RuntimeException runtimeException) {
            transactionManager.rollback(transaction);
            throw runtimeException;
        }
    }
}</code></pre>
<p>프록시 객체를 이용하여 핵심 메소드가 호출되기 이전에 부가 기능을 적용하였다.</p>
<br>

<h3 id="기존-프록시의-문제점">기존 프록시의 문제점</h3>
<ul>
<li>인터페이스의 모든 메소드를 구현해 위임하도록 코드를 만들어야 한다.</li>
<li>부가기능인 기능이 모든 메소드에 중복돼서 나타난다.</li>
</ul>
<p>프록시 객체를 이용하여 핵심 비즈니스 로직과 부가 기능 관심사를 분리할 수 있었지만 여전히 한계가 존재한다.. 100개의 클래스가 이와 비슷한 기능을 요구한다면, 100개의 프록시 클래스를 생성하고 인터페이스 메서드를 일일이 구현해야 합니다.</p>
<p>다행히 이러한 별도의 프록시를 번거롭게 생성하는 작업을 생략하는 방법이 존재합니다. Java의 Reflection API를 이용하거나, Spring의 ProxyFactoryBean 등을 사용하는 방법이 존재한다.</p>
<ul>
<li>더 자세한 설명은 토비의 스프링 or <a href="https://gunju-ko.github.io/toby-spring/2018/11/20/AOP.html">해당 글</a> 참고!!</li>
</ul>
<br>

<h2 id="spring-aop">Spring AOP</h2>
<p><strong>Spring AOP</strong>는 Proxy를 기반으로 한 <strong>Runtime Weaving</strong> 방식이다</p>
<ul>
<li>프록시 패턴 기반의 AOP 구현체</li>
<li>스프링 빈에만 AOP를 적용 가능</li>
<li>메소드 조인포인트만 제공</li>
</ul>
<p>스프링이 사용하는 다이나믹 프록시에는 2가지 방법이 있다. </p>
<ul>
<li><code>JDK Dynamic Proxy</code></li>
<li><code>CGLib Proxy</code></li>
</ul>
<p>스프링에서는 기본적으로 jdk dynamic proxy, 스프링 부트에서는 CGLib Proxy 방식으로 AOP를 사용한다. </p>
<p>JDK Dynamin Proxy, CGLib Proxy의 차이를 알아보자.</p>
<br>

<h2 id="jdk-dynamic-proxy">JDK Dynamic Proxy</h2>
<p>JDK Dynamic Proxy는 Proxy Factory에 의해 런타임시 동적으로 만들어지는 오브젝트이다. JDK Dynamic Proxy는 반드시 <strong>인터페이스가 정의되어있고, 인터페이스에 대한 명세를 기준으로 Proxy를 생성</strong>한다. 즉, 인터페이스 선언에 대한 강제성이 있다는 단점이 있다.</p>
<p>내부적으로 JDK Dynamic Proxy에서는 InvationHandler라는 인터페이스를 구현해 만들어지는데, invoke 함수를 오버라이딩하여 Proxy의 위임 기능을 수행한다. 이 과정에서 객체에 대한 <code>Reflection</code> 기능을 사용해 구현하기 때문에 퍼포먼스 하락의 원인이 되기도 한다.</p>
<ul>
<li>이 방식이 Spring AOP의 근간이 되는 방식이다.</li>
<li>인터페이스를 기준으로 Proxy 객체를 생성해준다.</li>
<li>인터페이스가 반드시 필요하다.</li>
<li>리플렉션을 사용하기때문에 성능적으로 좋지 못 하다.</li>
</ul>
<br>

<h2 id="cglib-proxy">CGLib Proxy</h2>
<p>CGLIB Proxy는 순수 Java JDK 라이브러리를 이용하는 것이 아닌 <code>CGLIB</code>라는 외부 라이브러리를 추가해야만 사용할 수 있다. CGLIB의 Enhancer 클래스를 바탕으로 Proxy를 생성하며, 인터페이스가 없어도 Proxy를 생성할 수 있다. CGBLIB Proxy는 타겟 클래스를 <code>상속</code>받아 생성하기 때문에 Proxy를 생성하기 위해 인터페이스를 만들어야하는 수고를 덜 수 있다.</p>
<p>하지만, 상속을 이용하므로 <strong>final</strong>이나 <strong>private</strong>와 같이 상속에 대해 오버라이딩을 지원하지 않는 경우에는 Aspect를 적용할 수 없다는 단점이 있다.</p>
<p>CGLIB Proxy는 바이트 코드를 조작해서 프록시 객체를 생성하므로 JDK Dynamic Proxy보다 퍼포먼스가 빠른 장점이 있다.</p>
<ul>
<li>상속을 이용하기 때문에 클래스나 메소드에 final이 있으면 안된다.</li>
<li>스프링 부트에서 AOP 사용을 위해 채택했다.</li>
<li>CGLIB은 고성능의 코드 생성 라이브러리로 인터페이스를 필요로 하는 JDK Dynamic Proxy 대신 사용될 수 있다. 바이트코드를 조작하는 프레임워크인 ASM을 사용하여 리플렉션보다 빠르다.</li>
</ul>
<br>

<h2 id="스프링-부트에서는-왜-cglib">스프링 부트에서는 왜 cglib?</h2>
<ul>
<li>스프링에선 CGLib은 3가지 한계가 존재했다.<ul>
<li>해당 라이브러리를 추가해야 한다.</li>
<li>CGLib을 구현하기 위해 반드시 파라미터가 없는 defalut 생성자가 필요하다.</li>
<li>생성된 Proxy의 메소드를 호출하게 되면 타깃의 생성자가 2번 호출된다.</li>
</ul>
</li>
<li>하지만 문제되는 부분들을 개선하여 안정화 시켰다.</li>
<li>스프링 부트에서는 AOP를 사용할 때 인터페이스로 선언되어 있어도 CGLib 방식으로 프록시 객체를 생성한다.</li>
</ul>
<p><img src="https://images.velog.io/images/ljo_0920/post/652fff4c-b6db-4143-8383-59f8b57c92bb/2.png" alt=""></p>
<p>참고 출처 : <a href="https://gmoon92.github.io/spring/aop/2019/04/20/jdk-dynamic-proxy-and-cglib.html">https://gmoon92.github.io/spring/aop/2019/04/20/jdk-dynamic-proxy-and-cglib.html</a></p>
<br>

<h2 id="aop-주요-개념"><strong>AOP 주요 개념</strong></h2>
<ul>
<li>Target : Aspect를 적용하는 곳 (클래스, 메서드 .. )</li>
<li>Aspect : 위에서 설명한 흩어진 관심사를 모듈화 한 것. 주로 부가기능을 모듈화함.</li>
<li>Advice : 실질적으로 어떤 일을 해야할 지에 대한 것, 실질적인 부가기능을 담은 구현체</li>
<li>JointPoint : Advice가 적용될 위치, 끼어들 수 있는 지점. 메서드 진입 지점, 생성자 호출 시점, 필드에서 값을 꺼내올 때 등 다양한 시점에 적용가능</li>
<li>PointCut : JointPoint의 상세한 스펙을 정의한 것. &#39;A란 메서드의 진입 시점에 호출할 것&#39;과 같이 더욱 구체적으로 Advice가 실행될 지점을 정할 수 있음</li>
</ul>
<br>

<h2 id="참고-출처">참고 출처</h2>
<ul>
<li><a href="http://wonwoo.ml/index.php/post/1708">http://wonwoo.ml/index.php/post/1708</a></li>
<li><a href="https://tecoble.techcourse.co.kr/post/2021-06-25-aop-transaction/">https://tecoble.techcourse.co.kr/post/2021-06-25-aop-transaction/</a></li>
<li><a href="https://mangkyu.tistory.com/175">https://mangkyu.tistory.com/175</a></li>
<li><a href="https://gunju-ko.github.io/toby-spring/2018/11/20/AOP.html">https://gunju-ko.github.io/toby-spring/2018/11/20/AOP.html</a></li>
<li><a href="https://jojoldu.tistory.com/71">https://jojoldu.tistory.com/71</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring WebClient]]></title>
            <link>https://velog.io/@ljo_0920/Spring-WebClient</link>
            <guid>https://velog.io/@ljo_0920/Spring-WebClient</guid>
            <pubDate>Thu, 27 Jan 2022 04:37:08 GMT</pubDate>
            <description><![CDATA[<h2 id="개요">개요</h2>
<p>스프링 어플리케이션에서 HTTP 요청할 때 사용하는 방법으로 RestTemplate과 WebClient가 있다. 스프링 5.0 이전까지는 클라이언트에서 HTTP 접근을 위해 사용한 것은 RestTemplate 이었다. 스프링 5.0 에서 WebClient가 나왔고 현재는 WebClient를 사용하기를 권고하고 있다. 이번 팀 프로젝트를 진행하면서 외부 api호출 시 WebClient을 사용해보았다.  그럼 RestTemplate과 WebClient는 어떤 특징이 있으며 왜 WebClient를 사용하길 권고하는지 알아보도록 하자.</p>
<br>

<h2 id="resttemplate"><strong>RestTemplate</strong></h2>
<p>스프링 3.0에서부터 지원하며 HTTP 통신에 유용하게 쓸 수 있는 템플릿이다. REST 서비스를 호출하도록 설계되어 HTTP 프로토콜의 메서드 (GET, POST, DELETE, PUT)에 맞게 여러 메서드를 제공한다. RestTemplate은 다음과 같은 특징이 있다</p>
<p><strong>RestTemplate 특징</strong></p>
<ul>
<li>통신을 단순화하고 RESTful 원칙을 지킨다</li>
<li>멀티쓰레드 방식을 사용</li>
<li>Blocking 방식을 사용</li>
</ul>
<p><img src="https://images.velog.io/images/ljo_0920/post/61a5f1dd-f1ac-4f2d-906b-63df631c07de/1.png" alt=""></p>
<p>1) 클라이언트 애플리케이션 구동 시 쓰레드 풀을 만든다.</p>
<p>2) Request는 먼저 queue에 쌓이고 가용 쓰레드가 있으면 해당 쓰레드에 할당된다. (1요청 당 1쓰레드 할당)</p>
<p>3) 각 쓰레드는 블로킹 방식이기 때문에 완료 응답이 올 때까지 다른 요청에 할당될 수 없다.</p>
<p>4) 쓰레드가 다 찼다면 이후 요청은 queue에 대기하게 된다.</p>
<br>

<h3 id="동작원리">동작원리</h3>
<p>RestTemplate은 Multi-Thread와 Blocking방식을 사용한다.</p>
<p>HttpClient는 HTTP를 사용하여 통신하는 범용 라이브러리이고, RestTemplate은 HttpClient 를 추상화(HttpEntity의 json, xml 등)해서 제공해준다. 따라서 내부 통신(HTTP 커넥션)에 있어서는 Apache HttpComponents 를 사용한다. </p>
<p><img src="https://images.velog.io/images/ljo_0920/post/729e44c0-c35e-496f-9fa3-6796f191e5c6/2.png" alt=""></p>
<ol>
<li>어플리케이션이 RestTemplate를 생성하고, URI, HTTP메소드 등의 헤더를 담아 요청한다.</li>
<li>RestTemplate 는 HttpMessageConverter 를 사용하여 requestEntity 를 요청메세지로 변환한다.</li>
<li>RestTemplate 는 ClientHttpRequestFactory 로 부터 ClientHttpRequest 를 가져와서 요청을 보낸다.</li>
<li>ClientHttpRequest 는 요청메세지를 만들어 HTTP 프로토콜을 통해 서버와 통신한다.</li>
<li>RestTemplate 는 ResponseErrorHandler 로 오류를 확인하고 있다면 처리로직을 태운다.</li>
<li>ResponseErrorHandler 는 오류가 있다면 ClientHttpResponse 에서 응답데이터를 가져와서 처리한다.</li>
<li>RestTemplate 는 HttpMessageConverter 를 이용해서 응답메세지를 java object(Class responseType) 로 변환한다.</li>
<li>어플리케이션에 반환된다.</li>
</ol>
<p>출처: <a href="https://sjh836.tistory.com/141">https://sjh836.tistory.com/141</a></p>
<br>

<h3 id="resttemplate-사용"><strong>RestTemplate 사용</strong></h3>
<p>RestTemplate을 생성할 때 어떤 HttpClient를 사용할 것인지 ClientHttpRequestFactory를 전달하여 지정할 수 있다. 기본 생성자의 경우 내부적으로 ClientHttpRequestFactory 의 구현체SimpleClientHttpRequestFactory를 사용하여 초기화한다.</p>
<p>RestTemplate을 사용하기 위해서는 <code>restTemplate.메소드명()</code> 을 사용하면 된다.</p>
<p><img src="https://images.velog.io/images/ljo_0920/post/318e105e-6791-4308-91e7-a4b014c1039c/3.png" alt=""></p>
<p>출처 : <a href="https://velog.io/@soosungp33/%EC%8A%A4%ED%94%84%EB%A7%81-RestTemplate-%EC%A0%95%EB%A6%AC%EC%9A%94%EC%B2%AD-%ED%95%A8">https://velog.io/@soosungp33/스프링-RestTemplate-정리요청-함</a></p>
<br>

<h3 id="connection-pool">Connection Pool</h3>
<p>RestTemplate 객체를 생성할때 별도의 파리미터 없이 new RestTempalte()으로 생성한다면 Connection Pool을 활용하지 않는 객체이다. 이말은 즉, 요청때 마다 새로운 TCP Connection 을 연결한다는 의미이며 이 때 소켓이 close () 된 이후 소켓이 &quot;TIME_WAIT&quot; 상태가 되는데 만약 요청이 많아진다면 TIME_WAIT 상태의 소켓들을 재사용하지 못해서 요청에 대한 응답에 지연이 생길 수 있다. </p>
<p> 이러한 응답 지연 상황을 대비하여 DB가 Connection Pool을 이용하듯이 RestTemplate도 Connection Pool을 이용할 수 있다. 그러기 위해선 RestTemplate 내부 구성을 설정해줘야한다.</p>
<p>단, 호출하는 API 서버가 <strong>Keep-Alive를 지원</strong>해야지 RestTemplate의 Connection Pool을 활용할 수 있다. 타겟 서버가 Keep-Alive를 지원하지 않는다면 미리 Connection Pool을 만들어 놓지 못하고 요청마다 새로운 Connection이 연결되어 매번 핸드쉐이크가 발생된다. 따라서 Connection Pool을 위한 RestTemplate의 내부 설정이 무의미하게 된다.</p>
<pre><code class="language-java">@Configuration
public class RestTemplateConfig {
    @Bean
    HttpClient httpClient() {
        return HttpClientBuilder.create()
            .setMaxConnTotal(100)    //최대 오픈되는 커넥션 수
            .setMaxConnPerRoute(5)   //IP, 포트 1쌍에 대해 수행할 커넥션 수
            .build();
    }

    @Bean
    HttpComponentsClientHttpRequestFactory factory(HttpClient httpClient) {
        HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
        factory.setReadTimeout(5000);        //읽기시간초과, ms
        factory.setConnectTimeout(3000);     //연결시간초과, ms
        factory.setHttpClient(httpClient);

        return factory;
    }

    @Bean
    RestTemplate restTemplate(HttpComponentsClientHttpRequestFactory factory) {
        return new RestTemplate(factory);
    }
}</code></pre>
<br>

<h2 id="webclient"><strong>WebClient</strong></h2>
<p>WebCleint는 스프링 5.0에서 추가된 인터페이스다. Spring WebFlux는 HTTP request를 수행하는 client인 WebClient 를 포함하고 있으며 반응형으로 동작하도록 설계되었다. 스프링 5.0 이후부터는 <code>RestTemplate</code> 의 대안으로 WebClient를 사용할 것을 권장한다. 실제로는 spring-webflux  라이브러리에 속하지만 이 솔루션은 동기 및 비동기 작업을 모두 지원하므로 서블릿 스택에서 실행되는 애플리케이션에도 적용 가능하다.</p>
<p>WebClient는 다음과 같은 특징이 있다.</p>
<ul>
<li>싱글 스레드 방식을 사용</li>
<li>Non-Blocking 방식을 사용</li>
<li>JSON, XML을 쉽게 응답받는다.</li>
</ul>
<p><img src="https://images.velog.io/images/ljo_0920/post/ac5369ed-1aeb-446a-8e9d-d420be468dfd/4.png" alt=""></p>
<p>출처: <a href="https://luminousmen.com/post/asynchronous-programming-blocking-and-non-blocking">https://luminousmen.com/post/asynchronous-programming-blocking-and-non-blocking</a></p>
<p>Core 당 1개의 Thread를 이용한다.</p>
<p>각 요청은 Event Loop내에 Job으로 등록이 되어 Event Loop는 각 Job을 제공자에게 요청한 후, 결과를 기다리지 않고 다른 Job을 처리한다.</p>
<p>Event Loop는 제공자로부터 callback으로 응답이 오면, 그 결과를 요청자에게 제공한다.</p>
<br>

<h3 id="의존성-설정"><strong>의존성 설정</strong></h3>
<p>webflux 의존성을 추가해줘야 한다. Gradle 기준으로 아래와 같이 의존성을 추가해주면 된다.</p>
<pre><code class="language-java">implementation &#39;org.springframework.boot:spring-boot-starter-webflux&#39;</code></pre>
<br>

<h3 id="webclient-생성"><strong>WebClient 생성</strong></h3>
<p>WebClient를 생성하는 데는 2가지의 방법이 있다.</p>
<ul>
<li>static factory method (WebClient.create();)</li>
<li>다른 옵션을 사용하기 위해 Builder를 활용한 클래스 생성 (WebClient.builder())<ul>
<li><code>uriBuilderFactory</code> : base url을 커스텀한 UriBuilderFactory</li>
<li><code>defaultHeader</code> : 모든 요청에 사용할 헤더</li>
<li><code>defaultCookie</code> : 모든 요청에 사용할 쿠키</li>
<li><code>defaultRequest</code> : 모든 요청을 커스텀할 Consumer</li>
<li><code>filter</code> : 모든 요청에 사용할 클라이언트 필터</li>
<li><code>exchangeStrategies</code> : HTTP 메시지 reader &amp; writer 커스텀</li>
<li><code>clientConnector</code> : HTTP 클라이언트 라이브러리 세팅</li>
</ul>
</li>
</ul>
<pre><code class="language-java">DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(baseUrl);
        factory.setEncodingMode(DefaultUriBuilderFactory.EncodingMode.VALUES_ONLY);

Webclient webClient = WebClient
                        .builder()
                      .uriBuilderFactory(factory)
                        .baseUrl(&quot;http://localhost:8080&quot;)
                        .defaultCookie(&quot;쿠키&quot;,&quot;쿠키값&quot;)
                        .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                        .build();</code></pre>
<br>

<h3 id="response-받아오기"><strong>Response 받아오기</strong></h3>
<p>response를 받아오는 방법에는 두 가지가 있다.</p>
<ul>
<li>retrieve() → body를 받아 디코딩하는 간단한 메서드</li>
<li>exchange() → ClientResponse를 상태값 그리고 헤더와 함께 가져온다</li>
</ul>
<p>exchange()를 통해 세세한 컨트롤이 가능하지만, Response 컨텐츠에 대한 모든 처리를 직접 하면서 메모리 누수 가능성 때문에 retrieve()를 권고하고 있다.</p>
<pre><code class="language-java">WebClient client = WebClient.create(&quot;https://example.org&quot;);

Mono&lt;Person&gt; result = client.get()
      .uri(&quot;/persons/{id}&quot;, id).accept(MediaType.APPLICATION_JSON)
      .retrieve()
      .bodyToMono(Person.class);</code></pre>
<p>bodyToMono 는 가져온 body를 Reactor의 Mono 객체로 바꿔준다. Mono 객체는 0-1개의 결과를 처리하는 객체이다. Flux는 0-N개의 결과를 처리하는 객체이다.</p>
<blockquote>
<p>block() 을 사용하면 RestTemplate 처럼 동기식으로 사용할 수 있다.</p>
</blockquote>
<p>4xx, 5xx의 응답 코드를 받으면 <code>WebClientResponseException</code> 또는 HTTP 상태에 해당하는 <code>WebClientResponseException.BadRequest</code> 등 과 같은 하위 exception을 던진다. <code>onStatus</code> 메서드로 상태별 exception을 커스텀도 가능하다.</p>
<pre><code class="language-java">Mono&lt;Person&gt; result = client.get()
      .uri(&quot;/persons/{id}&quot;, id).accept(MediaType.APPLICATION_JSON)
      .retrieve()
      .onStatus(HttpStatus::is4xxClientError, response -&gt; ...)
      .onStatus(HttpStatus::is5xxServerError, response -&gt; ...)
      .bodyToMono(Person.class);</code></pre>
<ul>
<li><code>onStatus</code> 를 사용하는 경우, response에 body가 있다면 <code>onStatus</code> 콜백에서 소비하지 않으면 리소스 반환을 위해 body를 자동으로 비우므로 주의해야 한다.</li>
</ul>
<br>

<h2 id="resttempalte-과-webclient의-차이">RestTempalte 과 WebClient의 차이</h2>
<p>결국 RestTemplate과 WebClient의 가장 큰 차이점은 Non-Blocking과 비동기화 가능 여부일 것이다. 결국 이러한 차이점이 스프링에서 RestTemplate을 사용하는 것 보다 WebClinet의 사용을 권장하는 이유라고 생각한다.</p>
<p><a href="https://www.baeldung.com/spring-webclient-resttemplate">해당 글</a>을 참고해 보면 차이점을 가장 쉽게 이해할 수 있을 것이다</p>
<p><img src="https://user-images.githubusercontent.com/63634505/126900349-905377fe-27ac-4d7a-8b9a-371fb22aee74.png" alt="https://user-images.githubusercontent.com/63634505/126900349-905377fe-27ac-4d7a-8b9a-371fb22aee74.png"></p>
<blockquote>
<p>Non-Blocking?</p>
<p><em>시스템을 호출한 직후에 프로그램으로 제어가 다시 돌아와서 시스템 호출의 종료를 기다리지 않고 다음 동작을 진행한다. 호출한 시스템의 동작을 기다리지 않고 동시에 다른 작업을 진행할 수 있어서 작업의 속도가 빨라진다는 장점이 있다.</em></p>
</blockquote>
<br>

<h2 id="참고---spring-webflux-성능">참고 - Spring WebFlux 성능</h2>
<p><img src="https://user-images.githubusercontent.com/63634505/126900385-ffdecfe7-f5b6-4c7a-a2ed-69145cc85390.png" alt="https://user-images.githubusercontent.com/63634505/126900385-ffdecfe7-f5b6-4c7a-a2ed-69145cc85390.png"></p>
<p>출처 : <a href="https://alwayspr.tistory.com/44">https://alwayspr.tistory.com/44</a></p>
<br>

<h2 id="참고-출처">참고 출처</h2>
<ul>
<li><a href="https://sjh836.tistory.com/141">https://sjh836.tistory.com/141</a></li>
<li><a href="https://www.baeldung.com/spring-webclient-resttemplate">https://www.baeldung.com/spring-webclient-resttemplate</a></li>
<li><a href="https://godekdls.github.io/Reactive%20Spring/webclient/#22-retrieve">https://godekdls.github.io/Reactive Spring/webclient/#22-retrieve</a></li>
<li><a href="https://happycloud-lee.tistory.com/220">https://happycloud-lee.tistory.com/220</a></li>
<li><a href="https://alwayspr.tistory.com/44">https://alwayspr.tistory.com/44</a></li>
<li><a href="https://velog.io/@gkskaks1004/%EC%8A%A4%ED%94%84%EB%A7%81-WebClient%EC%97%90-%EB%8C%80%ED%95%B4%EC%84%9C">https://velog.io/@gkskaks1004/스프링-WebClient에-대해서</a></li>
<li><a href="https://sjh836.tistory.com/141">https://sjh836.tistory.com/141</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring jpa n + 1(~작성중)]]></title>
            <link>https://velog.io/@ljo_0920/Spring-jpa-n-1%EC%9E%91%EC%84%B1%EC%A4%91</link>
            <guid>https://velog.io/@ljo_0920/Spring-jpa-n-1%EC%9E%91%EC%84%B1%EC%A4%91</guid>
            <pubDate>Wed, 19 Jan 2022 07:14:53 GMT</pubDate>
            <description><![CDATA[<h2 id="동기">동기</h2>
<p>spring jpa를 사용하면서 빈번히 맞닥뜨렸던 n+1 문제 정리 및 프로그래머스 데브코스 진행하며 발표 했던 <a href="https://docs.google.com/presentation/d/1OIJR7m9S2BN4nQ4Fh2cTRPzQDOHX4fFC/edit?usp=sharing&amp;ouid=108263827094117026436&amp;rtpof=true&amp;sd=true">내용</a>에 페이지네이션 보완하기 위해 작성하게 되었다.</p>
<h2 id="n--1-이란">N + 1 이란?</h2>
<h2 id="원인">원인</h2>
<h2 id="xxtomany에서의-주의점과-pagination">xxToMany에서의 주의점과 <strong>Pagination</strong></h2>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring @Async]]></title>
            <link>https://velog.io/@ljo_0920/Spring-Async</link>
            <guid>https://velog.io/@ljo_0920/Spring-Async</guid>
            <pubDate>Wed, 19 Jan 2022 06:46:02 GMT</pubDate>
            <description><![CDATA[<h2 id="발단">발단</h2>
<p>사용자가 어떤 게시글을 작성하면 조건에 맞는 다른 사용자에게 쪽지같은 알림을 구현해야 하는 상황</p>
<ul>
<li>게시글 작성</li>
<li>알림</li>
</ul>
<p>처음에는 <code>하나의 transaction</code>으로 처리로 구현을 진행했으나 알림 기능은 부가적인 기능이고 게시글 작성 기능에 영향을 주면 안된다고 생각이 들었다. 따라서 게시글 작성 후 알림 처리가 지연되는 경우 게시글 작성 자체를 지연하는 것이 아니라, 게시글 작성은 완료시키고 다른 Thread에서 알림을 처리하도록 비동기 처리를 진행할 수 있을 것이다.  </p>
<p><strong>스프링에서는 @Async Annotation을 이용하여 간단하게 비동기 처리를 할 수 있다.</strong></p>
<h1 id="java-비동기방식-처리">Java 비동기방식 처리</h1>
<p>그전에 먼저 자바의 비동기 작업 처리를 알아보자. </p>
<p>따라서 method가 실행되면 새로운 thread를 만들고 그 thread에서 메시지를 저장하도록 처리하면 될 것 같다.</p>
<pre><code class="language-java">public class AsyncService {
    public void asyncMethod(String message) throws Exception {
        // do something
    }
}</code></pre>
<p>하지만 해당 방법은 thread를 관리할 수 없기 때문에 위험한 방법이다.</p>
<p>Thread를 관리하기 위해서 ExecutorService 사용해보자. </p>
<p><a href="https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ExecutorService.html">ExecutorService</a>는 쉽게 비동기로 작업을 실행할 수 있도록 도와주는 JDK(1.5부터)에서 제공하는 interface 이다. 일반적으로 ExecutorService는 작업 할당을 위한 스레드 풀과 API를 제공한다.</p>
<pre><code class="language-java">import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class AsyncService {

    private static ExecutorService executorService = Executors.newFixedThreadPool(10);

    public void asyncMethod(final String message) throws Exception {
        executorService.submit(new Runnable() {
            @Override
            public void run() {
                // do something
            }            
        });
    }

}</code></pre>
<p>하지만 비동기방식으로 처리하고 싶은 method마다 반복적으로 동일한 수정 작업들을 진행 해야할 것이다.</p>
<h2 id="async-with-simpleasynctaskexecutor">@Async with SimpleAsyncTaskExecutor</h2>
<p><code>@Async</code> Annotation은 Spring에서 제공하는 Thread Pool을 활용하는 비동기 메소드 지원 Annotation이다.</p>
<p>만약 <code>Spring Boot</code>에서 간단히 사용하고 싶다면, 단순히 <code>Application</code> Class에 <code>@EnableAsync</code> Annotation을 추가하고, 원하는 method 위에 <code>@Async</code> Annotation을 붙여주면 사용할 수 있다.</p>
<pre><code class="language-java">@EnableAsync
@SpringBootApplication
public class SpringBootApplication {
    ...
}</code></pre>
<pre><code class="language-java">public class AsyncService {

    @Async
    public void asyncMethod(String message) throws Exception {
        ....
    }
}</code></pre>
<p>하지만 @Async의 기본설정은 SimpleAsyncTaskExecutor를 사용하도록 되어있기 때문입니다.</p>
<p>본인의 개발 환경에 맞게 Customize하기에는 직접 <code>AsyncConfigurerSupport</code>를 상속받는 Class를 작성하는 것이 좋다.</p>
<h2 id="async-with-threadpooltaskexecutor">@Async with ThreadPoolTaskExecutor</h2>
<p>Thread pool을 이용해서 thread를 관리가능한 방식다. 아래와 같은 <code>AsyncConfigurerSupport</code>를 상속받는 Customize Class를 구현하자.</p>
<p>그리고  Application 클래스에 @EnableAutoConfiguration(혹은 @SpringBootApplication) 설정이 되어있다면 런타임시 @Configuration가 설정된 SpringAsyncConfig 클래스의 threadPoolTaskExecutor bean 정보를 읽어들이기 때문에 앞서 설정한 Application 클래스의 @EnableAsync을 제거한다.</p>
<pre><code class="language-java">import java.util.concurrent.Executor;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurerSupport;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

@EnableAsync
@Configuration
public class AsyncConfig extends AsyncConfigurerSupport {

    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecuto();
        executor.setCorePoolSize(10);
        executor.setMaxPoolSize(20);
        executor.setQueueCapacity(100);
        executor.setThreadNamePrefix(&quot;my thread-&quot;);
        executor.initialize();
        return executor;
    }
}</code></pre>
<p>여기서 <strong>설정한 요소들은 아래</strong>와 같다.</p>
<ul>
<li><strong>@Configuration</strong> : <strong>Spring 설정 관련 Class로 @Component 등록되어 Scanning</strong> 될 수 있다.</li>
<li><strong>@EnableAsync</strong> : <strong>Spring method에서 비동기 기능을 사용가능하게 활성화</strong> 한다.</li>
<li><strong>CorePoolSize :</strong> 기본 실행 대기하는 Thread의 수**</li>
<li><strong>MaxPoolSize : 동시 동작하는 최대 Thread의 수</strong></li>
<li><strong>QueueCapacity</strong> : <strong>MaxPoolSize 초과 요청</strong>에서 <strong>Thread 생성 요청시</strong>,<strong>해당 요청을 Queue에 저장</strong>하는데 이때 <strong>최대 수용 가능한 Queue의 수</strong>,Queue에 저장되어있다가 <strong>Thread에 자리가 생기면 하나씩 빠져나가 동작</strong></li>
<li><strong>ThreadNamePrefix</strong> : <strong>생성되는 Thread 접두사</strong> 지정</li>
</ul>
<p>위와 같이 작성한 후 <strong>비동기 방식 사용을 원하는 method에 <code>@Async</code> Annotation을 지정</strong>해주면 된다.</p>
<p> @Async annotation에 bean의 이름을 제공하면 SimpleAsyncTaskExecutor가 아닌 설정한 TaskExecutor로 thread를 관리하게 된다.</p>
<pre><code class="language-java">@EnableAsync
@Configuration
public class AsyncConfig extends AsyncConfigurerSupport {

    @Bean(name = &quot;threadPoolTaskExecutor&quot;)
    public Executor threadPoolTaskExecutor()
    {
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setCorePoolSize(3);
        taskExecutor.setMaxPoolSize(30);
        taskExecutor.setQueueCapacity(10);
        taskExecutor.setThreadNamePrefix(&quot;Executor-&quot;);
        taskExecutor.initialize();
        return taskExecutor;
    }
}

---

public class AsyncService {

        @Async(&quot;threadPoolTaskExecutor&quot;)
    public void asyncMethod(String message) throws Exception {
        // do something
    }
}
</code></pre>
<p>Thread Pool의 종류를 여러개 설정하고자한다면 SpringAsyncConfig 안에 bean을 여러개 만들고 @Async를 설정시 원하는 bean의 이름을 설정하면 된다.</p>
<h2 id="제약사항">제약사항</h2>
<p><code>@Async</code> Annotation을 사용할 때 아래와 같은 사항을 주의 해야한다.</p>
<ol>
<li>private method는 사용 불가, public method만 사용 가능</li>
<li>self-invocation(자가 호출) 불가, 즉 inner method는 사용 불가</li>
</ol>
<p>제약의 원인은 간단한데 <strong><code>@Async</code></strong>  은 <strong>AOP에 의해 동작하고 있기 때문에 해당 메서드는 프록시될 수 있어야 하기 때문이다.</strong></p>
<p><img src="https://media.vlpt.us/images/gillog/post/5bb64a29-5263-4fcc-9f02-cffea4162137/image.png" alt="https://media.vlpt.us/images/gillog/post/5bb64a29-5263-4fcc-9f02-cffea4162137/image.png"></p>
<blockquote>
<p>출처 : <a href="https://dzone.com/articles/effective-advice-on-spring-async-part-1">https://dzone.com/articles/effective-advice-on-spring-async-part-1</a></p>
</blockquote>
<p>해당 <code>@Async</code> method를 가로챈 후, 다른 Class에서 호출이 가능해야 하므로,<code>private</code> method는 사용할 수 없는 것이다. 또한 inner method의 호출은 해당 메서드를 직접호출 하기 때문에  <code>self-invocation</code>이 불가능하다. @Transactional 사용시 주의점과 비슷하다고 할 수 있다.</p>
<p><img src="https://images.velog.io/images/ljo_0920/post/57e2fcf1-060d-42e4-80e0-7b56a32269cc/1.png" alt=""></p>
<pre><code class="language-java">@Slf4j
@Service
public class TestService {

    @Async
    public void innerAsyncMethod(int i) {
        log.info(&quot;async i = &quot; + i);
    }

    public void asyncMethod(int i) {
        innerAsyncMethod(i);
    }

}</code></pre>
<p><img src="https://images.velog.io/images/ljo_0920/post/3ad9d7f4-f8a2-4bb6-8096-254b4d84eaac/2.png" alt=""></p>
<h2 id="리턴-타입">리턴 타입</h2>
<p>@Async 메서드는 <code>AsyncExecutionAspectSupport</code> 클래스의 doSubmit 메서드에서 선택한 실행자와 함께 지정된 작업을 실제로 실행하도록 위임한다.</p>
<p> 리턴타입은  크게 두가지로 나뉠 수 있다.</p>
<ul>
<li>리턴값이 없는 경우</li>
<li>있는 경우(Futrue)</li>
</ul>
<p>Future에 경우에도 여러 타입이 존재하는데 해당 리턴 값에 대한 것은 <a href="https://brunch.co.kr/@springboot/401">여기</a> 에서 자세히 보면 좋을 것 같다</p>
<pre><code class="language-java">@Nullable
protected Object doSubmit(Callable&lt;Object&gt; task, AsyncTaskExecutor executor, Class&lt;?&gt; returnType) {
    if (CompletableFuture.class.isAssignableFrom(returnType)) {
        return CompletableFuture.supplyAsync(() -&gt; {
            try {
                return task.call();
            }
            catch (Throwable ex) {
                throw new CompletionException(ex);
            }
        }, executor);
    }
    else if (ListenableFuture.class.isAssignableFrom(returnType)) {
        return ((AsyncListenableTaskExecutor) executor).submitListenable(task);
    }
    else if (Future.class.isAssignableFrom(returnType)) {
        return executor.submit(task);
    }
    else {
        executor.submit(task);
        return null;
    }
}</code></pre>
<h2 id="예외-처리">예외 처리</h2>
<p>메서드 반환 형식이 Futre인 경우 예외 처리가 쉽다. Future.get() 메서드에서 예외가 발생하기 때문이다.</p>
<p>하지만 반환값이 없는 void이면 예외가 호출 스레드에 전파되지 않는다. 즉 해당 thread가 소리없이 죽기 때문에 예외 처리가 관리되지 않는다. </p>
<p>이러한 예외 처리를 위해서는 <code>AsyncUncaughtExceptionHandler</code> 인터페이스를 구현하여 사용자 지정 비동기 예외 처리기를 생성한다. <code>handleUncaughtException()</code> 메서드는 캐치되지 않은 비동기 예외가 있을 때 호출된다.</p>
<pre><code class="language-java">
public class CustomAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {

    @Override
    public void handleUncaughtException(Throwable ex, Method method, Object... params) {
        log.warn(&quot;Exception message - {}&quot;, ex.getMessage());
        log.warn(&quot;Method name - {}&quot;, method.getName());
    }
}</code></pre>
<p><img src="https://images.velog.io/images/ljo_0920/post/174aa49b-97af-48a0-b358-bd6acb3789e6/3.png" alt=""></p>
<h2 id="추가">추가</h2>
<p>추후 해당 처리를 Spring event를 이용하거나 AOP를 이용하여 리팩토링도 진행할 예정이다.</p>
<h2 id="참고-출처">참고 출처</h2>
<ul>
<li><a href="https://brunch.co.kr/@springboot/401">https://brunch.co.kr/@springboot/401</a></li>
<li><a href="https://velog.io/@gillog/Spring-Async-Annotation%EB%B9%84%EB%8F%99%EA%B8%B0-%EB%A9%94%EC%86%8C%EB%93%9C-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0">https://velog.io/@gillog/Spring-Async-Annotation비동기-메소드-사용하기</a></li>
<li><a href="https://www.baeldung.com/spring-async">https://www.baeldung.com/spring-async</a></li>
<li><a href="http://dveamer.github.io/java/SpringAsync.html">http://dveamer.github.io/java/SpringAsync.html</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring Boot Slice Test]]></title>
            <link>https://velog.io/@ljo_0920/Spring-Boot-slice-test</link>
            <guid>https://velog.io/@ljo_0920/Spring-Boot-slice-test</guid>
            <pubDate>Wed, 12 Jan 2022 06:43:31 GMT</pubDate>
            <description><![CDATA[<p>Spring Data JPA 사용하는 미션에서 멘토님께 리뷰를 받으며 다음과 같은 피드백을 받으며 슬라이스 테스트 존재에 대해 알게 되었다.</p>
<ul>
<li>Repository Test시 @SpringBootTest를 @DataJpaTest로 변경해서 테스트 작성하기</li>
</ul>
<p>슬라이스 테스트란 무엇이고 왜 사용하는 것일까??</p>
<br>

<h2 id="spring-boot-슬라이스-테스트"><strong>Spring Boot 슬라이스 테스트</strong></h2>
<hr>
<h3 id="슬라이스-테스트란"><strong>슬라이스 테스트란?</strong></h3>
<aside>
💡 Test slicing is about segmenting the `ApplicationContext` that is created for your test. Typically, if you want to test a controller using `MockMvc`, surely you don’t want to bother with the data layer. Instead you’d probably want to *mock* the service that your controller uses and validate that all the web-related interaction works as expected.

</aside>

<ul>
<li>즉 스프링은 레이어 별로 잘라서 특정 레이어에 대해서 Bean을 최소한으로 등록시켜 테스트 하고자 하는 부분에 최대한 단위 테스트를 지원해주고 있다.</li>
<li>그렇다면 <code>@SpringBootTest</code> 대신 슬라이스 테스트를 하는 이유는 무엇일까??</li>
</ul>
<br>

<h2 id="first-테스트-원칙"><strong>F.I.R.S.T 테스트 원칙</strong></h2>
<p>단위 테스트는 응용 프로그램에서 테스트 가능한 가장 작은 소프트웨어를 실행하여 예상대로 동작하는지 확인하는 테스트이며 로버트 마틴의 클린코드에서 깨끗한 테스트를 위한 다섯 가지 F.I.R.S.T 규칙을 말한다.</p>
<ul>
<li><strong>Fast</strong> — 테스트는 빨라야 한다.</li>
<li><strong>Isolated</strong> — 각 테스트는 서로 의존하면 안된다.</li>
<li><strong>Repeatable</strong> — 테스트는 어떤 환경에서도 반복 가능해야 한다.</li>
<li><strong>Self-validating</strong> — 테스트는 bool 값으로 결과를 내야 한다.</li>
<li><strong>Timely</strong> — 테스트는 적시에 작성해야 한다.</li>
</ul>
<p><code>@SpringBootTest</code> 어노테이션을 사용하는 경우 단점은 아래와 같다.</p>
<ul>
<li>실제 구동되는 애플리케이션의 설정, 모든 <code>Bean</code>을 로드하기 때문에 시간이 오래걸리고 무겁다.</li>
<li>테스트 단위가 크기 때문에 디버깅이 어려운 편이다.</li>
<li>외부 API 콜같은 Rollback 처리가 안되는 테스트 진행을 하기 어려움</li>
</ul>
<p>따라서 repository 레이어의 단위테스트의 경우 <code>@SpringBootTest</code> 대신 <code>@DataJpaTest</code> 사용하여 테스트를 작성하는 경우 통해 속도적인 측면과 의존성 측면에서 이점을 가질 수 있다.</p>
<br>

<h2 id="슬라이스-테스트-어노테이션-종류"><strong>슬라이스 테스트 어노테이션 종류</strong></h2>
<p>아래는 대표적인 슬라이스 테스트 어노테이션이 존재하는데 해당 글에서는 중 <code>@WebMvcTest</code>, <code>@DataJpaTest</code> 살펴보도록 할 것이다.</p>
<ul>
<li><code>@WebMvcTest</code></li>
<li><code>@WebFluxTest</code></li>
<li><code>@DataJpaTest</code></li>
<li><code>@JsonTest</code></li>
<li><code>@RestClientTest</code></li>
</ul>
<br>

<h2 id="webmvctest">@WebMvcTest</h2>
<ul>
<li>MVC를 위한 테스트.</li>
<li>웹에서 테스트하기 힘든 컨트롤러를 테스트하는 데 적합.</li>
<li>웹상에서 요청과 응답에 대해 테스트할 수 있음.</li>
<li>시큐리티, 필터까지 자동으로 테스트하며, 수동으로 추가/삭제 가능.</li>
<li>@SpringBootTest 어노테이션보다 가볍게 테스트할 수 있음.</li>
<li>다음과 같은 내용만 스캔하도록 제한함.@Controller, @ControllerAdvice, @JsonComponent, Converter, GenericConverter, Filter, HandlerInterceptor,<ul>
<li>따라서 의존성이 끊기기 때문에, 예를 들면 서비스와 같은 객체들은 @MockBean을 사용해서 만들어 사용해야 한다.</li>
</ul>
</li>
</ul>
<pre><code class="language-java">@WebMvcTest(ShelterPostController.class)
public class ShelterPostControllerTest {

    @Autowired
    protected MockMvc mockMvc;

    @Autowired
    protected ObjectMapper objectMapper;

    @MockBean
    protected ShelterPostService shelterPostService;

    @Test
    @DisplayName(&quot;게시글 리스트 조회 테스트&quot;)
    void getShelterPostsTest() throws Exception {
        // given, when, then
                ...
    }
}</code></pre>
<br>

<h3 id="mockbean"><strong>@MockBean</strong></h3>
<p>spring-boot-test 패키지는 Mockito를 포함하고 있기 때문에 기존에 사용하던 방식대로 Mock 객체를 생성해서 테스트하는 방법도 있지만, spring-boot-test에서는 새로운 방법도 제공한다.</p>
<ul>
<li>바로 @MockBean 어노테이션을 사용해서 이름 그대로 Mock 객체를 빈으로써 등록할 수 있다.</li>
<li>기존에 사용되던 스프링 Bean이 아닌 Mock Bean을 주입한다.</li>
<li>그렇기 때문에 만일 @MockBean으로 선언된 빈을 주입받는다면 Spring의 ApplicationContext는 Mock 객체를 주입한다.</li>
<li>새롭게 @MockBean을 선언하면 Mock 객체를 빈으로써 등록하지만, 만일 @MockBean으로 선언한 객체와 같은 이름과 타입으로 이미 빈으로 등록되어있다면 해당 빈은 선언한 Mock 빈으로 대체된다.</li>
</ul>
<p>해당 어노테이션은 테스트 내용 중 외부 서비스를 호출하는 부분을 Mock해서 쉽게 처리할 수 있다.</p>
<pre><code class="language-java">@SpringBootTest
public class XXXControllerTest {

    @MockBean  // 외부 서비스 호출에 사용되는 RestTemplate Bean을 Mock
    private RestTemplate mockRT;

    @MockBean  // 외부 서비스 호출에 사용되는 Service Bean을 Mock
    private XXXService xXXService;

}</code></pre>
<br>

<h2 id="datajpatest">@DataJpaTest</h2>
<p>Spring Data JPA를 테스트하고자 한다면 <code>@DataJpaTest</code> 기능을 사용해볼 수 있다.</p>
<ul>
<li>해당 테스트는 기본적으로 in-memory embedded database를 생성하고 <code>@Entity</code> 클래스를 스캔한다.</li>
<li>일반적인 다른 컴포넌트들은 스캔하지 않는다. 따라서 특정 bean의 의존성이 필요한 경우 아래의 방법 사용<ul>
<li>@import</li>
<li>@DataJpaTest(includeFilters = @Filter(..))</li>
</ul>
</li>
</ul>
<p><code>@DataJpaTest</code>는 <code>@Transactional</code> 어노테이션을 포함하고 있다. </p>
<ul>
<li>따라서 테스트가 완료되면 자동으로 롤백된다.
<img src="https://images.velog.io/images/ljo_0920/post/563be9c9-8607-4a07-bf46-0581e59ab3d2/Untitled.png" alt=""></li>
</ul>
<p>만약 <code>@Transactional</code> 기능이 필요하지 않다면 아래와 같이 줄 수 있다.</p>
<pre><code class="language-java">@DataJpaTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
class SomejpaTest {
    ...
}
</code></pre>
<p><code>@DataJpaTest</code> 기능을 사용하면 <code>@Entity</code>를 스캔하고 repository를 설정하는 것 이외에도 테스트를 위한 <code>TestEntityManager</code>라는 빈이 생성된다.</p>
<ul>
<li>이 빈을 사용해서 테스트에 이용한 데이터를 정의할 수 있다.</li>
</ul>
<pre><code class="language-java">@DataJpaTest
class SomejpaTest {

    @Autowired
    private TestEntityManager entityManager;

    @Test
    @DisplayName(&quot;게시글 아이디로 댓글 목록 삭제 테스트&quot;)
    void deleteAllByMissingPostIdTest() {
        // given
        LongStream.rangeClosed(1, 3).forEach(idx -&gt;
            entityManager.persist(Comment.builder()
                .missingPost(missingPost)
                .content(&quot;내용&quot;)
                .account(account)
                .build()
            )
        );

        // when
        commentRepository.deleteAllByMissingPostId(missingPost.getId());
        List&lt;Comment&gt; comments = commentRepository.findAll();

        // then
        SoftAssertions.assertSoftly(softAssertions -&gt; {
                softAssertions.assertThat(comments).hasSize(3);
                comments.forEach(foundComment -&gt; softAssertions.assertThat(foundComment.isDeleted()).isTrue());
            }
        );
    }

}</code></pre>
<p>만약 테스트에 내장된 임베디드 데이터베이스를 사용하지 않고 real database를 사용하고자 하는 경우, <code>@AutoConfigureTestDatabase</code> 어노테이션을 사용하면 손쉽게 설정할 수 있습니다.</p>
<pre><code class="language-java">@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class SomejpaTest {
    ...
}
</code></pre>
<br>

<h2 id="사용시-주의할-점">사용시 주의할 점</h2>
<p>슬라이스 테스트 시, 하위 레이어는 <code>Mock</code> 기반으로 만들기 때문에 주의할 점들이 있다.</p>
<ul>
<li>의존성 객체를 Mocking하기 때문에 완벽한 테스트는 아님</li>
<li>Mocking 처리하기 위한 시간이 소요</li>
<li>Mocking 라이브러리에 대한 학습 비용 발생</li>
<li>Mock 기반 으로 테스트하기 때문에 실제 환경에서는 결과가 다를 수 있음</li>
</ul>
<br>

<h2 id="참고--출처">참고  출처</h2>
<ul>
<li><a href="https://github.com/cheese10yun/spring-guide/blob/master/docs/test-guide.md#%EB%8B%A8%EC%A0%90">https://github.com/cheese10yun/spring-guide/blob/master/docs/test-guide.md#단점</a></li>
<li><a href="https://tecoble.techcourse.co.kr/post/2021-05-18-slice-test/">https://tecoble.techcourse.co.kr/post/2021-05-18-slice-test/</a></li>
<li><a href="https://meetup.toast.com/posts/124">https://meetup.toast.com/posts/124</a></li>
<li><a href="https://github.com/HomoEfficio/dev-tips/blob/master/Spring-Boot-Test-%EC%99%B8%EB%B6%80-%EC%84%9C%EB%B9%84%EC%8A%A4-%ED%98%B8%EC%B6%9C-with-%40MockBean.md">https://github.com/HomoEfficio/dev-tips/blob/master/Spring-Boot-Test-외부-서비스-호출-with-%40MockBean.md</a></li>
<li><a href="https://jojoldu.tistory.com/226">https://jojoldu.tistory.com/226</a></li>
<li><a href="https://lalwr.blogspot.com/2019/09/spring-test.html">https://lalwr.blogspot.com/2019/09/spring-test.html</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[java 버전별 차이 & 특징]]></title>
            <link>https://velog.io/@ljo_0920/java-%EB%B2%84%EC%A0%84%EB%B3%84-%EC%B0%A8%EC%9D%B4-%ED%8A%B9%EC%A7%95</link>
            <guid>https://velog.io/@ljo_0920/java-%EB%B2%84%EC%A0%84%EB%B3%84-%EC%B0%A8%EC%9D%B4-%ED%8A%B9%EC%A7%95</guid>
            <pubDate>Wed, 12 Jan 2022 04:43:25 GMT</pubDate>
            <description><![CDATA[<p>Java 버전별 특징들을 찾아보면서 <a href="https://www.marcobehler.com/guides/a-guide-to-java-versions-and-features">좋은 글</a>이 있어 해당 글을 정리한 포스팅입니다.
따라서 잘못된 내용이 존재할 수 있습니다!</p>
<h2 id="어떤-자바-버전을-사용해야-할까">어떤 자바 버전을 사용해야 할까?</h2>
<p>최신 Java 버전은 이제 6개월마다 따른다. </p>
<p>수많은 새로운 버전이 출시됨에 따라 기본적으로 다음과 같은 사용 시나리오가 존재</p>
<ul>
<li>기업의 기존 프로젝트에서는 Java 8을 사용해야 하는 경우가 많음</li>
<li>일부 레거시 프로젝트는 Java 1.5(2004년 출시) 또는 1.6(2006년 출시)에서 중단되기도 함</li>
<li>최신 IDE, 프레임워크 및 빌드 도구를 사용하고 그린 필드 프로젝트를 시작하는 경우 Java 11(LTS) 또는 최신 Java 17 LTS를 망설임 없이 사용할 수 있다.</li>
<li>안드로이드 개발의 특별한 분야가 있는데, 자바 버전은 기본적으로 자바 7에 고정되어 있고, 특정한 자바 8 기능들을 이용할 수 있다. 또는 코틀린 프로그래밍 언어를 사용하는 것으로 전환</li>
</ul>
<br>

<h2 id="특정-자바-버전을-학습해야-할까">특정 자바 버전을 학습해야 할까?</h2>
<p>12, 17와 같은 특정 Java 버전만을 &quot;학습&quot;할 필요가 없다.</p>
<p>Python 2에서 3과 같이 릴리스 사이에 심각한 문제가 있는 것과 달리 자바는 <code>하위 호환성</code>이 매우 높기 때문</p>
<ul>
<li>즉, Java 5 또는 8 프로그램이 Java 8-17 가상 머신에서 실행되도록 보장된다.</li>
<li>이걸 backward compatible (하위 호환성) 이라 한다.</li>
</ul>
<p>반대로 java 8 JVM에서는 사용할 수 없는 java 17 기능을 의존한다면 컴파일 되지 않는다.</p>
<ul>
<li>대부분 java.lang.UnsupportedClassVersionError  발생</li>
</ul>
<p>그러므로 오히려 Java 8의 내용들로 토대를 쌓고 Java 9-17에 추가된 기능에 대해 알아보고 언제든지 사용할 수 있다.</p>
<br>

<h2 id="자바-distribution">자바 Distribution</h2>
<p>JDK 다운로드를 제공하는 다양한 사이트가 있으며 &quot;누가 어떤 라이선스로 무엇을 제공하는지&quot;가 불분명하다. </p>
<h3 id="openjdk-프로젝트">OpenJDK 프로젝트</h3>
<p>Java 소스 코드(RE/JDK의 소스 코드)의 경우 OpenJDK 프로젝트 사이트에 존재하는 유일한 소스 코드</p>
<p>그러나 이것은 소스 코드일 뿐 배포 가능한 빌드가 아니다.</p>
<ul>
<li>이론적으로, 해당 소스 코드로 빌드를 만들 수 있다</li>
<li>예를 들어 MarcoJDK라고 부르며 배포하기 시작 가능</li>
<li>하지만 합법적으로 자바 SE 호환이라고 부를 수 있는 우리의 배포판은 인증이 부족할 것</li>
</ul>
<p>이러한 이유로 실제로 이러한 빌드를 만들고 인증을 받은 후 배포하는 벤더가 많지 않다.</p>
<h3 id="openjdk-빌드오라클-및-oraclejdk-빌드">OpenJDK 빌드(오라클) 및 OracleJDK 빌드</h3>
<p>자바를 소스에서 빌드하는 벤더 중 하나가 오라클이다. </p>
<p><strong>Oracle JDK와 OpenJDK의 차이점</strong></p>
<ul>
<li>Oracle JDK는 상용(유료)이지만, OpenJDK는 오픈소스기반(무료)</li>
<li>Oracle JDK의 라이선스는 Oracle BCL(Binary Code License) Agreement이지만, OpenJDK의 라이선스는 Oracle GPL v2</li>
<li>Oracle JDK는 LTS(장기 지원) 업데이트 지원을 받을 수 있지만, OpenJDK는 LTS 없이 6개월마다 새로운 버전이 배포된다.</li>
<li>Oracle JDK는 Oracle이 인수한 Sun Microsystems 플러그인을 제공하지만, OpenJDK는 제공하지 않는다.</li>
<li>Oracle JDK는 OpenJDK 보다 CPU 사용량과 메모리 사용량이 적고, 응답시간이 높다.</li>
</ul>
<p>Java 8 이전 OpenJDK 빌드와 OracleJDK 빌드 사이에는 실제 소스 차이가 존재했는데, 최신의 두 버전은 본질적으로 동일하며 약간의 차이만 존재한다.</p>
<h3 id="adoptium-formerly-adoptopenjdk">Adoptium (formerly AdoptOpenJDK)</h3>
<p>2017년에 자바 유저 그룹 멤버, 개발자, 벤더(아마존, 마이크로소프트, 피보탈, 레드햇 등)로 구성된 그룹이 AdoptOpenJDK라는 커뮤니티를 시작</p>
<ul>
<li>참고: 2021년 8월 현재, AdoptOpenJDK 프로젝트는 새로운 집으로 옮겨졌고 지금은 Eclipse Adoptium 프로젝트로 불린다.</li>
</ul>
<p>또한 보다 긴 가용성/업데이트를 갖춘 강력한 무료 OpenJDK 빌드를 제공하며 다음과 같은 두 가지 Java 가상 머신도 선택 가능</p>
<ul>
<li><a href="https://en.wikipedia.org/wiki/HotSpot">HotSpot</a> and <a href="https://en.wikipedia.org/wiki/OpenJ9">OpenJ9</a>.</li>
</ul>
<p>Java를 설치하려는 경우 권장된다.</p>
<h3 id="azul-zulu-amazon-corretto-sapmachine"><strong>Azul Zulu, Amazon Corretto, SAPMachine</strong></h3>
<ul>
<li>그 외에 주목할만한 distribution</li>
</ul>
<br>

<h2 id="java8-17-특징">java8-17 특징</h2>
<hr>
<h3 id="java-8">java 8</h3>
<p>Java 8은 대규모 릴리스였으며 <a href="https://www.oracle.com/technetwork/java/javase/8-whats-new-2157071.html">Oracle 웹 사이트</a> 에서 모든 기능 목록을 확인 가능</p>
<ul>
<li>Lambda,</li>
<li>stream</li>
<li>interface default method</li>
<li>Optional</li>
<li>new Date and Time API(LocalDateTime, …)</li>
</ul>
<br>

<p><strong>Lambda</strong></p>
<p>Java 8 이전 익명 클래스의 사용을 람다를 이용하여 더욱 간결하고 직관적으로 구현 가능</p>
<pre><code class="language-java">Runnable runnable = new Runnable(){
   @Override
   public void run(){
     System.out.println(*&quot;Hello world !&quot;*);
   }
 };</code></pre>
<pre><code class="language-java">Runnable runnable = () -&gt; System.out.println(*&quot;Hello world two!&quot;*);</code></pre>
<p><strong>Stream</strong></p>
<p>자바 8은 스트림 API를 통해 컬렉션을 처리하면서 발생하는 <code>모호함과 반복적인 코드 문제</code>와 <code>멀티코어 활용 어려움</code>이라는 두 가지 문제를 모두 해결</p>
<pre><code class="language-java">List&lt;String&gt; list = Arrays.asList(*&quot;franz&quot;*, *&quot;ferdinand&quot;*, *&quot;fiel&quot;*, *&quot;vom&quot;*, *&quot;pferd&quot;*);
list.stream()
    .filter(name -&gt; name.startsWith(*&quot;f&quot;*))
    .map(String::toUpperCase)
    .sorted()
    .forEach(System.out::println);</code></pre>
<br>

<h3 id="java-9">Java 9</h3>
<p>Java 9는 다음과 같은 몇 가지 추가 사항이 포함된 상당히 큰 릴리스</p>
<ul>
<li>모듈시스템 등장(jigsaw)</li>
</ul>
<br>

<p><strong>컬렉션</strong></p>
<p>컬렉션에는 list, set, map을 쉽게 구성할 수 있는 몇 가지 추가 기능</p>
<pre><code class="language-java">List&lt;String&gt; list = List.of(*&quot;one&quot;*, *&quot;two&quot;*, *&quot;three&quot;*);
Set&lt;String&gt; set = Set.of(*&quot;one&quot;*, *&quot;two&quot;*, *&quot;three&quot;*);
Map&lt;String, String&gt; map = Map.of(*&quot;foo&quot;*, *&quot;one&quot;*, *&quot;bar&quot;*, *&quot;two&quot;*);</code></pre>
<p><strong>스트림</strong></p>
<p> takeWhile, dropWhile, iterate 메서드의 형태로 몇 가지 추가 기능</p>
<pre><code class="language-java">Stream&lt;String&gt; stream = Stream.iterate(*&quot;&quot;*, s -&gt; s + *&quot;s&quot;*)
  .takeWhile(s -&gt; s.length() &lt; 10);</code></pre>
<p><strong>optional</strong></p>
<p>ifPresentOrElse 추가 기능</p>
<pre><code class="language-java">user.ifPresentOrElse(this::displayAccount, this::displayLogin);</code></pre>
<p><strong>인터페이스</strong></p>
<p>인터페이스에 private method 사용 가능</p>
<pre><code class="language-java">public interface MyInterface {

    private static void myPrivateMethod(){
        System.out.println(*&quot;Yay, I am private!&quot;*);
    }
}</code></pre>
<p><strong>기타 언어 기능</strong></p>
<p>try-with-resources 문 또는 다이아몬드 연산자(&lt;&gt;) 확장, HTTP클라이언트와 같은 몇 가지 다른 개선 사항 존재</p>
<br>

<h3 id="java-10">Java 10</h3>
<p>가비지 컬렉션 등과 같은 Java 10에 몇 가지 변경 사항이 존재</p>
<p>개발자로서 보게 될 유일한 실제 변경 사항은 로컬 변수 유형 추론이라고도 하는 &quot;var&quot; 키워드의 도입</p>
<ul>
<li>var 키워드</li>
<li>병렬 처리 가비지 컬렉션 도입으로 인한 성능 향상</li>
<li>JVM 힙 영역을 시스템 메모리가 아닌 다른 종류의 메모리에도 할당 가능</li>
</ul>
<br>

<p><strong>지역 변수 유형 추론: var-keyword</strong></p>
<pre><code class="language-java">// Pre-Java 10
String myName = &quot;Marco&quot;;

// With Java 10
var myName = &quot;Marco&quot;</code></pre>
<p>JAVA에서 var 예약어를 사용하면 중복을 줄임으로써 코드를 간결하게 만들 수 있다. </p>
<ul>
<li>var 키워드는 <strong>지역 변수 타입 추론</strong>을 허용한다.</li>
<li><code>메서드 내부의 변수</code>에만 적용 가능</li>
</ul>
<br>

<h3 id="java-11">Java <strong>11</strong></h3>
<p>Java 11은 개발자의 관점에서 볼 때 약간 작은 릴리스</p>
<ul>
<li>Oracle JDK와 OpenJDK 통합</li>
<li>Oracle JDK가 구독형 유료 모델로 전환</li>
<li>서드파티 JDK 로의 이전 필요</li>
<li>lambda 지역변수 사용법 변경</li>
<li>기타 추가</li>
</ul>
<br>

<p><strong>Strings &amp; Files</strong></p>
<p>Strings and Files에는 몇 가지 새로운 메서드 추가</p>
<pre><code class="language-java">*&quot;Marco&quot;*.isBlank();
*&quot;Mar\nco&quot;*.lines();
*&quot;Marco  &quot;*.strip();

Path path = Files.writeString(Files.createTempFile(*&quot;helloworld&quot;*, *&quot;.txt&quot;*), *&quot;Hi, my name is!&quot;*);
String s = Files.readString(path);</code></pre>
<p><strong>Run Source Files</strong></p>
<p>Java 10부터 Java 소스 파일 을 먼저 컴파일 하지 않고도 실행할 수 있다. 스크립팅을 향한 한 걸음</p>
<pre><code class="language-bash">ubuntu@DESKTOP-168M0IF:~$ java MyScript.java</code></pre>
<p><strong>람다 매개변수에 대한 지역 변수 유형 추론(var)</strong></p>
<p>람다 표현식에 var 사용 가능</p>
<pre><code class="language-java">(var firstName, var lastName) -&gt; firstName + lastName</code></pre>
<br>

<h3 id="java-12">Java <strong>12</strong></h3>
<p>Java 12에는 몇 가지 <a href="https://www.oracle.com/technetwork/java/javase/12-relnote-issues-5211422.html">새로운 기능과 정리가 포함</a> 되어 있지만</p>
<ul>
<li>언급할 가치가 있는 것은 유니코드 11 지원과 새로운 스위치 표현식의 preview 뿐</li>
</ul>
<br>

<h3 id="java-13">Java <strong>13</strong></h3>
<p><a href="https://www.oracle.com/technetwork/java/13-relnote-issues-5460548.html">여기</a> 에서 전체 기능 목록을 찾을 수 있지만 기본적으로 유니코드 12.1 지원과 두 가지 새롭거나 개선된 preview 기능(향후 변경될 수 있음)이 제공</p>
<br>

<p><strong>스위치 표현식(preview)</strong></p>
<p>이제 스위치 표현식이 값을 반환 가능하며 fall-through/break 문제 없이 표현식에 람다 스타일 구문을 사용 가능</p>
<pre><code class="language-java">switch(status) {
  case SUBSCRIBER:
    *// code block*break;
  case FREE_TRIAL:
    *// code block*break;
  default:
    *// code block*}</code></pre>
<pre><code class="language-java">boolean result = switch (status) {
    case SUBSCRIBER -&gt; true;
    case FREE_TRIAL -&gt; false;
    default -&gt; throw new IllegalArgumentException(*&quot;something is murky!&quot;*);
};</code></pre>
<p><strong>Multiline Strings (Preview)</strong></p>
<pre><code class="language-java">String htmlBeforeJava13 = *&quot;&lt;html&gt;\n&quot;* +
              *&quot;    &lt;body&gt;\n&quot;* +
              *&quot;        &lt;p&gt;Hello, world&lt;/p&gt;\n&quot;* +
              *&quot;    &lt;/body&gt;\n&quot;* +
              *&quot;&lt;/html&gt;\n&quot;*;</code></pre>
<pre><code class="language-java">String htmlWithJava13 = *&quot;&quot;&quot;
              &lt;html&gt;
                  &lt;body&gt;
                      &lt;p&gt;Hello, world&lt;/p&gt;
                  &lt;/body&gt;
              &lt;/html&gt;
              &quot;&quot;&quot;*;</code></pre>
<br>

<h3 id="자바-14"><strong>자바 14</strong></h3>
<ul>
<li>스위치 표현시 표준화</li>
<li>instanceof 패턴 매칭 (preview)</li>
<li>record (data object) 선언 기능 추가 (preview)</li>
</ul>
<br>

<p><strong>스위치 표현(Standard)</strong></p>
<p>버전 12 및 13에서 preview 였던 스위치 표현식 이 이제 표준화 되었다.</p>
<pre><code class="language-java">int numLetters = switch (day) {
    case MONDAY, FRIDAY, SUNDAY -&gt; 6;
    case TUESDAY                -&gt; 7;
    default      -&gt; {
      String s = day.toString();
      int result = s.length();
      yield result;
    }
};</code></pre>
<p><strong>record(preview)</strong></p>
<p>Java로 많은 상용구를 작성하는 고통을 완화하는 데 도움이 되는 레코드 클래스가 있다.</p>
<p>데이터, (잠재적으로) getter/setters, equals/hashcode, toString만 포함하는 이 Java 14 이전 클래스</p>
<pre><code class="language-java">final class Point {
    public final int x;
    public final int y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
}
    *// state-based implementations of equals, hashCode, toString// nothing else*</code></pre>
<p>레코드를 사용</p>
<pre><code class="language-java">record Point(int x, int y) { }</code></pre>
<p><strong>유용한 NullPointerExceptions</strong></p>
<p>마지막으로 NullPointerExceptions는 정확히 어떤 변수가 null 인지 설명한다 .</p>
<pre><code class="language-java">author.age = 35;
---
Exception in thread *&quot;main&quot;* java.lang.NullPointerException:
     Cannot assign field *&quot;age&quot;* because *&quot;author&quot;* is null</code></pre>
<p><strong>Pattern Matching For InstanceOf (Preview)</strong></p>
<p>이전에는 다음과 같이 instanceof 내부에서 객체를 캐스팅 필요</p>
<pre><code class="language-java">if (obj instanceof String) {
    String s = (String) obj;
    *// use s*}</code></pre>
<p>이제 이 작업을 수행하여 캐스트를 효과적으로 삭제 가능</p>
<pre><code class="language-java">if (obj instanceof String s) {
    System.out.println(s.contains(*&quot;hello&quot;*));
}</code></pre>
<br>

<h3 id="java-15">Java <strong>15</strong></h3>
<ul>
<li>Text-Blocks / Multiline Strings</li>
<li>Records &amp; Pattern Matching(2차 preview, 상단 Java 14 참조)</li>
<li>스케일링 가능한 낮은 지연의 가비지 컬렉터 추가(ZGC)</li>
<li>레코드 (2차 preview, 상단 Java 14 참조)</li>
<li>Sealed Classes - Preview</li>
<li>Nashorn JavaScript Engine 제거</li>
</ul>
<br>

<p><strong>Text-Blocks / Multiline Strings</strong></p>
<p>Java 13의 실험 기능으로 도입된 여러 줄 문자열은 이제 프로덕션 준비 완료</p>
<pre><code class="language-java">String text = *&quot;&quot;&quot;
                Lorem ipsum dolor sit amet, consectetur adipiscing \
                elit, sed do eiusmod tempor incididunt ut labore \
                et dolore magna aliqua.\
                &quot;&quot;&quot;*;</code></pre>
<p><strong>Sealed Classes - Preview</strong></p>
<ul>
<li>상속 가능한 클래스를 지정할 수 있는 봉인 클래스가 제공된다.</li>
<li>상속 가능한 대상은 상위 클래스 또는 인터페이스 패키지 내에 속해 있어야 한다.</li>
</ul>
<pre><code class="language-java">public abstract sealed class Shape
    permits Circle, Rectangle, Square {...}</code></pre>
<p>즉, 클래스가 public인 동안 하위 클래스로 허용되는 유일한 Shape 클래스들은 Circle, Rectangle 및 Square 이다.</p>
<br>

<h3 id="java-16">Java <strong>16</strong></h3>
<ul>
<li>Pattern Matching for instanceof</li>
<li>Unix-Domain Socket Channels</li>
<li>Foreign Linker API - Preview</li>
<li>Records &amp; Pattern Matching</li>
</ul>
<br>

<p><strong>Unix-Domain Socket Channels</strong></p>
<p>이제 Unix 도메인 소켓에 연결할 수 있다(macOS 및 Windows(10+)에서도 지원됨).</p>
<pre><code class="language-java"> socket.connect(UnixDomainSocketAddress.of(
        *&quot;/var/run/postgresql/.s.PGSQL.5432&quot;*));</code></pre>
<p><strong>Foreign Linker API - Preview</strong></p>
<p>JNI(Java Native Interface)에 대한 계획된 교체로, 기본 라이브러리에 바인딩할 수 있다(C 생각).</p>
<br>

<h3 id="java-17">Java <strong>17</strong></h3>
<p>Java 17은 Java 11 이후의 새로운 Java LTS(장기 지원) 릴리스</p>
<ul>
<li>Pattern Matching for switch (Preview)</li>
<li>Sealed Classes (Finalized)</li>
<li>Foreign Function &amp; Memory API (Incubator)</li>
<li>Deprecating the Security Manager</li>
</ul>
<br>

<p><strong>Pattern Matching for switch (Preview)</strong></p>
<p>이제 객체를 전달하여 기능을 전환하고 특정 유형을 확인할 수 있다.</p>
<pre><code class="language-java">public String test(Object obj) {

    return switch(obj) {

    case Integer i -&gt; *&quot;An integer&quot;*;

    case String s -&gt; *&quot;A string&quot;*;

    case Cat c -&gt; *&quot;A Cat&quot;*;

    default -&gt; *&quot;I don&#39;t know what it is&quot;*;

    };

}</code></pre>
<p><strong>Sealed Classes (Finalized)</strong></p>
<p>Java 15에서 preview 제공되었던 기능 완료</p>
<p><strong>Foreign Function &amp; Memory API (Incubator)</strong></p>
<p>Java Native Interface(JNI)를 대체한다. 기본 함수를 호출하고 JVM 외부의 메모리에 액세스할 수 있다. 지금은 C가 C++, 포트란과 같은 추가 언어 지원 계획을 가지고 있다고 생각</p>
<p><strong>Deprecating the Security Manager</strong></p>
<p>자바 1.0 이후로 보안 관리자가 존재해 왔었지만 현재는 더 이상 사용되지 않으며 향후 버전에서는 제거될 예정</p>
<br>

<h2 id="참고-출처">참고 출처</h2>
<ul>
<li><a href="https://www.marcobehler.com/guides/a-guide-to-java-versions-and-features">https://www.marcobehler.com/guides/a-guide-to-java-versions-and-features</a></li>
<li><a href="https://velog.io/@arkeio/JDK-%EB%B2%84%EC%A0%84%EB%B3%84-%EC%B0%A8%EC%9D%B4">https://velog.io/@arkeio/JDK-%EB%B2%84%EC%A0%84%EB%B3%84-%EC%B0%A8%EC%9D%B4</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[이펙티브 자바] private 생성자나 열거 타입으로 싱글턴임을 보증하라]]></title>
            <link>https://velog.io/@ljo_0920/%EC%9D%B4%ED%8E%99%ED%8B%B0%EB%B8%8C-%EC%9E%90%EB%B0%94-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/@ljo_0920/%EC%9D%B4%ED%8E%99%ED%8B%B0%EB%B8%8C-%EC%9E%90%EB%B0%94-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>Wed, 13 Oct 2021 13:35:39 GMT</pubDate>
            <description><![CDATA[<h2 id="싱글턴">싱글턴</h2>
<ul>
<li>인스턴스를 오직 하나만 생성할 수 있는 클래스를 말한다.<ul>
<li>ex) 함수와 같은 무상태 객체, 설계상 유일해야 하는 시스템 컴포넌트</li>
</ul>
</li>
<li>클래스를 싱글턴으로 만들면 이를 사용하는 클라이언트를 테스트하기가 어려워질 수 있다.<ul>
<li>타입을 인터페이스로 정의한 다음 해당 인터페이스를 구현해서 만든 싱글턴이 아니라면 싱글턴 인스턴스를 가짜(mock) 구현으로 대체할 수 없기 때문이다.</li>
</ul>
</li>
<li>싱글턴 만드는 방식<ul>
<li>두 방식 모두 생성자는 private로 감춰두고, 유일한 인스턴스에 접근할 수 있는 수단으로 public static 멤버를 하나 마련해둔다.</li>
</ul>
</li>
</ul>
<h2 id="1-public-static-멤버가-final-필드인-방식">1. public static 멤버가 final 필드인 방식</h2>
<ul>
<li>private 생성자는 public static final 필드인 Elvis.INSTANCE를 초기화할 때 한번만 호출된다.</li>
<li>public, protected 생성자가 없으므로 Elvis 클래스가 초기화될 때 만들어진 인스턴스가 전체 시스템에서 하나뿐임이 보장된다.<ul>
<li>예외: 권한이 있는 클라이언트는 리플렉션 api인 AccessibleObject.setAccessible을 사용해 private 생성자를 호출할 수 있다.</li>
<li>이러한 공격을 방어하려면 생성자를 수정하여 두 번째 객체가 생성되려 할 때 예외를 던지도록 한다.</li>
</ul>
</li>
<li>장점<ul>
<li>해당 클래스가 싱글턴임이 API에 명백히 드러난다</li>
<li>public static 필드가 final 이니 절대로 다른 객체를 참조할 수 없다</li>
<li>간결하다</li>
</ul>
</li>
</ul>
<pre><code class="language-java">public class Elvis {
    public static final Elvis INSTANCE = new Elvis();
    private Elvis() { ... }

    public void leaveTheBuilding() { ... }
}</code></pre>
<h2 id="2-정적-팩터리-메서드를-public-static-멤버로-제공">2. 정적 팩터리 메서드를 public static 멤버로 제공</h2>
<ul>
<li>Elvis.getInstance는 항상 같은 객체의 참조를 반환하므로 제2의 Elvis 인스턴스는 만들어지지 않다<ul>
<li>리플렉션을 통한 예외는 똑같이 적용</li>
</ul>
</li>
<li>장점<ul>
<li>API를 바꾸지 않고도 싱글턴이 아니게 변경할 수 있다.</li>
<li>정적 팩터리를 제네릭 싱글턴 팩터리(아이템 30)를 만들 수 있다.</li>
<li>정적 팩터리의 메서드 참조를 공급자(supplier)로 사용할 수 있다.<ul>
<li>Elvis::get Instance → Supplier<Elvis></li>
</ul>
</li>
<li>이런한 장점들이 굳이 필요하지 않는다면 public 필드 방식이 좋다.</li>
</ul>
</li>
</ul>
<pre><code class="language-java">public class Elvis {
         private static final Elvis INSTANCE = new Elvis();
    private Elvis() { }
    public static Elvis getInstance() { return INSTANCE; }

    public void leaveTheBuilding() {...}
}</code></pre>
<ul>
<li>두 가지 방식으로 만든 싱글턴 클래스를 직렬화하는 경우 Serializable을 구현하는 것만으로 부족<ul>
<li>모든 인스턴스 필드를 일시적(transient)이라고 선언하고 readResolve 메서드를 제공해야 한다.</li>
<li>그렇지 않으면 직렬화된 인스턴스를 역직렬화할 때 마다 <code>새로운 인스턴스</code>가 생성된다</li>
</ul>
</li>
</ul>
<pre><code class="language-java">public Object readResolve() {
    // 진짜 Elvis를 반환하고, 가짜 Elvis는 가비지 컬렉터에 맡긴다.
    return INSTANCE;
  }
}</code></pre>
<h3 id="singleton은-왜-안티패턴이라-불리는가">Singleton은 왜 안티패턴이라 불리는가?</h3>
<ul>
<li>SOLID 원칙의 대부분은 인터페이스 설계와 관련이 되어있다. 의존성을 concrete class(구현 클래스)가 아닌 Interface에 두면, 실제 concrete class의 구현이 변경되어도 이를 사용한 코드는 큰 영향을 받지 않는다. 그렇기 때문에 SOLID원칙(OCP, LSP, ISP, DIP등)을 지키기 위해서는 인터페이스로 설계를 해야한다.</li>
<li>하지만 싱글톤을 이용하는 경우 대부분 인터페이스가 아닌 콘크리트 클래스의 객체를 미리 생성해놓고 정적 메소드를 이용하여 사용하게 된다. 이는 여러 SOLID원칙을 위반할 수 있는 가능성을 열어둠과 동시에, 싱글톤을 사용하는 곳과 싱글톤 클래스 사이에 의존성이 생기게 된다. 클래스 사이에 강한 의존성, 즉 높은 결합이 생기게 되면 수정, 단위테스트의 어려움 등 다양한 문제가 발생한다.</li>
</ul>
<p><strong>1. private 생성자를 갖고 있어 상속이 불가능하다.</strong></p>
<ul>
<li>싱글톤은 자신만이 객체를 생성할 수 있도록 생성자를 private으로 제한한다. 하지만 상속을 통해 다형성을 적용하기 위해서는 다른 기본생성자가 필요하므로 객체지향의 장점을 적용할 수 없다. 또한 싱글톤을 구현하기 위해서는 객체지향적이지 못한 static 필드와 static 메소드를 사용해야 한다.</li>
</ul>
<p><strong>2. 테스트하기 힘들다.</strong></p>
<ul>
<li>싱글톤은 테스트하기가 힘드며 테스트 방법에 따라 불가능할 수 있다. 싱글톤은 생성 방식이 제한적이기 때문에 Mock 객체로 대체하기가 어려우며, 동적으로 객체를 주입하기도 힘들다.</li>
<li>테스트는 개발의 핵심인데, 테스트 코드를 작성할 수 없다는 것은 큰 단점이 된다.</li>
</ul>
<p><strong>3. 서버 환경에서는 싱글톤이 1개만 생성됨을 보장하지 못한다.</strong></p>
<ul>
<li>서버에서 클래스 로더를 어떻게 구성하느냐에 따라 싱글톤 클래스임에도 불구하고 1개 이상의 객체가 만들어질 수 있다. 따라서 Java 언어를 이용한 싱글톤 기법은 서버 환경에서 싱글톤이 꼭 보장된다고 볼 수 없다. 또한 여러 개의 JVM에 분산돼서 설치되는 경우에도 독립적으로 객체가 생성된다.</li>
<li>생성자를 private하게 두었어도 reflection을 통해 하나 이상의 오브젝트가 만들어질 수 있다. 또한 여러개의 JVM에 분산돼서 설치가 되는 경우에도 각각 독립적으로 오브젝트가 생기기 때문에 싱글톤으로서의 가치가 떨어진다.</li>
</ul>
<p><strong>4. 전역 상태를 만들 수 있기 때문에 바람직하지 못하다.</strong></p>
<ul>
<li>싱글톤의 스태틱 메소드를 이용하면 언제든지 해당 객체를 사용할 수 있고, 전역 상태(Global State)로 사용되기 쉽다. 아무 객체나 자유롭게 접근하고 수정하며 공유되는 전역 상태는 객체지향 프로그래밍에서 권장되지 않는다.</li>
<li>싱글톤 패턴은 객체를 1번 생성하고 재사용할 수 있다는 장점이 있다. 하지만 다른 단점들이 너무 크기 때문에 활용이 쉽지 않았는데, Spring에서는 컨테이너를 통해 직접 객체(빈)들을 싱글톤으로 관리함으로써 객체를 재사용함과 동시에 객체지향스로운 개발을 할 수 있도록 해주었다.</li>
</ul>
<h2 id="3-원소가-하나인-열거-타입을-선언">3. 원소가 하나인 열거 타입을 선언</h2>
<ul>
<li>public 필드 방식과 비슷하지만  더 간결하고, 직렬화 가능하고 좋다</li>
<li>단, 만들려는 싱글턴이 Enum 외의 클래스를 상속해야 한다면 사용 불가</li>
</ul>
<pre><code class="language-java">public enum Elvis {
  INSTANCE;

  public void leaveTheBuilding() {...}
}</code></pre>
<h2 id="참고-출처">참고 출처</h2>
<ul>
<li><a href="https://mangkyu.tistory.com/153">https://ssoco.tistory.com/65</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[이펙티브 자바] 선택적 매개변수가 많다면 빌더를 고려하라]]></title>
            <link>https://velog.io/@ljo_0920/%EC%9D%B4%ED%8E%99%ED%8B%B0%EB%B8%8C-%EC%9E%90%EB%B0%94-%EC%84%A0%ED%83%9D%EC%A0%81-%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/@ljo_0920/%EC%9D%B4%ED%8E%99%ED%8B%B0%EB%B8%8C-%EC%9E%90%EB%B0%94-%EC%84%A0%ED%83%9D%EC%A0%81-%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>Tue, 12 Oct 2021 15:34:20 GMT</pubDate>
            <description><![CDATA[<h2 id="정적-팩터리와-생성자의-제약">정적 팩터리와 생성자의 제약</h2>
<ul>
<li>선택적 매개변수가 많을 때 적절히 대응하기 어려움</li>
<li>ex) 영양정보를 표현하는 클래스<ul>
<li>필수 항목: 1회 내용량, n회 제공량, 1회 제공량당 칼로리</li>
<li>선택 항목: 총 지방, 트랜스지방, 콜레스테롤, 나트륨 등 20가지 이상</li>
<li>대다수 제품은 선택 항목 중 대다수의 값이 0</li>
</ul>
</li>
<li>이런 클래스용 생성자 혹은 정적 팩터리는 주로  <code>점층적 생성자 패턴</code>을 사용해왔음</li>
</ul>
<h3 id="점층적-생성자-패턴">점층적 생성자 패턴</h3>
<ul>
<li>telescoping constructor pattern</li>
<li>필수매개변수 생성자, 필수 + 선택 1 생성자... 형태로 선택 매개변수를 전부 다 받는 생성자까지 늘려가는 방식</li>
<li>해당 클래스의 인스턴스를 만들려면 원하는 매개변수를 모두 포함한 생성자 중 가장 짧은 것을 골라 호출</li>
<li>단점<ul>
<li>매개변수가 많아지면 클라이언트 코드를 작성하거나 읽기 어렵다</li>
</ul>
</li>
</ul>
<pre><code class="language-java">public class NutritionFacts{
    private final int servingSize;  // 필수
    private final int servings;     // 필수
    private final int calories;     // 선택
    private final int fat;          // 선택
    private final int sodium;       // 선택
    private final int carbohydrate; // 선택

    public NutritionFacts(int servingSize, int servings){
        this(servingSize, servings, 0);
    }

    public NutritionFacts(int servingSize, int servings, int calories){
        this(servingSize, servings, calories, 0);
    }

    public NutritionFacts(int servingSize, int servings, int calories,
                          int fat){
        this(servingSize, servings, calories, fat, 0);
    }

    public NutritionFacts(int servingSize, int servings, int calories,
                          int fat, int sodium){
        this(servingSize, servings, calories, fat, sodium, 0);
    }

    public NutritionFacts(int servingSize, int servings, int calories,
                          int fat, int sodium, int carbohydrate){
        this.servingSize = servingSize;
        this.servings = servings;
        this.calories = calories;
        this.fat = fat;
        this.sodium = sodium;
        this.carbohydrate = carbohydrate;
    }
}</code></pre>
<h3 id="자바-빈즈-패턴javabeans-pattern">자바 빈즈 패턴(javaBeans pattern)</h3>
<ul>
<li>두번째 대안인 자바빈즈 패턴, 매개변수가 없는 생성자로 객체를 만든 후, 세터 메서드들을 호출해 원하는 매개변수의 값을 설정하는 방식</li>
<li>심각한 단점<ul>
<li>객체 하나를 만들려면 메서드를 여러 개 호출해야 하고, 객체가 완전히 생성되기 전까지는 일관성이 무너진 상태에 놓이게 된다</li>
<li>이런 일관성이 무너지는 문제 때문에 클래스를 <code>불변으로 만들 수 없으며</code> 스레드 안정성을 얻으려면 추가 작업이 필요하다.</li>
<li>생성이 끝난 객체를 수동으로 freezing하고 얼리기 전에는 사용할 수 없도록 하기도 한다.<ul>
<li>하지만 freeze 메서드를 확실히 호출해줬는지를 컴파일러가 보증 x, 런타임 오류 취약</li>
</ul>
</li>
</ul>
</li>
</ul>
<pre><code class="language-java">class NutritionFacts{

    private int servingSize  = -1;  // 필수
    private int servings     = -1;  // 필수
    private int calories     = 0;   // 선택
    private int fat          = 0;   // 선택
    private int sodium       = 0;   // 선택
    private int carbohydrate = 0;   // 선택

    public NutritionFacts() { }

    public void setServingSize(int val) { servingSize = val; }

    public void setServings(int servings) { servings = val; }

    public void setCalories(int calories) { calories = val; }

    public void setFat(int fat) { fat = val; }

    public void setSodium(int sodium) { sodium = val; }

    public void setCarbohydrate(int carbohydrate) { carbohydrate = val; }</code></pre>
<pre><code class="language-java">NutritionFacts cocaCola = new NutritionFacts();
cocaCola.setServingSize(240);
cocaCola.setServings(8);
cocaCola.setCalories(100);
cocaCola.setSodium(35);
cocaCola.setCarbohydrate(27);</code></pre>
<h3 id="빌더-패턴builder-pattern">빌더 패턴(Builder pattern)</h3>
<ul>
<li><p>앞의 두가지의 장점을 섞은 세번째 대안</p>
<ul>
<li>점층적 생성자 패턴의 안정성과 자바 빈즈 패턴의 가독성을 겸비한 패턴</li>
</ul>
</li>
<li><p>클라이언트는 필요한 객체를 직접 만드는 대신, 필수 매개변수만으로 생성자(정적 팩토리)를 호출해 <code>빌더 객체</code>를 얻는다</p>
<ul>
<li>빌더 객체가 제공하는 일종의 세터 메서드들로 원하는 선택 매개변수들을 설정한다</li>
<li>매개변수가 없는 build 메서드를 호출해 필요한(보통은 불변인) 객체를 얻는다.</li>
</ul>
<p>cf) 빌더는 생성할 클래스안에 <code>정적 멤버 클래스</code>로 만들어두는게 보통</p>
</li>
<li><p><a href="https://devlog-wjdrbs96.tistory.com/206">https://devlog-wjdrbs96.tistory.com/206</a></p>
</li>
</ul>
<pre><code class="language-java">class NutritionFacts{
    private final int servingSize;
    private final int servings;
    private final int calories;
    private final int fat;
    private final int sodium;
    private final int carbohydrate;

    public static class Builder{
        // 필수 매개변수
        private final int servingSize;
        private final int servings;

        // 선택 매개변수 - 기본값으로 초기화한다.
        private int calories     = 0;
        private int fat          = 0;
        private int sodium       = 0;
        private int carbohydrate = 0;

        public Builder(int servingSize, int servings) {
            this.servingSize = servingSize;
            this.servings = servings;
        }

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

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

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

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

        public NutritionFacts build(){
            return new NutritionFacts(this);
        }
    }

    private NutritionFacts(Builder builder){
        servingSize  = builder.servingSize;
        servings     = builder.servings;
        calories     = builder.calories;
        fat          = builder.fat;
        sodium       = builder.sodium;
        carbohydrate = builder.carbohydrate;
    }
}</code></pre>
<ul>
<li>계층적으로 설계된 클래스와 함게 쓰기에 좋다<ul>
<li>각 계층의 클래스에 관련 빌더를 멤버로 정의, 추상 클래스는  추상 빌더를, 구체 클래스는 구체 빌더</li>
<li>Pizzan.Builder 클래스는 재귀적 타입 한정을 이용하는 제네릭 타입</li>
<li>추상 메서드인 self를 더해 하위 클래스에서는 형변환 하지 않고도 메서드 체이닝 지원<ul>
<li>self 타입이 없는 자바를 위한 우회 방법을 시뮬레이트한 셀프 타입 관용구라 한다.</li>
</ul>
</li>
</ul>
</li>
</ul>
<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();

                // 하위 클래스는 이 메서드를 오버라이딩하여 this를 반환하도록 해야 한다
        protected abstract T self();
    }

    Pizza(Builder&lt;?&gt; builder){
        toppings = builder.toppings.clone();    // 아이템 50 참조
    }
}</code></pre>
<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;        
        }

        public NyPizza(Builder builder) {
            super(builder);
            size = builder.size;
        }
    }
}</code></pre>
<h2 id="단점">단점</h2>
<ul>
<li>빌더 생성 비용이 문제 될 수도 있다</li>
<li>코드가 장황해서 매개변수가 4개 이상은 되어야 값어치를 한다</li>
</ul>
<h2 id="결론">결론</h2>
<ul>
<li>생성자나 정적 팩터리가 처리해야할 매개변수가 많다면 빌더 패턴을 선택하는게 더 낫다.</li>
<li>매개변수 중 다수가 필수가 아니거나 같은 타입이면 특히 더 그렇다.</li>
<li>빌더는 클라이언트 코드의 가독성이 좋고, 자바빈즈보다 훨씬 안전하다.</li>
<li>이런 스타일의 빌더 패턴은 Lombok @Builder로 가능</li>
<li><a href="https://johngrib.github.io/wiki/design-pattern/builder-pattern/">https://johngrib.github.io/wiki/design-pattern/builder-pattern/</a></li>
</ul>
]]></description>
        </item>
    </channel>
</rss>