<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>benji_android.log</title>
        <link>https://velog.io/</link>
        <description>Android 주니어 개발자</description>
        <lastBuildDate>Mon, 01 May 2023 08:36:48 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>benji_android.log</title>
            <url>https://velog.velcdn.com/images/aios_/profile/fa166866-1b73-4272-9fdb-bbbf85cb0b38/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. benji_android.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/aios_" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[디자인 패턴] 컴포지트 패턴]]></title>
            <link>https://velog.io/@aios_/%EB%94%94%EC%9E%90%EC%9D%B8%ED%8C%A8%ED%84%B4-%EC%BB%B4%ED%8F%AC%EC%A7%80%ED%8A%B8-%ED%8C%A8%ED%84%B4</link>
            <guid>https://velog.io/@aios_/%EB%94%94%EC%9E%90%EC%9D%B8%ED%8C%A8%ED%84%B4-%EC%BB%B4%ED%8F%AC%EC%A7%80%ED%8A%B8-%ED%8C%A8%ED%84%B4</guid>
            <pubDate>Mon, 01 May 2023 08:36:48 GMT</pubDate>
            <description><![CDATA[<h3 id="정의">정의</h3>
<blockquote>
<p>객체들의 관계를 트리 구조로 구성하여 부분-전체(Part-Whole) 계층을 표한하는 패턴
단일 객체와 복합 객체 모두 동일하게 다루도록 한다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/aios_/post/64bbe6ee-e611-4fd6-81d4-60dc2deb1809/image.png" alt=""></p>
<hr>
<h3 id="구현할-클래스-목록">구현할 클래스 목록</h3>
<p><img src="https://velog.velcdn.com/images/aios_/post/9f79fb0a-2503-41e3-a5bc-250de6154919/image.png" alt=""></p>
<p><code>Component</code> 를 구현하여 필요한 함수를 사용하고 하나의 Leaf 로 구성할 수 있도록 Interface를 만들어 객체를 생성한다.</p>
<hr>
<h3 id="component-만들기">Component 만들기</h3>
<pre><code class="language-kotlin">interface Component {
    fun getWeight(): Int
    fun getItemCount(): Int
    fun getItemName(): String
}</code></pre>
<p>아이템의 전체 구성요소가 될 Interface 를 만든다.</p>
<p><code>Component</code> 인터페이스의 경우 Item에 반드시 필요한 객체의 상태를 구현하면 된다.</p>
<hr>
<h3 id="leaf-만들기">Leaf 만들기</h3>
<pre><code class="language-kotlin">class M4A1: Component {
    override fun getWeight(): Int {
        return 3800
    }

    override fun getItemCount(): Int {
        return 1
    }

    override fun getItemName(): String {
        return &quot;M4A1&quot;
    }
}

class Bullet_5(private val count: Int) : Component {
    override fun getWeight(): Int {
        return 5 * getItemCount()
    }

    override fun getItemCount(): Int {
        return count
    }

    override fun getItemName(): String {
        return &quot;5.56mm&quot;
    }
}

... (생략)</code></pre>
<p>간단하게 총과 총알을 사용하는 Item Class 를 만들었다.</p>
<p><code>Component</code> 인터페이스를 상속받아 구현하는 구현체를 작성한다.</p>
<p>각 아이템의 무게, 개수, 이름을 입력한 Leaf 를 만들 수 있다.</p>
<hr>
<h3 id="composite-만들기">Composite 만들기</h3>
<pre><code class="language-kotlin">class BattlegroundBag(
    private val name: String
) : Component {
    private val _components: ArrayList&lt;Component&gt; = arrayListOf()

    fun addItem(component: Component) {
        _components.add(component)
    }

    fun hasItemList(): String {
        val iter = _components.iterator()
        val sb = StringBuilder()
        while (iter.hasNext()) {
            sb.append(iter.next().getItemName())
                .append(&quot;, &quot;)
        }
        sb.delete(sb.lastIndex - 1, sb.lastIndex)
        return sb.toString()
    }

    override fun getWeight(): Int {
        return _components.sumOf { it.getWeight() }
    }

    override fun getItemCount(): Int {
        return _components.sumOf { it.getItemCount() }
    }

    override fun getItemName(): String {
        return name
    }
}</code></pre>
<p><code>Compostite</code> 클래스인 <code>BattlegroundBag</code> 의 는 Leaf 를 저장할 수 있는 <code>_components</code> 리스트가 있고 가방안에 있는 모든 아이템의 무게와 갯수를 출력할 수 있도록 구현하게 되었고</p>
<p>가방안에 있는 Item의 종류를 출력해주는 함수도 존재합니다.</p>
<p>이처럼 Leaf / Tree 구조를 통해 전체와 부분을 하나의 객체처럼 사용할 수 있도록 구현하게 됩니다.</p>
<hr>
<h3 id="battleground-play-해보기">Battleground play 해보기</h3>
<pre><code class="language-kotlin">class Battleground {

    init {
        val level3Bag  = BattlegroundBag(&quot;3레벨 가방&quot;)
        val m4 = M4A1()
        val Kar98 = Kar98()
        val bullet5 = Bullet_5(120)
        val bullet7 = Bullet_7(30)

        level3Bag.addItem(m4)
        level3Bag.addItem(Kar98)
        level3Bag.addItem(bullet5)
        level3Bag.addItem(bullet7)

        println(level3Bag.getItemName())
        println(&quot;가방의 총 무게 : &quot; + level3Bag.getWeight() + &quot;g&quot;)
        println(&quot;아이템 리스트 : &quot; + level3Bag.hasItemList())
        println(&quot;총알 개수&quot;)
        println(&quot;5탄 : ${bullet5.getItemCount()}, 7탄 : ${bullet7.getItemCount()}&quot;)
    }
}

private fun main() {
    Battleground()
}</code></pre>
<p><img src="https://velog.velcdn.com/images/aios_/post/71a3a8a6-2fb8-461f-8502-f0b60dc14fb3/image.png" alt=""></p>
<hr>
<h3 id="마무리">마무리</h3>
<p>컴포짓 패턴의 경우 <strong>전체-부분</strong> 의 개념으로 객체를 접근하도록 구현하는 패턴입니다.</p>
<p><strong>트리 구조로 만들어야하는 제약</strong>이 존재 하지만, 하나의 객체를 그룹 또는 각각의 객체로 사용하기 편하기 때문에 사용하게 됩니다.</p>
<h3 id="장점">장점</h3>
<ul>
<li>복잡한 트리 구조를 편리하게 사용할 수 있음.</li>
<li>다형성과 재귀를 활용할 수 있음</li>
<li>클라이언트 코드를 변경하지 않고 새로운 타입을 추가할 수 있음</li>
</ul>
<h3 id="단점">단점</h3>
<ul>
<li>트리를 만들어야 하기 때문에(공통된 인터베이스를 정의) 지나치게 일반화 해야하는 경우가 생길 수 있음</li>
</ul>
<hr>
<h3 id="참고">참고</h3>
<ul>
<li>헤드퍼스트 개정판 [<a href="http://hanbit.co.kr/store/books/look.php?p_code=B6113501223">hanbit.co.kr/store/books/look.php?p_code=B6113501223</a>]</li>
<li>인프런 - 코딩으로 학습하는 GoF 디자인 패턴 [<a href="https://www.inflearn.com/course/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4">https://www.inflearn.com/course/디자인-패턴</a>]</li>
<li><a href="https://velog.io/@lsj8367/Composite-%ED%8C%A8%ED%84%B4">https://velog.io/@lsj8367/Composite-패턴</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[디자인패턴] 브릿지 패턴]]></title>
            <link>https://velog.io/@aios_/%EB%94%94%EC%9E%90%EC%9D%B8%ED%8C%A8%ED%84%B4-%EB%B8%8C%EB%A6%BF%EC%A7%80-%ED%8C%A8%ED%84%B4</link>
            <guid>https://velog.io/@aios_/%EB%94%94%EC%9E%90%EC%9D%B8%ED%8C%A8%ED%84%B4-%EB%B8%8C%EB%A6%BF%EC%A7%80-%ED%8C%A8%ED%84%B4</guid>
            <pubDate>Sun, 30 Apr 2023 07:27:10 GMT</pubDate>
            <description><![CDATA[<h3 id="정의">정의</h3>
<blockquote>
<p>추상적인 것과 구체적인 것을 분리하여 연결하는 패턴.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/aios_/post/2db8ec3a-da9b-447b-89be-e533b9e40927/image.png" alt=""></p>
<hr>
<h3 id="구현할-클래스-목록">구현할 클래스 목록</h3>
<p><img src="https://velog.velcdn.com/images/aios_/post/e2f219de-baa6-40e7-9480-5c4240cbf2d4/image.png" alt=""></p>
<ul>
<li><strong>Abstraction</strong> : 기능 계층의 최상위 클래스, 추상 인터페이스</li>
<li><strong>Refind Abstraction</strong> : 기능 계층에서 새로운 부분을 확장한 클래스</li>
<li><strong>Implementation</strong> : Abstraction의 기능을 구현하기 위한 인터페이스</li>
<li><strong>Concreate Implementation</strong> : 실제 기능을 구현한 클래스</li>
</ul>
<hr>
<h3 id="implementation--concreate-implementation">Implementation &amp; Concreate Implementation</h3>
<p><img src="https://velog.velcdn.com/images/aios_/post/43095c78-77bf-4f7f-a4d0-b4da86fdabda/image.png" alt=""></p>
<h3 id="tv-interface-구현">TV Interface 구현</h3>
<pre><code class="language-kotlin">interface TV {
    fun on()
    fun off()
    fun tuneChannel(channel: Int)
    fun getChannel(): Int
}</code></pre>
<p>TV를 동작시키기 위해 필요한 공통 기능을 <code>TV interface</code> 에 함수로 정의 하고 <code>Concreate Implementation</code> 에 구현하도록 정의 합니다.</p>
<p>추상화(RemoteControl)는 TV interface 에 선언된 메서드를 통해서만 구현 객체와 소통할 수 있습니다.</p>
<h3 id="concreate-implementation-구현">Concreate Implementation 구현</h3>
<pre><code class="language-kotlin">/**
    공통으로 사용하는 on, off 함수를 구현하고 TV의 타입을 받아 구별한다.
*/
abstract class DefaultTV(
    val type: String
) : TV {

    override fun on() {
        println(&quot;Turning on the $type TV.&quot;)
    }

    override fun off() {
        println(&quot;Turning off the $type TV.&quot;)
    }

/**
    LG 브랜드의 TV를 정의하고
    각 TV에 저장된 `channel` 변수를 사용하여 채널을 조작한다.
*/
class LG : DefaultTV(&quot;LG&quot;) {
    private var channel: Int = 1

    override fun tuneChannel(channel: Int) {
        this.channel = channel
        println(&quot;Set the $type TV Channel to ${this.channel}&quot;)
    }

    override fun getChannel(): Int {
        return this.channel
    }

}</code></pre>
<p>각 브랜드 별 TV의 맞춤 코드를 작성하여 동작하도록 합니다.</p>
<hr>
<h3 id="abasraction--refind-abstraction">Abasraction &amp; Refind Abstraction</h3>
<p><img src="https://velog.velcdn.com/images/aios_/post/08f669dd-e3b4-4468-a38d-692634b0149a/image.png" alt=""></p>
<h3 id="remotecontrol-구현">RemoteControl 구현</h3>
<pre><code class="language-kotlin">abstract class RemoteControl(private val tvFactory: TVFactory) {
    private lateinit var tv: TV

    fun on() {
        this.tv.on()
    }

    fun off() {
        this.tv.off()
    }

    fun setChannel(channel: Int) {
        tv.tuneChannel(channel)
    }

    fun getChannel(): Int {
        return this.tv.getChannel()
    }

    fun setTV(type: String) {
        try {
            tv = tvFactory.getTV(type)
        } catch (e: Exception) {
            println(e)
        }
    }
}</code></pre>
<p><code>TV Factory</code> 에서 TV의 종류를 가져와 리모컨의 역할을 수행할 수 있도록 구현된 구현체이다.</p>
<p><strong>구현 객체(TV interface)에 의존</strong>해 하위 수준의 작업을 진행한다.</p>
<h3 id="genericremote-구현">GenericRemote 구현</h3>
<pre><code class="language-kotlin">class GenericRemote(tvFactory: TVFactory) : RemoteControl(tvFactory) {

    fun nextChannel() {
        val channel = this.getChannel()
        this.setChannel(channel + 1)
    }

    fun prevChannel() {
        val channel = this.getChannel()
        this.setChannel(channel - 1)
    }
}</code></pre>
<p>RemoteControl 의 변형된 코드를 제공합니다. 부모처럼 구현 객체를 가져와 새로운 또는 복잡한 동작을 정의합니다.</p>
<hr>
<h3 id="실행해보기">실행해보기</h3>
<pre><code class="language-kotlin">class BridgeClient {
    init {
        val tvFactory = TVFactory()
        val remoteSony = SpecialRemote(tvFactory)
        println()
        println(&quot;Connect your remote to the TV.&quot;)
        remoteSony.setTV(&quot;Sony&quot;)
        remoteSony.on()
        remoteSony.up()
        remoteSony.up()
        remoteSony.down()
        remoteSony.off()
        println(&quot;==============================\n&quot;)

        val remoteLG = GenericRemote(tvFactory)
        println(&quot;Connect your remote to the TV.&quot;)
        remoteLG.setTV(&quot;LG&quot;)
        remoteLG.on()
        remoteLG.setChannel(30)
        remoteLG.nextChannel()
        remoteLG.prevChannel()
        remoteLG.prevChannel()
        remoteLG.off()
        println(&quot;==============================\n&quot;)

        val remoteSamsung = GenericRemote(tvFactory)
        println(&quot;Connect your remote to the TV.&quot;)
        remoteSamsung.setTV(&quot;Samsung&quot;)
        remoteSamsung.setChannel(99)
        remoteSamsung.nextChannel()
        remoteSamsung.nextChannel()
        remoteSamsung.nextChannel()
        remoteSamsung.off()

    }
}</code></pre>
<p><img src="https://velog.velcdn.com/images/aios_/post/83577c8c-b840-4678-a5e2-fd44d058b830/image.png" alt=""></p>
<hr>
<h3 id="마무리">마무리</h3>
<p>브릿지 패턴은 추상적인 것과 구체적인 것을 분리하고 브릿지(다리)를 통해 서로 이어주는 패턴 입니다.</p>
<p>개발자는 해당 패턴을 사용하기 전에 미리 추상적인 것과 구체적인 것을 분리하는 과정이 필요합니다. </p>
<h3 id="장점">장점</h3>
<ul>
<li>추상적인 코드를 구체적인 코드 변경 없이도 독립적으로 확장할 수 있다. (OCP)</li>
<li>추상적인 코드와 구체적인 코드를 분리할 수 있다.</li>
</ul>
<h3 id="단점">단점</h3>
<ul>
<li>계층 구조가 늘어나 복잡도가 증가할 수 있다.</li>
</ul>
<hr>
<h3 id="참고">참고</h3>
<ul>
<li>헤드퍼스트 개정판 [<a href="http://hanbit.co.kr/store/books/look.php?p_code=B6113501223">hanbit.co.kr/store/books/look.php?p_code=B6113501223</a>]</li>
<li>인프런 - 코딩으로 학습하는 GoF 디자인 패턴 [<a href="https://www.inflearn.com/course/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4">https://www.inflearn.com/course/디자인-패턴</a>]</li>
<li>Refactoring GURU website [<a href="https://refactoring.guru/ko/design-patterns/bridge">https://refactoring.guru/ko/design-patterns/bridge</a>]</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[디자인 패턴] 데코레이터 패턴]]></title>
            <link>https://velog.io/@aios_/%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/@aios_/%EB%8D%B0%EC%BD%94%EB%A0%88%EC%9D%B4%ED%84%B0-%ED%8C%A8%ED%84%B4</guid>
            <pubDate>Wed, 29 Mar 2023 15:40:34 GMT</pubDate>
            <description><![CDATA[<h3 id="정의">정의</h3>
<blockquote>
<p>주어진 상황 및 용도에 따라 어떤 객체에 책임을 덧붙이는 패턴으로, 기능 확장이 필요할 때, 서브클래싱 대신 쓸 수 있는 유연한 대안이 될 수 있다.
데코레이터(Decortor) 뜻 그대로 원하는 클래스를 장식한다.
즉, Wrapping 한다 라고 생각할 수 있다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/aios_/post/885b972b-7c86-4638-9f22-262269f5877f/image.png" alt=""></p>
<hr>
<h3 id="구현할-클래스-목록">구현할 클래스 목록</h3>
<p><img src="https://velog.velcdn.com/images/aios_/post/a850c010-4380-4702-8ba2-9c18487eb422/image.png" alt=""></p>
<p><code>Beverage</code> 를 사용하여 커피를 만듭니다.</p>
<hr>
<h3 id="component-beverage-구현">Component (Beverage) 구현</h3>
<pre><code class="language-kotlin">interface Beverage {

    fun getDescription(): String {
        return &quot;Unknown Beverage&quot;
    }

    fun cost(): Double
}</code></pre>
<p>Interface 를 활용하면 실제 구현체를 서브 클래스로 위임할 수 있게 된다.</p>
<p>구현은 ConcreateComponent 에서 <code>override</code> 를 활용하여 구현하게 된다.</p>
<hr>
<h3 id="concreatecomponent">ConcreateComponent</h3>
<p><img src="https://velog.velcdn.com/images/aios_/post/6044eee3-56da-4a62-9384-55a374e47965/image.png" alt=""></p>
<p>다이어 그램과 같이 <code>Beverage</code> implements 하여 구현체를 작성하는 ConcreateComponent 만든다.</p>
<pre><code class="language-kotlin">class Espresso : Beverage {

    override fun getDescription(): String {
        return &quot;Espresso&quot;
    }

    override fun cost(): Double {
        return 1.99
    }
}

class Decaf : Beverage {

    override fun getDescription(): String {
        return &quot;Decaf Coffee&quot;
    }
    override fun cost(): Double {
        return 1.05
    }
}</code></pre>
<p><code>Beverage</code> 의 구현체 클래스를 만들어 보았습니다.</p>
<p>Beverage는 인터페이스기 때문에 Client 에서 사용하기 위해 구현체 클래스를 만들어 주어야 합니다.</p>
<p>Client 에서 사용하기 위해 <code>Espresso</code> , <code>Decaf</code> 클래스를 만들었습니다.</p>
<hr>
<h3 id="decorator--concreatedecorator">Decorator &amp; ConcreateDecorator</h3>
<pre><code class="language-kotlin">// 비슷한 카테고리를 한번에 처리하기 위해 abstract class 만들어 사용할 수 있다.
abstract class CondimentDecorator : Beverage {
    abstract val beverage: Beverage
}

// 기본 Beverage 를 생성자로 받아서 행동을 추가하는 class 이다.
class Milk(override val beverage: Beverage) : CondimentDecorator() {

    override fun getDescription(): String {
        return beverage.getDescription() + &quot;, Milk&quot;
    }
    override fun cost(): Double {
        return .10 + beverage.cost()
    }
}</code></pre>
<p>기본적인 <code>Beverage</code> 클래스를 Decorator 할 클래스를 만듭니다.</p>
<p><em>abstract class</em> 로 만들어 <code>Beverage</code> 에서 사용하는 class 를 <em>override</em> 할 수 있도록 하고</p>
<p><code>Beverage</code> 를 서브 클래스에서 사용하도록 강제합니다.</p>
<p>서브클래스에서는 기본 <code>Beverage</code> 의 값에 추가로 장식할 수 있도록 함수를 재정의합니다.</p>
<p><em>Decorator class</em> 만들어 사용하게 되면, 원래 데이터가 Wrapping 되는 효과를 얻을 수 있습니다.</p>
<hr>
<h3 id="사용해보기">사용해보기</h3>
<pre><code class="language-kotlin">class AppCafe {
    init {
        val bw = System.out.bufferedWriter()
        var beverage1: Beverage = Decaf()
        bw.appendLine(&quot;1번 커피 주문&quot;)
        bw.appendLine(&quot;${beverage1.getDescription()} $${beverage1.cost()}&quot;)

        var beverage2: Beverage = HouseBlend()
        beverage2 = Mocha(beverage2)
        beverage2 = Mocha(beverage2)
        beverage2 = Whip(beverage2)
        bw.appendLine(&quot;2번 커피 주문&quot;)
        bw.appendLine(&quot;${beverage2.getDescription()} $${beverage2.cost()}&quot;)

        var beverage3: Beverage = Espresso()
        beverage3 = Milk(beverage3)
        beverage3 = Soy(beverage3)
        beverage3 = Mocha(beverage3)
        bw.appendLine(&quot;3번 커피 주문&quot;)
        bw.appendLine(&quot;${beverage3.getDescription()} $${beverage3.cost()}&quot;)

        bw.flush()
    }
}

private fun main() {
    AppCafe()
}</code></pre>
<p>모든 데코레이터를 만들고 커피를 주문해보겠습니다.</p>
<p>1번 커피의 경우 디카페인 커피에 아무것도추가하지 않습니다.</p>
<p>2번 커피는 기본 블랜딩 + 2개의 모카 + 휘핑 으로 주문합니다.</p>
<p>3번 커피는 에스프레소 + 우유 + 두유 + 모카 로 주문합니다.</p>
<p>기본 Beverage 에 Decorator class 덮어 Wrapping 하면 Beverage 에 Decorator에서 추가한 작업이 (+) 되어 return 됩니다.</p>
<p><img src="https://velog.velcdn.com/images/aios_/post/46389cb8-dd6d-4f53-9d02-1db0a805b64f/image.png" alt=""></p>
<hr>
<h3 id="마무리">마무리</h3>
<p>Decorator 하고 싶은 Target을 Interface로 정의하고 구현체 Class 추가로 만듭니다.</p>
<p>생성자로 Target Interface 를 받아 장식하고 싶은 부분을 <em>override</em> 를 사용하여 추가합니다.</p>
<p>Decortor 를 지난 Target 은 Wrapping 되어 내가 추가한 값이 저장됩니다.</p>
<h3 id="장점">장점</h3>
<ul>
<li>새로운 클래스를 만들지 않고 기존 기능을 조합할 수 있다.(이미 생성된 Decortor class 조합하면 다양한 경우의 수를 조합할 수 있습니다.)</li>
<li>컴파일 타임이 아닌 런타임에 동적으로 기능을 변경할 수 있다.</li>
</ul>
<h3 id="단점">단점</h3>
<ul>
<li>테코레어터를 조합하는 코드가 복잡할 수 있다.</li>
</ul>
<hr>
<h3 id="참고">참고</h3>
<ul>
<li>헤드퍼스트 개정판 [<a href="http://hanbit.co.kr/store/books/look.php?p_code=B6113501223">hanbit.co.kr/store/books/look.php?p_code=B6113501223</a>]</li>
<li>인프런 - 코딩으로 학습하는 GoF 디자인 패턴 [<a href="https://www.inflearn.com/course/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4">https://www.inflearn.com/course/디자인-패턴</a>]</li>
<li>준비된 개발자 [<a href="https://readystory.tistory.com/125">https://readystory.tistory.com/125</a>]</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[디자인 패턴] 어댑터 패턴]]></title>
            <link>https://velog.io/@aios_/%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%8C%A8%ED%84%B4</link>
            <guid>https://velog.io/@aios_/%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%8C%A8%ED%84%B4</guid>
            <pubDate>Mon, 20 Mar 2023 14:18:23 GMT</pubDate>
            <description><![CDATA[<h3 id="정의">정의</h3>
<blockquote>
<p>기존 코드를 클라이언트가 사용하는 인터페이스의 구현체로 바꿔주는 패턴</p>
<p>클라이언트가 사용하는 인터페이스를 따르지 않는 기존 코드를 재사용할 수 있게 해준다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/aios_/post/0d553e46-5550-432b-8a73-e456f5246767/image.png" alt=""></p>
<hr>
<h3 id="구현할-클래스-목록">구현할 클래스 목록</h3>
<p><img src="https://velog.velcdn.com/images/aios_/post/700d9869-5d2b-4866-9c07-07f84b9e9f24/image.png" alt=""></p>
<p><code>Account</code> 객체로 <code>LoginHelper</code> 를 통해 해당 유저가 로그인이 가능한 상태인지 확인합니다.</p>
<hr>
<h3 id="adaptee-구현">Adaptee 구현</h3>
<pre><code class="language-kotlin">// 사용자의 데이터를 저장하는 객체
data class Account(
    var name: String,
    var password: String,
    var email: String
)

// 저장된 사용자의 정보를 핸들링 할 수 있는 class
// 예시를 위해 findAccountByUsername 할 때,
// Account 를 생성하도록 구현하였습니다.
class AccountService {
    fun findAccountByUsername(username: String): Account {
        val account = Account(
            name = username,
            password = username,
            email = username
        )

        return account
    }

    fun createNewAccount(account: Account) {}

    fun updateAccount(account: Account) {}
}</code></pre>
<p>Adaptee 에 해당하는 클래스를 만들었습니다.</p>
<p><code>Account</code> 객체에는 회원가입된 사용자의 정보를 담을 수 있는 정보를 가지고 있으며,</p>
<p><code>AccountService</code> 는 <code>Account</code> 의 생성, 변경, 찾기 등 여러 동작을 지원하는 클래스입니다.</p>
<p>우리가 구현하고자하는 목표는 <code>Account</code> 를 활용하여 <code>LoginHelper</code>를 통해 로그인이 가능한지 확인하는 것입니다.</p>
<p><code>Account</code>를 <code>LoginHeldper</code> 에서 사용할 수 있도록 하는 어댑터가 필요합니다.</p>
<hr>
<h3 id="target-interface-만들기">Target Interface 만들기</h3>
<pre><code class="language-kotlin">// Account 의 정보를 가지고 올 수 있는 target
interface UserDetails {
    fun getUsername(): String
    fun getPassword(): String
}

// 로그인 하고자 하는 username 을 통해 UserDetail 를 return
interface UserDetailService {
    fun loadUser(username: String): UserDetails
}</code></pre>
<p>2가지 Target Interface 를 구현합니다.</p>
<ul>
<li><code>UserDetailService</code> : username 을 이용하여 즉, 사용자의 이름을 이용하여 <code>UserDeatils</code> 을 반환합니다. 이렇게 만들면 <code>Account</code> 객체를 <strong>직접 참조하지 않고</strong> <code>UserDetails</code> 클래스에 정의된 함수를 통해 Account 의 데이터에 접근할 수 있습니다.</li>
<li><code>UserDetails</code> : <code>Account</code> 객체를 직접 참조 하지 않고 구현체에 동작을 위임 할 수 있습니다. 현재는 간단하게 <code>getUsername()</code>, <code>getPassword()</code> 만 구현 하였지만 Adapter 클래스에서 해당 함수를 구현할 때, validate 처리, 잘못 된 값 처리 등 다양한 동작을 추가할 수 있습니다.</li>
</ul>
<hr>
<h3 id="adapter-만들기">Adapter 만들기</h3>
<pre><code class="language-kotlin">class AccountUserDetails(
    private val account: Account
) : UserDetails {
    override fun getUsername(): String {
        return this.account.name
    }

    override fun getPassword(): String {
        return this.account.password
    }
}

class AccountUSerDetailService(
    private val accountService: AccountService
) : UserDetailService {

    override fun loadUser(username: String): UserDetails {
        val account = accountService.findAccountByUsername(username)
        return AccountUserDetails(account)
    }
}</code></pre>
<ul>
<li><p><em><strong>AccountUserDetailService</strong></em></p>
<ul>
<li><code>AccountService</code> 를 생성자로 받아와서 사용자의 정보(Account) 를 찾아서 <code>AccountDetails</code> Adapter를 반환합니다.</li>
<li><code>Account</code> 는 <code>AccountUserDetails</code> Adapter 에 캡슐화 되어 직접 참조가 불가능한 상태가 되어 집니다.</li>
</ul>
</li>
<li><p><em><strong>AccountUserDetails</strong></em></p>
<ul>
<li><code>Account</code> 를 생성자로 받아 <code>Account</code> 의 데이터를 <strong>UserDetails Target Interface</strong> 에 정의된 함수로 값을 return 합니다.</li>
<li>직접 참조하지 않고 오버라이딩 된 함수를 통해서만 <code>Account</code> 객체에 접근할 수 있어 보다 안전하게 객체를 관리할 수 있습니다.</li>
</ul>
</li>
</ul>
<hr>
<h3 id="로그인-할-수-있는지-확인하기">로그인 할 수 있는지 확인하기</h3>
<pre><code class="language-kotlin">class LoginHandler(private var userDetailsService: UserDetailService) {

    fun login(username: String, password: String): String {
        val userDetails = userDetailsService.loadUser(username)
        return if (userDetails.getPassword() == password) {
            userDetails.getUsername()
        } else {
            throw IllegalArgumentException()
        }
    }
}

// App.kt
private fun main() {
    val accountService = AccountService()
    val userDetailService = AccountUSerDetailService(accountService)
    val loginHandler = LoginHandler(userDetailService)
    val login = loginHandler.login(&quot;Jack&quot;, &quot;Jack&quot;)
    println(login)
}</code></pre>
<p>모든 구현이 끝났습니다.</p>
<p>LoginHelper 에 login() 매소드를 통해 해당 유저가 로그인 가능한지 확인하면 됩니다.</p>
<p>LoginHelper 는 UserDetailService 생성자를 통해 로그인한 사용를 조회 합니다.</p>
<p><code>Account</code> 객체 대신 <code>UserDetails</code> 객체를 반환하고 <code>userDetails.getPassword() == password</code> 조회된 사용자의 비밀번호와 입력한 비밀번호가 같다면 <strong>로그인</strong></p>
<p>다르다면 <code>Exception</code> 을 던지도록 구현합니다.</p>
<hr>
<h3 id="마무리">마무리</h3>
<p>쉽게 생각하면 A 생성자로 B 라는 새로운 객체를 만든다고 생각하시면 쉽게 이해할 수 있습니다.</p>
<p>모든 어탭터 패턴이 <code>A → B → C</code> 이렇게 변하지는 않습니다.</p>
<h3 id="장점">장점</h3>
<ul>
<li>기존 코드를 변경하지 않고 원하는 인터페이스 구현체를 만들어 재사용할 수 있다. <strong>(Open-Close principle, Single Responsibility principle)</strong></li>
<li>기존 코드가 하던 일과 특정 인터페이스 구현체로 변환하는 작업을 각기 다른 클래스로 분리하여 관리할 수 있다. <strong>(Interface Segregation principle)</strong></li>
</ul>
<h3 id="단점">단점</h3>
<ul>
<li>새 클래스가 생겨 복잡도가 증가할 수 있다. 경우에 따라서는 기존 코드가 해당 인터페이스를 구현하도록 수정하는 것이 좋은 선택이 될 수도 있다.</li>
</ul>
<hr>
<h2 id="참고">참고</h2>
<ul>
<li>헤드퍼스트 개정판 [<a href="http://hanbit.co.kr/store/books/look.php?p_code=B6113501223">hanbit.co.kr/store/books/look.php?p_code=B6113501223</a>]</li>
<li>인프런 - 코딩으로 학습하는 GoF 디자인 패턴 [<a href="https://www.inflearn.com/course/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4">https://www.inflearn.com/course/디자인-패턴</a>]</li>
<li>dp.chys - velog [<a href="https://velog.io/@ljinsk3/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-Adapter-Pattern">https://velog.io/@ljinsk3/디자인-패턴-Adapter-Pattern</a>]</li>
<li>준비된 개발자 [<a href="https://readystory.tistory.com/125">https://readystory.tistory.com/125</a>]</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[디자인 패턴] 빌더 패턴]]></title>
            <link>https://velog.io/@aios_/%EB%94%94%EC%9E%90%EC%9D%B8%ED%8C%A8%ED%84%B4-%EB%B9%8C%EB%8D%94-%ED%8C%A8%ED%84%B4</link>
            <guid>https://velog.io/@aios_/%EB%94%94%EC%9E%90%EC%9D%B8%ED%8C%A8%ED%84%B4-%EB%B9%8C%EB%8D%94-%ED%8C%A8%ED%84%B4</guid>
            <pubDate>Sun, 19 Mar 2023 07:09:42 GMT</pubDate>
            <description><![CDATA[<h2 id="정의">정의</h2>
<blockquote>
<p>복잡한 객체를 생성하는 방법을 정의하는 클래스와 표현하는 방법을 정의하는 클래스를 별도로 분리.
서로 다른 표현이라도 이를 생성할 수 있는 동일한 절차를 제공</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/aios_/post/3c9774f6-09bd-4341-9e1b-3601cb1f4531/image.png" alt=""></p>
<ul>
<li><p>Android 개발을 진행하면 Builder Pattern을 많이 찾아 볼 수 있습니다.</p>
<p>  ex) Dialog, Retrofit, Okhttp, Glidle 등</p>
</li>
</ul>
<hr>
<h2 id="구현할-클래스-목록">구현할 클래스 목록</h2>
<p><img src="https://velog.velcdn.com/images/aios_/post/f1face79-ae01-4452-ba14-387cb4f7a244/image.png" alt=""></p>
<ul>
<li><strong>BuilderClientSimple</strong>: 빌더 항목을 사용합니다.</li>
<li><strong>TourPlanBuilder</strong>: 빌더 생성에 필요한 메소드를 제공</li>
<li><strong>Tour, DetailPlan</strong>: 빌더를 만들기 위한 Data Class(Product)</li>
<li><strong>DefaultTourBuilder</strong>: 인터페이스를 구현하여 Tour 객체를 만듬</li>
</ul>
<hr>
<h2 id="product-만들기">Product 만들기</h2>
<pre><code class="language-kotlin">/*
  빌더에 사용될 복잡한 Data Class 정의합니다.
*/
data class Tour(
    val name: String,
    val startDate: LocalDate,
    val nights: Int,
    val days: Int,
    val whereToStay: String,
    val detailPlans: List&lt;DetailPlan&gt;
) {
    override fun toString(): String {
        val sb = StringBuffer()
        sb.appendLine(&quot;====  $name  ====&quot;)
            .appendLine(&quot;date: $startDate&quot;)
            .appendLine(&quot;$nights 박 $days 일&quot;)
            .appendLine(&quot;stay: $whereToStay&quot;)
            .appendLine(&quot;plan&quot;)
            .appendLine(detailPlans.makeToString())
        return sb.toString()
    }

    private fun List&lt;DetailPlan&gt;.makeToString(): String {
        val sb = StringBuilder()
        this.forEach {
            sb.append(it)
                .append(&quot;\n&quot;)
        }
        if (this.isEmpty()) sb.append(&quot;There are no detailed plans.&quot;)
        return sb.toString()
    }
}

data class DetailPlan(
    val no: Int,
    val plan: String
) {
    override fun toString(): String {
        return &quot;$no - $plan&quot;
    }
}</code></pre>
<p>Tour 객체를 생성하기 위해서는 총 6가지의 파라미터를 넣어주어야하며,</p>
<p>nights, days 객체의 상관관계가 포함되어야 합니다.</p>
<pre><code class="language-kotlin">fun main() {
  // 예시
    val tour = Tour(
        name = &quot;tour&quot;,
        startDate = LocalDate.now(),
        nights = -123,
        days = 4,
        whereToStay = &quot;&quot;,
        detailPlans = listOf()
    )
}</code></pre>
<p>위 예시 처럼 -123박 4일 이라는 불완전한 객체가 생성 되어 버렸습니다.</p>
<p>Builder Pattern 을 사용하면 이러한 경우를 방지하는 코드를 만들 수 있고, 객체간의 상관 관계를 정의 하여 객체를 완전하게 생성할 수 있게 됩니다.</p>
<hr>
<h2 id="builder-interface-만들기">Builder Interface 만들기</h2>
<pre><code class="language-kotlin">interface TourPlanBuilder {

    fun nightsAndDays(nights: Int, days: Int): TourPlanBuilder

    fun title(title: String): TourPlanBuilder

    fun startDate(startDate: LocalDate): TourPlanBuilder

    fun whereToStay(whereToStay: String): TourPlanBuilder

    fun addPlan(no: Int, plan: String): TourPlanBuilder

    fun removePlan(plan: DetailPlan): TourPlanBuilder

    fun build(): Tour
}</code></pre>
<p><code>Tour</code> 객체 생성에 필요한 필드를 설정할 수 있도록 메소드를 만듭니다.</p>
<p>직접 적인 구현은 Builder Interface 를 상속받은 Class 에서 정의해주면 됩니다. 여기서는 객체 생성에 필요한 함수만 만들어 주면 됩니다.</p>
<hr>
<h3 id="concreate-builder-만들기">Concreate Builder 만들기</h3>
<pre><code class="language-kotlin">class DefaultTourBuilder : TourPlanBuilder {

    private var name: String = &quot;Default Tour&quot;
    private var startDate: LocalDate = LocalDate.now()
    private var nights: Int = 0
    private var days: Int = 1
    private var whereToStay: String = &quot;Hotel&quot;
    private val detailPlans: MutableList&lt;DetailPlan&gt; = mutableListOf()
    override fun nightsAndDays(nights: Int, days: Int): TourPlanBuilder = apply {
        this.nights = nights
        this.days = days
    }

    override fun title(title: String): TourPlanBuilder = apply {
        this.name = title
    }

    override fun startDate(startDate: LocalDate): TourPlanBuilder = apply {
        this.startDate = startDate
    }

    override fun whereToStay(whereToStay: String): TourPlanBuilder = apply {
        this.whereToStay = whereToStay
    }

    override fun addPlan(no: Int, plan: String): TourPlanBuilder = apply {
        this.detailPlans.add(DetailPlan(no, plan))
    }

    override fun removePlan(plan: DetailPlan): TourPlanBuilder = apply {
        if (this.detailPlans.contains(plan)) {
            this.detailPlans.remove(plan)
        }
    }

    override fun build(): Tour {
        println(&quot;Create Default Tour !!&quot;)
        return Tour(name, startDate, nights, days, whereToStay, detailPlans)
    }

}</code></pre>
<p>Kotlin 의 최대 장점인 확장 함수를 통해 Concreate Builder Class 만들었습니다.</p>
<p><code>apply</code>를 활용하여 builder 객체를 무조건 적으로 반환할 수 있도록 구현 할 수 있습니다.</p>
<p>마지막에 <code>build()</code> 함수를 통해 우리가 원하는 <code>Tour</code> 객체를 만들 수 있습니다.</p>
<p><code>Tour</code> 객체에 필요한 파라미터 변수는 Concreate class 에서만 접근 할 수 있도록 <code>private</code> 로 선언해야합니다. 그래서 interface 로 추가해준 함수로 값을 접근할 수 있도록 해야 합니다.</p>
<hr>
<h3 id="client-만들기">Client 만들기</h3>
<pre><code class="language-kotlin">class BuilderClientSimple {
    init {
        val defaultTourBuilder = DefaultTourBuilder()
        val tour = defaultTourBuilder
            .title(&quot;JangHee Tour&quot;)
            .nightsAndDays(2, 3)
            .startDate(LocalDate.of(2023, 3, 13))
            .whereToStay(&quot;근사한 호텔&quot;)
            .addPlan(1, &quot;비행기 타기&quot;)
            .addPlan(2, &quot;바다 보기&quot;)
            .addPlan(3, &quot;맛있는 저녁 먹기&quot;)
            .build()

        println(tour)

        val simpleTour = DefaultTourBuilder()
            .title(&quot;Simple Tour&quot;)
            .startDate(LocalDate.of(2023, 5, 5))
            .build()
        println(simpleTour)
    }
}

private fun main() {
    BuilderClientSimple()
}</code></pre>
<p>2가지의 예시 투어를 만들었습니다.</p>
<p>1번은 상세계획이 있는 Tour 이고</p>
<p>2번은 간단한 Tour 입니다.</p>
<p>builder에 함수를 계속 추가하여 객체를 생성합니다.</p>
<hr>
<h3 id="출력-결과">출력 결과</h3>
<p align="center">
  <img src="https://velog.velcdn.com/images/aios_/post/9f710a73-aa62-486c-a3c1-588ca38acdcd/image.png" />
  <span style="color:#bdbdbd">
    <small><i>1번 자세한 Tour</i></small>
  </span>
</p>

<p align="center">
  <img src="https://velog.velcdn.com/images/aios_/post/24e6f008-258c-4211-ae01-68144b2ac656/image.png" />
  <span style="color:#bdbdbd">
    <small><i>2번 간단한 Tour</i></small>
  </span>
</p>


<hr>
<h3 id="장점">장점</h3>
<blockquote>
<p>만들기 복잡한 객체를 순차적으로 만들 수 있다.
복잡한 객체를 만드는 과정을 숨길 수 있다.
동일한 프로세스를 통해 각기 다르게 구성된 객체를 만들 수 있다.
불완전한 객체를 사용하지 못하도록 방지 할 수 있다.</p>
</blockquote>
<h3 id="단점">단점</h3>
<blockquote>
<p>원하는 객체를 만들기 전에 빌더 부터 만들어야 한다.
구조가 복잡해 진다.</p>
</blockquote>
<hr>
<h2 id="참고">참고</h2>
<ul>
<li>헤드퍼스트 개정판 [<a href="http://hanbit.co.kr/store/books/look.php?p_code=B6113501223">hanbit.co.kr/store/books/look.php?p_code=B6113501223</a>]</li>
<li>인프런 - 코딩으로 학습하는 GoF 디자인 패턴 [<a href="https://www.inflearn.com/course/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4">https://www.inflearn.com/course/디자인-패턴</a>]</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[디자인 패턴] 옵저버 패턴]]></title>
            <link>https://velog.io/@aios_/%EC%98%B5%EC%A0%80%EB%B2%84-%ED%8C%A8%ED%84%B4</link>
            <guid>https://velog.io/@aios_/%EC%98%B5%EC%A0%80%EB%B2%84-%ED%8C%A8%ED%84%B4</guid>
            <pubDate>Thu, 28 Jul 2022 14:14:02 GMT</pubDate>
            <description><![CDATA[<h2 id="정의">정의</h2>
<blockquote>
<p>객체가 특정 객체 상태 변화를 감지하고 알림을 받는 패턴.
발생(publish) - 구독(subscribe) 패턴을 구현할 수 있다.
객체의 상태가 변경되면 Observer 의존하고 있는 객체에 업데이트된 상태를 알려주며,
일대다(one-to-many) 의존성 정의이다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/aios_/post/fb8650af-e68c-4c1c-984f-7e05102eea5c/image.png" alt=""></p>
<hr>
<h2 id="구현할-옵저버-패턴-클래스">구현할 옵저버 패턴 클래스</h2>
<p><img src="https://velog.velcdn.com/images/aios_/post/e09679df-7dfa-4b3e-bb8e-dc6205142ce8/image.png" alt=""></p>
<hr>
<h2 id="옵저버-인터페이스-만들기">옵저버 인터페이스 만들기</h2>
<ul>
<li>Observer</li>
</ul>
<pre><code class="language-kotlin">interface Observer {
        // update 함수의 생성자를 통해
        // 알려주고 싶은 상태를 생성자에 정의한다.
    fun update(temp: Float, humidity: Float, pressure: Float)
}</code></pre>
<ul>
<li>Subject</li>
</ul>
<pre><code class="language-kotlin">/**
 * 옵저버를 등록, 제거, 알림 기능을 제공하는 인터페이스이다.
 * 옵저버 패턴은 구독을하고 해제하는 함수를 항상 만들어 두어야 하며,
 * 필요가 없는 시점에 해제하지 않으면 메모리릭이 발생하기 때문에 사용하지 않는다면,
 * 꼭 해제를 명시적으로 해제를 시켜주어야 한다.
 * notifyObservers() 함수를 통해 구독된 옵저버 모두에게 변경된 상태를 알려 준다.
 */
interface Subject {
    fun registerObserver(o: Observer)
    fun removeObserver(o: Observer)
    fun notifyObservers()
}</code></pre>
<p>옵저버 인터페이스와 옵저버를 등록하는 인터페이스를 만들었다.</p>
<p>다음으로는 Subject 정의하는 클래스를 만들어 보자.</p>
<hr>
<h3 id="옵저버-등록-해제-알림-클래스-만들기">옵저버 등록, 해제, 알림 클래스 만들기</h3>
<pre><code class="language-kotlin">class WeatherData : Subject {
    private var observers: ArrayList&lt;Observer&gt; = arrayListOf()
    private var temperature: Float = 0f
    private var humidity: Float = 0f
    private var pressure: Float = 0f

    override fun registerObserver(o: Observer) {
        observers.add(o)
    }

    override fun removeObserver(o: Observer) {
        observers.remove(o)
    }

    override fun notifyObservers() {
        observers.forEach {
            it.update(temperature, humidity, pressure)
        }
    }

    fun measurementsChanged() {
        notifyObservers()
    }

    fun setMeasurements(temperature: Float, humidity: Float, pressure: Float) {
        this.temperature = temperature
        this.humidity = humidity
        this.pressure = pressure
        measurementsChanged()
    }
}</code></pre>
<ul>
<li><code>Subject</code> 인터페이스를 등록한다.</li>
<li>일대다(one-to-many) 구현을 위해 <code>ArrayList&lt;Observer&gt;</code> 생성한다.</li>
<li>업데이트해 줄 데이터를 선언한다.</li>
<li>Subject 인터페이스의 매소드를 구현한다.</li>
<li>상태가 변경되면 <code>notifyObservers()</code> 호출하여 구독하고 있는 사용자에게 알려준다.</li>
</ul>
<p><code>registerObserver(Observer)</code> 함수를 통해 ArrayList 에 구독자 정보를 저장하고 <code>notifyObservers()</code> 함수를 통해 ArrayList 에 저장되어 있는 모든 Observer 객체에게 데이터가 변경되 었다고 알려준다.</p>
<p>더 이상 구독된 정보를 받기를 원하지 않는 경우 또는 옵저버를 사용하지 않는 경우에 <code>removeObserver(Observer)</code> 함수를 통해 메모리에서 삭제한다.</p>
<hr>
<h2 id="옵저버-객체-만들기">옵저버 객체 만들기</h2>
<pre><code class="language-kotlin">class ForecastDisplay(
    weatherData: WeatherData
) : Observer, DisplayElement {
    private var currentPressure = 29.92f
    private var lastPressure: Float = 0f

    init {
        weatherData.registerObserver(this)
    }

    override fun display() {
        println(&quot;Forecast: &quot;)
        if (currentPressure &gt; lastPressure) {
            println(&quot;Improving weather on the way!&quot;)
        } else if (currentPressure == lastPressure) {
            println(&quot;More of the same&quot;)
        } else if (currentPressure &lt; lastPressure) {
            println(&quot;Watch out for cooler, rainy weather&quot;)
        }
    }

    override fun update(temp: Float, humidity: Float, pressure: Float) {
        lastPressure = currentPressure
        currentPressure = pressure
        display()
    }
}</code></pre>
<ul>
<li>생성자에 <code>Subject</code> 가 만들어져 있는 클래스를 받는다.</li>
<li><code>Observer</code> 인터페이스를 상속하여 <code>update()</code> 함수를 정의한다.</li>
<li><code>init</code> 함수를 통해 구독 요청을 보낸다.</li>
<li><code>notifyObservers()</code> 함수가 호출 되면 <code>update()</code> 정의해둔 로직이 실행 되어 데이터의 상태가 변경 된다.</li>
</ul>
<hr>
<h3 id="옵저버-실행-하기">옵저버 실행 하기</h3>
<pre><code class="language-kotlin">fun main() {
    val weatherData: WeatherData = WeatherData()

    val currentDisplay = CurrentConditionsDisplay(weatherData)
    val statisticsDisplay = StatisticsDisplay(weatherData)
    val forecastDisplay = ForecastDisplay(weatherData)

    println(&quot;첫 번째 데이터&quot;)
    weatherData.setMeasurements(85f, 65f, 30.4f)
    println(&quot;두 번째 데이터&quot;)
    weatherData.setMeasurements(82f, 70f, 29.2f)
    println(&quot;세 번째 데이터&quot;)
    weatherData.setMeasurements(78f, 90f, 29.2f)

    weatherData.removeObserver(forecastDisplay)
    println(&quot;\nForecastDisplay 구독 취소 후 데이터&quot;)
    weatherData.setMeasurements(62f, 90f, 28.1f)
}</code></pre>
<ul>
<li><code>Subject</code> 생성하는 <code>WeatherData</code> 클래스를 초기화 한다.</li>
<li>구독을 원하는 <code>Observer</code> 상속되어 있는 클래스를 초기화 한다.</li>
<li><code>setMeasurements()</code> 함수를 통해 구독자에게 데이터가 변경되었음을 알려준다.</li>
<li><code>forecastDisplay</code> 객체의 구독을 제거한다.</li>
<li>그 이후 데이터는 어떻게 나오게 될까? 확인해 보자.</li>
</ul>
<h3 id="결과">결과</h3>
<p><img src="https://velog.velcdn.com/images/aios_/post/be0b28b4-53f4-402a-a5eb-2fd15460e5d4/image.png" alt=""></p>
<p><code>ForecastDisplay</code> 구독을 취소 후 데이터를 변경하면,</p>
<p>기존에 <code>CurrentConditionsDisplay</code>, <code>StatisticsDisplay</code>의 데이터만 변경되고</p>
<p><code>ForecastDisplay</code>는 더 이상 데이터 변화를 감지할 수 없다.</p>
<hr>
<h2 id="특징">특징</h2>
<ul>
<li><p>장점</p>
<ul>
<li>상태를 변경하는 객체(Publisher)와 변경을 감지하는 객체(Subsriber)의 관계를 느슨하게 유지할 수 있다.</li>
<li>Subejct의 상태 변경을 주기적으로 조회하지 않고 자동으로 감지할 수 있다.</li>
<li>런타임에 옵저버를 추가하거나 제거할 수 있다.</li>
</ul>
</li>
<li><p>단점</p>
<ul>
<li>복잡도가 증가한다.</li>
<li>다수의 Observer 객체를 등록 이후 해지하지 않는다면 메모리릭이 발생할 수도 있다.</li>
</ul>
</li>
</ul>
<hr>
<h2 id="참고">참고</h2>
<ul>
<li>헤드퍼스트 개정판 [<a href="http://hanbit.co.kr/store/books/look.php?p_code=B6113501223">hanbit.co.kr/store/books/look.php?p_code=B6113501223</a>]</li>
<li>인프런 - 코딩으로 학습하는 GoF 디자인 패턴 [<a href="https://www.inflearn.com/course/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4">https://www.inflearn.com/course/디자인-패턴</a>]</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[sheet 오픈소스 기여 회고록]]></title>
            <link>https://velog.io/@aios_/sheet-%EC%98%A4%ED%94%88%EC%86%8C%EC%8A%A4-%EA%B8%B0%EC%97%AC-%ED%9A%8C%EA%B3%A0%EB%A1%9D</link>
            <guid>https://velog.io/@aios_/sheet-%EC%98%A4%ED%94%88%EC%86%8C%EC%8A%A4-%EA%B8%B0%EC%97%AC-%ED%9A%8C%EA%B3%A0%EB%A1%9D</guid>
            <pubDate>Fri, 27 May 2022 06:51:51 GMT</pubDate>
            <description><![CDATA[<p>사이드 프로젝트를 진행하면서 캘린더를 사용하기 위해 라이브러리를 찾는 와중에 “sheet” 오픈소스 라이브러리를 확인을 통해 <strong>Contributor</strong>된 회고록을 작성해보려고 합니다.</p>
<ul>
<li>Contribute을 하기 위해서 Readme에 Contribute작성 가이드를 확인 후 버그를 수정하고 pull request를 보내면 된다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/aios_/post/3bc10955-2088-47cc-8081-71c518f85981/image.png" alt=""></p>
<hr>
<h2 id="이슈-수정-시작하기">이슈 수정 시작하기</h2>
<h3 id="1-이슈-열기">1. 이슈 열기</h3>
<p>이슈는 Github 페이지의 issue 탭에 들어가서 게시물을 작성하면 됩니다.</p>
<ol>
<li>우측 상단의 “New iusse” 버튼을 클릭</li>
</ol>
<p><img src="https://velog.velcdn.com/images/aios_/post/af3866c6-57e8-4fa2-a908-0eba18705bde/image.png" alt="sheets 이슈"></p>
<ol start="2">
<li>해당 이슈의 종류를 선택 ( 버그 or 다른 요청 )</li>
</ol>
<p><img src="https://velog.velcdn.com/images/aios_/post/b2ae8cf5-9c22-4835-966e-0da23192f26d/image.png" alt=""></p>
<ol start="3">
<li>버그리포트 작성시 작성 가이드를 아래 사진과 같이 준다면 해당 가이드에 맞춰 작성</li>
</ol>
<p><img src="https://velog.velcdn.com/images/aios_/post/bc119a17-da61-46ed-b0fb-7cbb50c15e8b/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/aios_/post/304fbf30-f79d-417a-8898-c9b5e4d3bc11/image.png" alt=""></p>
<ol start="4">
<li>우측 라벨에 어떤 종류의 버그인지 태그 기능 사용</li>
</ol>
<p><img src="https://velog.velcdn.com/images/aios_/post/4d3230bd-6113-46d3-8f5e-31828aa938ca/image.png" alt=""></p>
<ol start="5">
<li>“Submit new issue” 버튼 클릭하여 이슈 등록 → 끝 !!</li>
</ol>
<aside>
💡 제가 직접 작성한 issue

</aside>

<p><img src="https://velog.velcdn.com/images/aios_/post/2eb7d222-76fd-4f3b-9c93-ffd40d538c15/image.png" alt=""></p>
<hr>
<h3 id="2-이슈-수정">2. 이슈 수정</h3>
<ul>
<li>수정 전 이슈 사항</li>
</ul>
<p><img src="https://velog.velcdn.com/images/aios_/post/696d99f0-6ec9-4a3f-a4e9-1c159d8cd7a2/image.gif" alt=""></p>
<p><img src="https://velog.velcdn.com/images/aios_/post/853fc5aa-e2f6-4ddd-ada3-a6fa84324ed0/image.gif" alt=""></p>
<p>좌우 스와이프를 했을 때,
년, 월 Spinner Adapter의 item이 선택된 데이터와 일치하지 않는 문제를 발견하고 수정을 진행 하였습니다.</p>
<ul>
<li><strong>원인파악</strong></li>
</ul>
<p>캘린더의 스와이프 이벤트를 먼저 찾아보겠습니다.</p>
<pre><code class="language-kotlin">// CalendarSheet.kt
package com.maxkeppeler.sheets.calendar

...

private fun setupMonthScrollListener() {
        // 캘린더의 스와이프 이벤트가 발생 되는 곳
    binding.calendarView.monthScrollListener = { month -&gt;
        val day = month.weekDays.first().first { it.owner == DayOwner.THIS_MONTH }
        selectedViewDate = day.date
        monthAdapter.updateCurrentYearMonth(day.date.yearMonth)
        updateSpinnerValues()
    }
}
...</code></pre>
<p>해당 함수의 동작을 파악한 결과</p>
<p><code>monthScrollListener</code> 에서 날짜 정보를 받아서 Spinner의 value를 바꿔주고 있습니다.</p>
<p>선택된 Month Item 구현 코드</p>
<pre><code class="language-kotlin">// MonthAdapter.kt
package com.maxkeppeler.sheets.calendar

...

// 현재 상태를 확인하여 글자의 Style를 변경하는 코드
private fun SheetsCalendarMonthItemBinding.handleState(monthAtIndex: Month) = when {
    disablePast &amp;&amp; currentSelectedYear.year == currentYearMonth.year 
&amp;&amp; monthAtIndex.value &lt; currentYearMonth.month.value -&gt; {
        month.isSelected = false
        month.setTextAppearance(ctx, R.style.TextAppearance_MaterialComponents_Subtitle2)
        month.setTextColor(textColor)
        month.alpha = 0.2f
    }
    disableFuture &amp;&amp; currentSelectedYear.year == currentYearMonth.year 
&amp;&amp; monthAtIndex.value &gt; currentYearMonth.month.value -&gt; {
        month.isSelected = false
        month.setTextAppearance(ctx, R.style.TextAppearance_MaterialComponents_Subtitle2)
        month.setTextColor(textColor)
        month.alpha = 0.2f
    }
    currentYearMonth.month == monthAtIndex &amp;&amp; selectedMonth == monthAtIndex -&gt; {
        month.isSelected = true
        month.setTextAppearance(ctx, R.style.TextAppearance_MaterialComponents_Subtitle2)
        month.setTextColor(primaryColor)
        month.alpha = 1f
    }
    currentYearMonth.month == monthAtIndex -&gt; {
        month.isSelected = true
        month.setTextAppearance(ctx, R.style.TextAppearance_MaterialComponents_Body2)
        month.setTextColor(primaryColor)
        month.alpha = 1f
    }
    selectedMonth == monthAtIndex -&gt; {
        month.isSelected = false
        month.setTextAppearance(ctx, R.style.TextAppearance_MaterialComponents_Subtitle2)
        month.setTextColor(primaryColor)
        month.alpha = 1f
    }
    else -&gt; {
        month.isSelected = false
        month.setTextAppearance(ctx, R.style.TextAppearance_MaterialComponents_Body2)
        month.setTextColor(textColor)
        month.alpha = 1f
    }
}

// 선택된 item 변경
private fun updateSelectedMonth(month: Month) {
    selectedMonth = month
    notifyDataSetChanged()
}
...</code></pre>
<p>해당 Adapter를 확인해보시면 <code>private fun updateSelectedMonth(month: Month)</code> 인데 private함수로 MonthAdapter.kt 파일 안에서만 사용할 수 있도록 구현되어 있습니다.</p>
<p>여기서 item의 선택된 변수가 Adapter의 click event에만 적용되어 있어 클릭을 하지 않으면 현재날짜와 item의 날짜가 다르게 표현되었습니다.</p>
<ul>
<li><strong>원인 수정</strong></li>
</ul>
<p>Adapter의 <code>updateSelectedMonth(month: Month)</code> 함수를 <code>CalendarView.monthScrollListener(month: CalendarMonth)</code> 에 구현해 주면 간단하게 Selected item을 구현할 수 있습니다.</p>
<p>구현 코드는 아래와 같습니다.</p>
<pre><code class="language-kotlin">// CalendarSheet.kt
package com.maxkeppeler.sheets.calendar

...

private fun setupMonthScrollListener() {

    binding.calendarView.monthScrollListener = { month -&gt;
        val day = month.weekDays.first().first { it.owner == DayOwner.THIS_MONTH }
        selectedViewDate = day.date
        monthAdapter.updateCurrentYearMonth(day.date.yearMonth)

                // monthAdapter, yearAdapter의 updateSelected 함수 추가
        monthAdapter.updateSelectedMonth(day.date.yearMonth.month)
        yearAdapter.updateSelectedYear(Year.of(day.date.yearMonth.year))

        updateSpinnerValues()
    }
}
...</code></pre>
<p>Adapter 외 다른 클래스에서 사용하기 위해서는 <code>updateSelected()</code> 를 공개적으로 사용할 수 있게 public으로 변경해 준다.</p>
<pre><code class="language-kotlin">// MonthAdapter.kt
package com.maxkeppeler.sheets.calendar

...
// private --&gt; public
fun updateSelectedMonth(month: Month) {
    selectedMonth = month
    notifyDataSetChanged()
}
...</code></pre>
<ul>
<li><strong>결과</strong></li>
</ul>
<p><img src="https://velog.velcdn.com/images/aios_/post/674bb530-9dcd-4c99-98f0-1cc0b5a8319a/image.gif" alt=""></p>
<p><img src="https://velog.velcdn.com/images/aios_/post/a4f2aa92-abc7-405e-994e-a0f980caf929/image.gif" alt=""></p>
<hr>
<h3 id="3-pull-request-보내기">3. Pull Request 보내기</h3>
<aside>
✅ 수정된 내용을 푸쉬하고 “Pull Request” 탭에 수정한 이슈와 함께 등록한다.

</aside>

<p><img src="https://velog.velcdn.com/images/aios_/post/e3c91ece-0d89-4a10-a871-e732830fc131/image.png" alt=""></p>
<p>Pull Request를 확인 후 main으로 merge가 되었다.</p>
<p>WoW !!</p>
<hr>
<h3 id="느낀점">느낀점</h3>
<p><img src="https://velog.velcdn.com/images/aios_/post/9f82c524-1c7e-40b3-95e0-f5bd9d7049f5/image.png" alt=""></p>
<p>간단하지만 처음으로 오픈소스를 수정하여 Contributor이 되었다.
정말 신기하고 해당 수정버전이 merge되고 실제 프로덕션으로 배포가 되어 다른 사용자들이
사용한다고 생각하니까 뿌듯하다!!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[디자인 패턴] 추상 팩토리 메소드 패턴]]></title>
            <link>https://velog.io/@aios_/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-%EC%B6%94%EC%83%81-%ED%8C%A9%ED%86%A0%EB%A6%AC-%EB%A9%94%EC%86%8C%EB%93%9C-%ED%8C%A8%ED%84%B4</link>
            <guid>https://velog.io/@aios_/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-%EC%B6%94%EC%83%81-%ED%8C%A9%ED%86%A0%EB%A6%AC-%EB%A9%94%EC%86%8C%EB%93%9C-%ED%8C%A8%ED%84%B4</guid>
            <pubDate>Thu, 19 May 2022 15:32:08 GMT</pubDate>
            <description><![CDATA[<h2 id="정의">정의</h2>
<blockquote>
<p><strong>추상 팩토리 패턴(Abstract Facgtory Pattern)</strong>은 구상 클래스에 의존하지 않고 서로 연관되거나 의존적인 객채로 이루어진 제품군을 생산하는 인터페이스 제공
구상 클래스는 서브 클래스에서 만듬</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/aios_/post/63e1335f-45a6-40e4-9bd7-458ea8be00cd/image.png" alt=""></p>
<p>📌  팩토리 쪽에만 집중하면 팩토리 메소드 패턴과 유사하지만, 클라이언트에서 추상 팩토리를 어떻게 사용하는지에 대한 관점으로 접근해야 목적과 처리점을 이해하기 쉽다.</p>
<hr>
<h2 id="구현할-추상-팩토리-메소드-클래스">구현할 추상 팩토리 메소드 클래스</h2>
<p><img src="https://velog.velcdn.com/images/aios_/post/8056ee11-8755-4286-ae02-5b723e172b58/image.png" alt=""></p>
<hr>
<h2 id="product-class">Product Class</h2>
<pre><code class="language-kotlin">abstract class PizzaAF {

    var name: String = &quot;&quot;

    var dough: Dough? = null
    var sauce: Sauce? = null
    var cheese: Cheese? = null
    var clams: Clams? = null
    var veggie: Veggie? = null
    var tomato: Tomato? = null

    abstract fun prepare()

    fun bake() {
        println(&quot;25분 동안 피자를 굽습니다.&quot;)
    }

    fun cut() {
        println(&quot;피자를 8등분으로 자릅니다.&quot;)
    }

    fun box() {
        println(&quot;피자 박스에 피자를 담고 있습니다.&quot;)
    }

        override fun toString(): String {
            ... 생략
        }
}</code></pre>
<p>일반 팩토리 메소드와 생성자의 구성은 비슷합니다.</p>
<p>달라진 부분이라면 dough, sauce, cheese, clams, veggie, tomato 변수가 각각의 <code>AbstractProduct</code>로 변경되었다는 점 입니다.</p>
<p>해당 객체는 아래에서 자세히 구현하도록 하겠습니다.</p>
<hr>
<h2 id="abstractfactory-만들기">AbstractFactory 만들기</h2>
<pre><code class="language-kotlin">interface PizzaIngredientFactory {
    fun createDough(): Dough
    fun createSauce(): Sauce
    fun createCheese(): Cheese
    fun createVeggie(): Veggie
    fun createClams(): Clams
    fun createTomato(): Tomato
}</code></pre>
<p>추상 팩토리 클래스 또는 인터페이스를 통해 <code>Dough, Sauce, Cheese, Veggie, Clams, Tomato</code> 객체에 대한 서브 클래스를 리턴해 줍니다.</p>
<p>이는 객체지향(OOP)의 다형성을 아주 잘 활용한 방식이라 볼 수 있습니다.</p>
<p>자세한 구현은 <code>ConcreteFactory</code> 를 통해 구현해보도록 하겠습니다.</p>
<hr>
<h2 id="concretefactory-만들기">ConcreteFactory 만들기</h2>
<pre><code class="language-kotlin">class NYPizzaIngredientFactory : PizzaIngredientFactory {
    override fun createDough(): Dough = NYDough()

    override fun createSauce(): Sauce = NYSauce()

    override fun createCheese(): Cheese = NYCheese()

    override fun createVeggie(): Veggie = NYVeggie()

    override fun createClams(): Clams = NYClams()

    override fun createTomato(): Tomato = NYTomato()
}</code></pre>
<p>NYPizzaIngredientFactory(ConcreteFactory) 클래스를 만들고 PizzaIngredientFactory(AbstractFactory) 인터페이스를 상속 받습니다.</p>
<p>PizzaIngredientFactory의 함수를 override 하게 되고 여기서 NYPizza 스타일에 맞는 서브 Product 클래스를 넣어 줍니다.</p>
<p>PizzaAF 에 있는 Dough, Sauce, Cheese, Veggie, Clams, Tomato 객체를 Factory를 통해 만들었습니다.</p>
<hr>
<h2 id="abstractproduct-만들기">AbstractProduct 만들기</h2>
<pre><code class="language-kotlin">interface Cheese {
    override fun toString(): String
}

interface Clams {
    override fun toString(): String
}

interface Dough {
    override fun toString(): String
}

interface Sauce {
    override fun toString(): String
}

interface Tomato {
    override fun toString(): String
}

interface Veggie {
    override fun toString(): String
}</code></pre>
<p>간단하게 <code>AbstractProduct</code> 클래스들을 만들어 보았습니다.</p>
<p>이제 <code>AbstractProduct</code>를 상속 받아 해당 객체를 구현해줄 서브 클래스들을 만들어 주어야 합니다.</p>
<hr>
<h2 id="product-sub-class-만들기">Product Sub Class 만들기</h2>
<pre><code class="language-kotlin">class NYCheese : Cheese {
    override fun toString(): String {
        return &quot;## 뉴욕 치즈 ##&quot;
    }
}

class ChicagoClams : Clams {
    override fun toString(): String {
        return &quot;## 시카고 조개 ##&quot;
    }
}

... 등등</code></pre>
<p>서브 클래스까지 모두 만들었습니다.</p>
<p>이제 Client를 만들어야 합니다.</p>
<p>제가 구현한 프로젝트에서 Client는 <code>PizzaStore</code> 클래스를 상속 받고 있는 클래스들 입니다.</p>
<p>해당 클래스의 상세 구현으로 넘어가 보겠습니다.</p>
<hr>
<h2 id="client-class-만들기">Client Class 만들기</h2>
<pre><code class="language-kotlin">abstract class PizzaStoreAF {
        // 팩토리 메소드와 같은 함수 입니다.
    abstract fun createPizza(item: PizzaTypeAF): PizzaAF

    fun orderPizza(type: PizzaTypeAF): PizzaAF {
        val pizza: PizzaAF = createPizza(type)
        println(&quot;--- Making a ${pizza.name} ---&quot;)
        return pizza.apply {
            prepare()
            bake()
            cut()
            box()
        }
    }
}</code></pre>
<pre><code class="language-kotlin">class NYPizzaStore : PizzaStoreAF() {

    override fun createPizza(item: PizzaTypeAF): PizzaAF {
        val ingredientFactory = NYPizzaIngredientFactory()

        return when(item) {
            PizzaTypeAF.CHEESE -&gt; {
                CheesePizza(ingredientFactory).apply {
                    name = &quot;뉴욕 치즈 피자&quot;
                }
            }
            PizzaTypeAF.CLAM -&gt; {
                ClamsPizza(ingredientFactory).apply {
                    name = &quot;뉴욕 조개 피자&quot;
                }
            }
            PizzaTypeAF.TOMATO -&gt; {
                TomatoPizza(ingredientFactory).apply {
                    name = &quot;뉴욕 토마토 피자&quot;
                }
            }
            PizzaTypeAF.VEGGIE -&gt; {
                VeggiePizza(ingredientFactory).apply {
                    name = &quot;뉴욕 야채 피자&quot;
                }
            }
        }
    }
}

class ChicagoPizzaStore : PizzaStoreAF() {
        override fun createPizza(item: PizzaTypeAF): PizzaAF {
                ...
        }
}</code></pre>
<p>Client를 자세히 보면 제일 처음 다이어그램에서 처럼 <code>AbstractFacotry</code>와 <code>AbstractProduct</code> 를 가지고 각각의 Pizza를 만들어 주는 것을 볼 수 있습니다.</p>
<p>Client 클래스인 <code>PizzaStore</code>도 모두 만들었습니다.</p>
<p>직접 사용할 수 있도록 코드를 작성해 봅시다.</p>
<hr>
<h2 id="결과">결과</h2>
<pre><code class="language-kotlin">fun main() {
    val nyStore = NYPizzaStore()
    val chicagoStore = ChicagoPizzaStore()

    var pizzaAF = nyStore.orderPizza(PizzaTypeAF.CHEESE)
    println(&quot;A 주문 피자: $pizzaAF&quot;)

    pizzaAF = chicagoStore.orderPizza(PizzaTypeAF.TOMATO)
    println(&quot;B 주문 피자: $pizzaAF&quot;)

    pizzaAF = chicagoStore.orderPizza(PizzaTypeAF.VEGGIE)
    println(&quot;C 주문 피자: $pizzaAF&quot;)

    pizzaAF = nyStore.orderPizza(PizzaTypeAF.CLAM)
    println(&quot;D 주문 피자: $pizzaAF&quot;)
}</code></pre>
<center>
<img src="https://velog.velcdn.com/images/aios_/post/b3f10551-d85b-4771-80de-06bfd78fdc4f/image.png" width="300" />
<img src="https://velog.velcdn.com/images/aios_/post/91ba6403-6d24-468c-b8c3-d08ba145c771/image.png" width="300" />
<img src="https://velog.velcdn.com/images/aios_/post/655d8b26-9686-4cf1-a9d8-78cabcf80c00/image.png" width="300" />
<img src="https://velog.velcdn.com/images/aios_/post/1f7a5dc8-d4d9-48eb-a243-33f62b78384c/image.png" width="300" />
</center>



<p>내가 원하는 Client 에서 원하는 Type의 피자를 주문하면 뉴욕 or 시카고 스타일의 피자를 만들어 줍니다.</p>
<p>사용하는 부분(직접 구현하는 부분)에서 if-else 또는 when 문을 사용하지 않고 쉽게 원하는 피자를 생성이 가능하게 되었습니다.</p>
<p>추가로 다른 종류의 피자를 추가한다면 추상 클래스 or 인터페이스를 추가하면 간단하게 확장이 가능하며, 기존 코드는 변경되지 않습니다.(OCP)</p>
<hr>
<h3 id="특징">특징</h3>
<blockquote>
<p>구현(Implement)보다 인터페이스(Interface)를 위한 코드 접근법을 제공.
sub class를 확장이 쉽다.
클라이언트 코드에서 구체적인 클래스의 의존성을 줄여주어 느슨한 결함이 가능함.</p>
</blockquote>
<h3 id="팩토리-매소드--추상-팩토리-매소드-패턴-차이점">팩토리 매소드 &amp; 추상 팩토리 매소드 패턴 차이점</h3>
<ul>
<li>관점이 다르다.<ul>
<li>팩토리 패턴은 “팩토리를 구현하는 방법 (inheritance)”에 초점을 둔다.</li>
<li>추상 팩토리 패턴은 “팩토리를 사용하는 방법 (composition)”에 초점을 둔다.</li>
</ul>
</li>
<li>목정이 조금 다르다.<ul>
<li>팩토리 패턴은 구체적인 객체 생성 과정을 하위 또는 구체적은 클래스로 옮기는 것이 목적</li>
<li>추장 팩토리 패턴은 관련있는 여러 객체를 구체적인 클래스에 의존하지 않고 만들 수 있게 해주는 것이 목적</li>
</ul>
</li>
</ul>
<hr>
<h3 id="참고">참고</h3>
<ul>
<li>헤드퍼스트 개정판 [<a href="http://hanbit.co.kr/store/books/look.php?p_code=B6113501223">hanbit.co.kr/store/books/look.php?p_code=B6113501223</a>]</li>
<li>인프런 - 코딩으로 학습하는 GoF 디자인 패턴 [<a href="https://www.inflearn.com/course/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4">https://www.inflearn.com/course/디자인-패턴</a>]</li>
<li>위키백과 - 추상 팩토리 메서드 패턴 [<a href="https://ko.wikipedia.org/wiki/%EC%B6%94%EC%83%81_%ED%8C%A9%ED%86%A0%EB%A6%AC_%ED%8C%A8%ED%84%B4">https://ko.wikipedia.org/wiki/추상_팩토리_메서드_패턴</a>]</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[디자인 패턴] 팩토리 메소드]]></title>
            <link>https://velog.io/@aios_/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-%ED%8C%A9%ED%86%A0%EB%A6%AC-%EB%A9%94%EC%86%8C%EB%93%9C</link>
            <guid>https://velog.io/@aios_/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-%ED%8C%A9%ED%86%A0%EB%A6%AC-%EB%A9%94%EC%86%8C%EB%93%9C</guid>
            <pubDate>Tue, 10 May 2022 13:16:00 GMT</pubDate>
            <description><![CDATA[<h2 id="정의">정의</h2>
<blockquote>
<p>객체지향 다형성의 원리를 기반으로 만들어짐
다양한 구현체(Product)가 있고, 특정한 구현체를 만들 수 있는 팩토리(Creator)를 제공</p>
<p><strong>확장에는 열려있고 변경에는 닫혀 있는 구조</strong></p>
</blockquote>
<p>부모(상위) 클래스에 알려지지 않은 구체 클래스를 생성하는 패턴이며, 자식(하위) 클래스가 어떤 객체를 생성할지 결정하도록 하는 패턴이다.</p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/aios_/post/7325f282-dd1a-469c-a243-7e9c7e3cf00b/image.png" alt=""></p>
<ul>
<li><p>Product
  공통으로 생성될 객체의 인터페이스</p>
</li>
<li><p>ConcreateProduct
  구체적으로 객체가 생성되는 클래스 </p>
</li>
<li><p>Creator
  팩토리 매서드를 가지고 있는 클래스</p>
</li>
<li><p>ConcreateCreator
  팩토리 매서드를 구현하는 클래스, ConcreateProduct 객체 생성</p>
</li>
</ul>
<hr>
<h2 id="구현할-팩토리-매소드-클래스">구현할 팩토리 매소드 클래스</h2>
<p><img src="https://velog.velcdn.com/images/aios_/post/e9ab0fb8-a59f-44fd-94b9-950a93fea131/image.png" alt=""></p>
<hr>
<h2 id="생성자product-만들기">생성자(Product) 만들기</h2>
<ul>
<li>생성자 Pizza 클래스를 만들어 봅니다.</li>
</ul>
<pre><code class="language-kotlin">abstract class Pizza {

    var name: String = &quot;&quot;
    var dough: String = &quot;&quot;
    var sauce: String = &quot;&quot;

    override fun toString(): String {
        return &quot;&quot;&quot;
            { name : $name, dough : $dough, sauce : $sauce }
        &quot;&quot;&quot;.trimIndent()
    }

    fun prepare() {
        println(&quot;Prepare $name&quot;)
        println(&quot;도우 셋팅 중...&quot;)
        println(&quot;소스 추가 중...&quot;)
        println(&quot;토핑 추가 중: &quot;)
    }

    fun bake() {
        println(&quot;25분 동안 피자를 굽습니다.&quot;)
    }

    fun cut() {
        println(&quot;피자를 8등분으로 자릅니다.&quot;)
    }

    fun box() {
        println(&quot;피자 박스에 피자를 담고 있습니다.&quot;)
    }
}</code></pre>
<p>피자에는 총 3가지의 변수를 가집니다.</p>
<ul>
<li>name: 피자의 이름</li>
<li>dough: 도우 종류</li>
<li>sauce: 소스의 종류</li>
</ul>
<p>피자를 준비에 필요한 행동(매소드)를 정의합니다.</p>
<ul>
<li><code>fun prepare()</code>: 피자를 만들기 위한 준비</li>
<li><code>fun bake()</code>: 피자 굽기</li>
<li><code>fun cut()</code>: 피자 자르기</li>
<li><code>fun box()</code>: 피자를 박스에 담기</li>
</ul>
<p>여기서 피자는 어떤 피자를 만들게 될지 모르고 주문이 들어왔을 때,</p>
<p>주문에 맞는 피자를 만들게 됩니다.</p>
<hr>
<h2 id="concreateproduct-클래스-만들기">ConcreateProduct 클래스 만들기</h2>
<ul>
<li>뉴욕 스타일 피자와 시카고 스타일 피자를 만들어 보겠습니다.</li>
</ul>
<pre><code class="language-kotlin">class NYStyleCheesePizza : Pizza() {
    init {
        name = &quot;NY 치즈 피자&quot;
        dough = &quot;치즈 도우&quot;
        sauce = &quot;치즈 소스&quot;
    }
}</code></pre>
<pre><code class="language-kotlin">class ChicagoStyleCheesePizza: Pizza() {
    init {
        name = &quot;시카고 치즈 피자&quot;
        dough = &quot;두꺼운 도우&quot;
        sauce = &quot;체다 치즈 소스&quot;
    }
}</code></pre>
<p>피자의 종류를 알 수 있는 클래스를 만들었습니다.</p>
<p><code>PizzaType.kt</code> 에 맞는 Pizza ConcreateProduct 클래스를 만들어 주면 됩니다.</p>
<p>클래스가 많아져도 괜찮습니다.</p>
<p>이제 피자의 종류를 알고 있으니 피자가게에가서 주문하면 됩니다.</p>
<p>주문은 ConcreateCreator 를 통해서 Pizza를 전달하고 Creator에서 만들어 줍니다. </p>
<hr>
<h2 id="pizzastorecreator-만들기">PizzaStore(Creator) 만들기</h2>
<ul>
<li>피자를 만들 수 있는 피자상점을 만들어 봅니다.</li>
</ul>
<pre><code class="language-kotlin">abstract class PizzaStore {

    abstract fun createPizza(item: PizzaType): Pizza

    fun orderPizza(type: PizzaType): Pizza {
        val pizza: Pizza = createPizza(type)
        println(&quot;--- Making a ${pizza.name} ---&quot;)
        return pizza.apply {
            prepare()
            bake()
            cut()
            box()
        }
    }
}</code></pre>
<p>피자의 종류에 따른 Pizza 를 만들어 주문을 넣을 수 있도록 합니다.</p>
<pre><code class="language-kotlin">enum class PizzaType {
    CHEESE,
    TOMATO,
    CLAM,
    VEGGIE
}</code></pre>
<p>Pizza 객체에 따른 피자를 만들기 위해 <code>abstract fun createPizza(item: PizzaType): Pizza</code> 구현 할 수 있는 ConcreateCreator 클래스를 만들어 주어야 합니다.</p>
<hr>
<h2 id="concreatecreator뉴욕-시카고-피자-상점-만들기">ConcreateCreator(뉴욕, 시카고 피자 상점) 만들기</h2>
<ul>
<li>저희는 뉴욕 피자, 시카고 피자 총 2가지의 종류의 피자를 주문할 수 있습니다.</li>
<li>스타일에 맞는 피자 상점을 만들어 <code>PizzaType</code> 으로 어떤 종류의 피자인지 알게됩니다.</li>
</ul>
<pre><code class="language-kotlin">class NYPizzaStore: PizzaStore() {
    override fun createPizza(item: PizzaType): Pizza {
        return when(item) {
            PizzaType.CHEESE -&gt; NYStyleCheesePizza()
            PizzaType.TOMATO -&gt; NYStyleTomatoPizza()
            PizzaType.CLAM -&gt; NYStyleClamPizza()
            PizzaType.VEGGIE -&gt; NYStyleVeggiePizza()
            else -&gt; throw IllegalArgumentException()
        }
    }
}</code></pre>
<pre><code class="language-kotlin">class ChicagoPizzaStore : PizzaStore() {
    override fun createPizza(item: PizzaType): Pizza {
        return when(item) {
            PizzaType.CHEESE -&gt; ChicagoStyleCheesePizza()
            PizzaType.CLAM -&gt; ChicagoStyleClamPizza()
            PizzaType.TOMATO -&gt; ChicagoStyleTomatoPizza()
            PizzaType.VEGGIE -&gt; ChicagoStyleVeggiePizza()
            else -&gt; throw IllegalArgumentException()
        }
    }
}</code></pre>
<p>뉴욕스타일 피자 상점과 시카고 스타일 피자 상점을 만들어 주었습니다.</p>
<p><code>override fun createPizza(item: PizzaType): Pizza</code> 구현하여 <code>Pizza</code> 객체를 <code>PizzaStore(Creator)</code> 클래스로 주게 됩니다.</p>
<hr>
<h2 id="결과">결과</h2>
<pre><code class="language-kotlin">fun main() {
    val nyStore = NYPizzaStore()
    val chicagoStore = ChicagoPizzaStore()

    val pizza: Pizza = nyStore.orderPizza(PizzaType.CHEESE)
    println(&quot;철수 주문 : ${pizza.name}, \n$pizza&quot;)

    print(&quot;\n\n&quot;)

    val chicagoTomatoPizza: Pizza = chicagoStore.orderPizza(PizzaType.TOMATO)
    println(&quot;영희 주문 : ${chicagoTomatoPizza.name}, \n$chicagoTomatoPizza&quot;)
}</code></pre>
<p><img src="https://velog.velcdn.com/images/aios_/post/233b48d0-37b0-4544-8b7b-b814f80e7b44/image.png" alt=""></p>
<p><code>PizzaType</code>에 맞는 피자를 <code>PizzaStore(Creator)</code> 클래스에서 만들어 주는 모습을 볼 수 있습니다.</p>
<p>간단하게 팩토리 메서드를 활용한 피자만들기를 구현해 보았습니다.</p>
<hr>
<ul>
<li>Product Diagram</li>
</ul>
<p><img src="https://velog.velcdn.com/images/aios_/post/e8128653-25a9-44ee-b674-c0476d77d5a0/image.png" alt=""></p>
<ul>
<li>Creator Diagarm</li>
</ul>
<p><img src="https://velog.velcdn.com/images/aios_/post/608fe907-f63f-4536-a2b3-f92b4d5af001/image.png" alt=""></p>
<h3 id="장점">장점</h3>
<blockquote>
<p>기존 코드를 수정하지 않고 새로운 인스턴스를 확장 할 수 있다.
Product - Creator 느슨한 결합으로 만들 수 있다.
코드가 간결해 진다.</p>
</blockquote>
<h3 id="단점">단점</h3>
<blockquote>
<p>클래스가 많아 진다.
제품이 변경, 추가 될 때 마다 클래스를 생성해야한다.</p>
</blockquote>
<h3 id="사용-이유">사용 이유</h3>
<ul>
<li>생성할 객체 타입을 예측할 수 없을 때</li>
<li>생성할 객체를 기술하는 책임을 서브클래스에게 정의하고자 할 때</li>
<li>객체 생성의 책임을 서브클래스에 위임시키고 서브클래스에 대한 정보를 은닉하고자 할 때</li>
</ul>
<hr>
<h2 id="참고">참고</h2>
<ul>
<li>헤드퍼스트 개정판 [<a href="http://hanbit.co.kr/store/books/look.php?p_code=B6113501223">hanbit.co.kr/store/books/look.php?p_code=B6113501223</a>]</li>
<li>인프런 - 코딩으로 학습하는 GoF 디자인 패턴 [<a href="https://www.inflearn.com/course/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4">https://www.inflearn.com/course/디자인-패턴</a>]</li>
<li>위키백과 - 팩토리 메서드 패턴 [<a href="https://ko.wikipedia.org/wiki/%ED%8C%A9%ED%86%A0%EB%A6%AC_%EB%A9%94%EC%84%9C%EB%93%9C_%ED%8C%A8%ED%84%B4">https://ko.wikipedia.org/wiki/팩토리_메서드_패턴</a>]</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Android] CameraX]]></title>
            <link>https://velog.io/@aios_/Android-CameraX</link>
            <guid>https://velog.io/@aios_/Android-CameraX</guid>
            <pubDate>Sat, 01 May 2021 06:40:57 GMT</pubDate>
            <description><![CDATA[<h1 id="안드로이드-camerax-api">안드로이드 CameraX API</h1>
<blockquote>
<p>안드로이드 5.0 이상에서 사용가능
camera2 기능 활용 + 생명주기 인식
<a href="https://codelabs.developers.google.com/codelabs/camerax-getting-started?hl=ko#0">구글 CameraX 예제</a></p>
</blockquote>
<h2 id="발생한-이슈">발생한 이슈</h2>
<ul>
<li>프리뷰가 실행되는 시점에서 권한 체크를 하면 프리뷰 화면이 나오지 않음</li>
<li>안드로이드 Q(10) 이상 파일 다운로드 위치 지정하기 쉽지 않음</li>
<li>화면 이동을 viewmodel.observe 사용시 break가 필요</li>
</ul>
<hr>
<h3 id="1-프리뷰-화면-나오지-않는-버그">1. 프리뷰 화면 나오지 않는 버그</h3>
<p>아직까지 어떠한 방법을 찾지 못했음.
추후에 찾아서 포스팅 예정</p>
<h3 id="2-안드로이드-q10-파일-다운로드-위치">2. 안드로이드 Q(10) 파일 다운로드 위치</h3>
<p>개인정보를 중요시 하면서 안드로이드에 개인정보에 많은 부분이 제약이 걸린 버전.
기기의 고유 값을 가져올 수 없음.
앱 외부 공용 파일에 저장하지 않기를 권고</p>
<p align="center" style="color:gray">
  <img style="margin:30px 0 10px 0" src="https://images.velog.io/images/aios_/post/c6785c69-340c-4b68-a28c-ea9eef505bbd/image.png" >
  <a href="https://developer.android.com/about/versions/10/privacy/changes?hl=ko">Google 공식 문서</a>
  </p>


]]></description>
        </item>
        <item>
            <title><![CDATA[2021. 04. 24 (토) - 개발 공부]]></title>
            <link>https://velog.io/@aios_/2021.-04.-24-%ED%86%A0-%EA%B0%9C%EB%B0%9C-%EA%B3%B5%EB%B6%80</link>
            <guid>https://velog.io/@aios_/2021.-04.-24-%ED%86%A0-%EA%B0%9C%EB%B0%9C-%EA%B3%B5%EB%B6%80</guid>
            <pubDate>Sat, 24 Apr 2021 15:56:57 GMT</pubDate>
            <description><![CDATA[<h1 id="개발-공부-일지">개발 공부 일지</h1>
<h2 id="custom-view-만들기">Custom View 만들기</h2>
<ul>
<li>@JvmOverLoads 어노테이션 (constructor 오버로딩)</li>
<li>attr format 종류 (reference(id), dimension(dp, px, sp...), string, int, boolean, eunm ...)</li>
<li>DataBinding</li>
</ul>
<hr>
<h3 id="1-jvmoverloads">1. @JvmOverLoads</h3>
<p> 커스텀 뷰를 생성할때, 생성자 오버로딩이 필수적이다.
 그 부분을 어노테이션이 대신해 준다.</p>
<p><strong>java</strong></p>
<pre><code class="language-java"> public class CustomView extends View {
     public CustomView(Context context) {
        super(context);
    }
    public CustomView(Context context, AttributeSet attr) {
        super(context, attr);
    }
    // ... etc
 }</code></pre>
<p> <strong>kotlin</strong></p>
<pre><code class="language-kotlin"> class CustomView : View {
     constructor(context: Context) : super(context)
        constructor(context: Context, attr: AttributeSet) : super(context, attr)
 }
 // ... etc</code></pre>
<p> constructor을 사용하여 자바와 비슷한 코드를 만들 수 있지만</p>
<p> <em>@JvmOverloads</em> 어노케이션을 사용하면 간결하게 생성자 오버로딩을 지원한다.</p>
<pre><code class="language-kotlin"> class CustomView @JvmOverloads constructor(
     context: Context,
     attr:AttributeSet? = null
 ) : View(context, attr) {
     // ... etc
}</code></pre>
<hr>
<h3 id="2-attr-format-종류">2. attr format 종류</h3>
<ul>
<li>referemce : id 값</li>
<li>dimension : dp, sp, px ...</li>
<li>enum : 열거형</li>
<li>fraction : ??</li>
<li>flag : ??</li>
<li>color : 색깔</li>
<li>integer, float, string, boolean ...</li>
</ul>
<hr>
<h3 id="3-databinding">3. DataBinding</h3>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Adnroid] FCM]]></title>
            <link>https://velog.io/@aios_/Adnroid-FCM-1</link>
            <guid>https://velog.io/@aios_/Adnroid-FCM-1</guid>
            <pubDate>Wed, 21 Apr 2021 13:30:15 GMT</pubDate>
            <description><![CDATA[<h1 id="firebase-cloud-messaging">Firebase Cloud Messaging</h1>
<blockquote>
<p>안드로이드 FCM 시작하기</p>
</blockquote>
<h3 id="순서">순서</h3>
<ol>
<li>안드로이드 프로젝트 콘솔에 등록</li>
<li>FCMService 만들기</li>
<li>Manifast에 인터넷 권한 주기</li>
<li>intent-filter MESSAGE_EVENT 추가</li>
<li>onNewToken, onMessageRecived 구현</li>
</ol>
<hr>
<h2 id="만들면서-발생한-이슈">만들면서 발생한 이슈</h2>
<h3 id="1-http---http-v1-변경">1. http -&gt; http v1 변경</h3>
<blockquote>
<p>서비스 키 --&gt; <strong>OAuth2.0</strong>
<strong>OAuth2.0</strong>을 활용하여 주기적으로 토큰 값을 갱신하여 주어야함
장점 : 주기적인 토큰 갱신으로 보안성 우수
단점 : 토큰을 계속적으로 요청해야 되기 때문에 구현 난이도 상승</p>
</blockquote>
<p align="center" style="color:gray">
  <img style="margin:30px 0 10px 0" src="https://images.velog.io/images/aios_/post/83c17371-18a8-4b81-ba2a-25b840803cc0/image.png" />
Postman - http v1

<p align="center" style="color:gray">
  <img style="margin:30px 0 10px 0" src="https://images.velog.io/images/aios_/post/3bfb5486-44d9-4a1a-bbb9-cb0a79798e3e/image.png" />
Postman - http

<h3 id="2-data--notification">2. Data &amp; Notification</h3>
<blockquote>
<p>FCM의 경우 2가지로 데이터를 표시할 수 있다.
  하나. Notification, 상단 알림창을 만들 수 있는 기능
  둘. Data, 제목, 내용 뿐만아니라 다양한 커스텀 메시지를 보낼 수 있음</p>
</blockquote>
<p align="center" style="color:gray">
  <img style="margin:30px 0 10px 0" src="https://images.velog.io/images/aios_/post/4b2136c7-fd40-46a4-bb87-93eea22838d2/image.png" >
  <a href="https://firebase.google.com/docs/cloud-messaging/android/receive?hl=ko">Firebase 공식 문서</a>
  </p>
<br />

<ul>
<li>모두 사용한 경우 onMessageReceived 함수 호출 불가</li>
<li>데이터를 인텐트로 받아야함</li>
<li>화면 깨우기 불가</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[첫글 시작 !!]]></title>
            <link>https://velog.io/@aios_/%EC%B2%AB%EA%B8%80-%EC%8B%9C%EC%9E%91</link>
            <guid>https://velog.io/@aios_/%EC%B2%AB%EA%B8%80-%EC%8B%9C%EC%9E%91</guid>
            <pubDate>Wed, 21 Apr 2021 12:11:02 GMT</pubDate>
            <description><![CDATA[<h2 id="2021-04-21">2021. 04. 21</h2>
<p>앞으로 공부하는 부분 열심히 올려보자</p>
]]></description>
        </item>
    </channel>
</rss>