<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>errored_pasta.log</title>
        <link>https://velog.io/</link>
        <description>Hola, Mundo</description>
        <lastBuildDate>Mon, 27 Feb 2023 08:39:19 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>errored_pasta.log</title>
            <url>https://images.velog.io/images/errored_pasta/profile/188328d3-01d9-48e5-9232-7925aaf75d03/social.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. errored_pasta.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/errored_pasta" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[DI] Dagger Hilt]]></title>
            <link>https://velog.io/@errored_pasta/DI-Dagger-Hilt</link>
            <guid>https://velog.io/@errored_pasta/DI-Dagger-Hilt</guid>
            <pubDate>Mon, 27 Feb 2023 08:39:19 GMT</pubDate>
            <description><![CDATA[<p>이전에 Dagger2를 이용하여 dependency injection을 하는 방법에 대해 알아보았습니다. Dagger2의 경우 Module과 Component를 정의하고 Component간 구조를 사용자가 일일이 정의를 해주고 마지막으로 Component를 생성하여 dependency들을 주입하였습니다.</p>
<p>Dagger Hilt는 Dagger2를 보다 간편하게 사용하기 위해 boilerplate 코드를 줄여주어 간단하게 dependency injection을 할 수 있습니다.
해당 글에서는 Plugin을 이용한 Dagger Hilt에 대해서 다루며 Plugin을 사용하지 않은 Dagger Hilt는 아래의 GitHub 링크를 참고해주시면 됩니다.</p>
<p><a href="https://github.com/ErroredPasta/Dependency-Injection/tree/dagger-hilt-without-plugin">Plugin 없는 Dagger Hilt</a></p>
<h2 id="component-구조와-scope">Component 구조와 Scope</h2>
<p>Dagger Hilt의 미리 정의된 Component와 Scope를 제공합니다.</p>
<img src="https://dagger.dev/hilt/component-hierarchy.svg" />

<p>위와 같이 Component는 Subcomponent 혹은 부모 Component를 가지고 필요시 부모 Component에서 제공하는 dependency를 가져옵니다.</p>
<p>해당 Component들은 사용자가 직접 생성해서 사용하는 것이 아니라 Dagger Hilt에서 자동으로 생성하여 <code>@Inject</code>로 dependency들을 주입하게 됩니다.</p>
<h2 id="module">Module</h2>
<p>Module은 Dagger2와 동일하나 <code>@InstallIn</code>을 이용하여 어떤 Component에 설치될지 지정해주어야 합니다.</p>
<pre><code class="language-kotlin">@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {
    @Provides
    @Singleton
    fun provideOkHttpClient(): OkHttpClient = OkHttpClient.Builder()
        .addInterceptor(HttpLoggingInterceptor().apply {
            level = HttpLoggingInterceptor.Level.BODY
        }).build()

    private const val BASE_URL = &quot;https://v6.exchangerate-api.com/&quot;

    @Provides
    @Singleton
    fun provideExchangeRateApi(
        okHttpClient: OkHttpClient
    ): ExchangeRateApi = Retrofit.Builder()
        .baseUrl(BASE_URL)
        .client(okHttpClient)
        .addConverterFactory(GsonConverterFactory.create())
        .build()
        .create(ExchangeRateApi::class.java)
}</code></pre>
<p>해당 코드는 Module을 <code>@InstallIn(SingletonComponent::class)</code>로 SingletonComponent에 설치하며 해당 Component에서 같은 객체를 사용할 dependency에 알맞는 Scope인 <code>@Singleton</code>을 적용했습니다.</p>
<h2 id="hilt-application과-android-entry-point">Hilt Application과 Android Entry Point</h2>
<p>Module들을 정의하고 알맞은 Component에 설치를 했으면 어디에서 코드 생성을 시작하고 dependency를 주입할지 지정해주어야 합니다.</p>
<h3 id="hilt-application">Hilt Application</h3>
<blockquote>
<p><code>@HiltAndroidApp</code> kicks off the code generation of the Hilt components and also generates a base class for your application that uses those generated components.</p>
</blockquote>
<pre><code class="language-kotlin">@HiltAndroidApp
class ConversionApplication : Application()</code></pre>
<p>Dagger Hilt를 사용할 때 Application을 상속받는 클래스에 <code>@HiltApplication</code> annotation을 적용시켜서 코드 생성을 시작할 수 있도록 해주어야 합니다.</p>
<h3 id="android-entry-point">Android Entry Point</h3>
<blockquote>
<p>Once you have enabled members injection in your Application, you can start enabling members injection in your other Android classes using the <code>@AndroidEntryPoint</code> annotation</p>
</blockquote>
<pre><code class="language-kotlin">@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
    ...
}</code></pre>
<p><code>@AndroidEntryPoint</code>는 Activity, Fragment 등과 같은 Android의 클래스에 적용이 되며 dependency를 주입할 수 있도록 해줍니다. 적용될 수 있는 클래스는 다음과 같습니다.</p>
<ul>
<li>Activity</li>
<li>Fragment</li>
<li>View</li>
<li>Service</li>
<li>BroadcastReceiver</li>
</ul>
<p>이 중에서 BroadcastReceiver는 위에서 봤듯이 Component가 없습니다. 그래서 SingletonComponent에서 dependency를 주입받습니다.</p>
<p>그리고 dependency가 주입되도록 하려면 자신을 포함하고 있는 클래스 또한 <code>@AndroidEntryPoint</code>가 적용되어 있어야 합니다. 즉, Fragment의 경우 자신을 포함하고 있는 Activity에도 <code>@AndroidEntryPoint</code>가 적용이 되어 있어야 dependency가 주입가능합니다.</p>
<h2 id="viewmodel">ViewModel</h2>
<blockquote>
<p>A Hilt View Model is a Jetpack ViewModel that is constructor injected by Hilt. To enable injection of a ViewModel by Hilt use the <code>@HiltViewModel</code> annotation</p>
</blockquote>
<p>Dagger Hilt의 장점중 하나는 ViewModel을 주입하기가 쉽습니다. <a href="https://velog.io/@errored_pasta/DI-Dagger2-Multibinding#multibinding%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%9C-viewmodel-factory">Dagger2를 사용할 때</a>는 아래와 같은 과정이 필요했습니다.</p>
<ol>
<li>Map multibinding에 사용할 <code>@MapKey</code>를 이용한 annotation 정의</li>
<li>Module에서 사용자가 정의한 ViewModel을 <code>ViewModel</code> type으로 bind 및 Map key 설정</li>
<li>ViewModel을 생성할 때 사용할 factory 클래스 정의</li>
<li>Map multibinding을 이용하여 ViewModel을 담고 있는 Map을 factory에 주입</li>
<li>ViewModel을 생성할 factory를 주입받아서 ViewModel 생성</li>
</ol>
<p>하지만 Dagger Hilt를 사용하면 단순히 정의한 ViewModel에 <code>@HiltViewModel</code>를 적용해주면 됩니다.</p>
<pre><code class="language-kotlin">@HiltViewModel
class MainViewModel @Inject constructor(
    private val repository: ConversionRepository
) : ViewModel() {
    ...
}</code></pre>
<p>이렇게 annotation을 적용했으면 <code>@AndroidEntryPoint</code>가 적용된 곳에서 ViewModel의 instance를 가져와서 사용할 수 있습니다.</p>
<pre><code class="language-kotlin">@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

    private val viewModel by viewModels&lt;MainViewModel&gt;()

    ...
}</code></pre>
<h2 id="reference">Reference</h2>
<p>[1] &quot;Hilt,&quot; Dagger, last modified n.d., accessed Feb 27, 2023, <a href="https://dagger.dev/hilt/">https://dagger.dev/hilt/</a>.</p>
<p>[2] &quot;Hilt Components,&quot; Dagger, last modified n.d., accessed Feb 27, 2023, <a href="https://dagger.dev/hilt/components">https://dagger.dev/hilt/components</a>.</p>
<p>[3] &quot;Hilt Application,&quot; Dagger, last modified n.d., accessed Feb 27, 2023, <a href="https://dagger.dev/hilt/application">https://dagger.dev/hilt/application</a>.</p>
<p>[4] &quot;Android Entry Points,&quot; Dagger, last modified n.d., accessed Feb 27, 2023, <a href="https://dagger.dev/hilt/android-entry-point">https://dagger.dev/hilt/android-entry-point</a>.</p>
<p>[5] &quot;View Models,&quot; Dagger, last modified n.d., accessed Feb 27, 2023, <a href="https://dagger.dev/hilt/view-model">https://dagger.dev/hilt/view-model</a>.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[DI] Dagger2 - Multibinding]]></title>
            <link>https://velog.io/@errored_pasta/DI-Dagger2-Multibinding</link>
            <guid>https://velog.io/@errored_pasta/DI-Dagger2-Multibinding</guid>
            <pubDate>Sun, 19 Jun 2022 18:09:56 GMT</pubDate>
            <description><![CDATA[<p>Dagger2가 제공하는 기능중에 Multibinding이 있습니다. Multibinding은 같은 type의 여러 dependency를 Map이나 Set을 이용하여 하나의 object로 제공할 수 있도록 해줍니다.</p>
<h2 id="multibinding을-이용한-viewmodel-factory">Multibinding을 이용한 ViewModel factory</h2>
<p>Dagger2를 이용할 때 multibinding을 사용해야하는 대표적인 경우로 ViewModel을 생성시 필요한 <code>ViewModelProvider.Factory</code>를 상속하여 구현할 때 Map multibinding이 사용됩니다.</p>
<pre><code class="language-kotlin">class MainViewModel @Inject constructor(
    private val repository: ConversionRepository
) : ViewModel() {
    ...
}

class SecondViewModel @Inject constructor(
    private val secondDependency: SecondDependency
) : ViewModel() {
    ...
}</code></pre>
<p>우선 map multibinding을 사용하기 위해서는 <code>@MapKey</code>를 이용하여 Map에 dependency를 넣을 때 지정할 key annotation class를 정의해야 합니다.</p>
<pre><code class="language-kotlin">@MapKey
annotation class ViewModelKey(val key: KClass&lt;out ViewModel&gt;)</code></pre>
<p>ViewModelProvider.Factory에서 ViewModel을 생성 시 생성할 클래스의 type을 넘겨주므로 <code>ViewModelKey</code>도 클래스의 type을 받도록 하였습니다.</p>
<p>이제 ViewModel들을 <code>ViewModel</code> type으로 bind하고 <code>@IntoMap</code>을 이용하여 Map에 넣어야 합니다.</p>
<pre><code class="language-kotlin">@Module
interface ViewModelModule {
    // @IntoMap을 이용하여 Map에 MainViewModel을 ViewModel type으로 넣도록
    // @ViewModelKey를 이용하여 Map에 넣을 때 사용할 key값을 제공
    @Binds
     @IntoMap
     @ViewModelKey(MainViewModel::class)
      fun bindMainViewModel(viewModel: MainViewModel): ViewModel

    @Binds
     @IntoMap
     @ViewModelKey(SecondViewModel::class)
      fun bindSecondViewModel(viewModel: SecondViewModel): ViewModel
}</code></pre>
<p>이렇게 했으면 이제 Map을 이용하여 ViewModel들을 주입할 수 있게 됩니다.
마지막으로 <code>ViewModelProvider.Factory</code>를 상속받는 factory를 구현해주고 <code>ViewModelProvider.Factory</code>로 bind해주면 됩니다.</p>
<pre><code class="language-kotlin">class ViewModelFactory @Inject constructor(
    private val providerMap: @JvmSuppressWildcards Map&lt;Class&lt;out ViewModel&gt;, Provider&lt;ViewModel&gt;&gt;
) : ViewModelProvider.Factory {
    @Suppress(&quot;UNCHECKED_CAST&quot;)
    override fun &lt;T : ViewModel&gt; create(modelClass: Class&lt;T&gt;): T {
        return providerMap[modelClass]!!.get() as T
    }
}

@Module
interface ViewModelModule {
    ...

    // ViewModelFactory를 ViewModelProvider.Factory로 bind하여
    // ViewModel을 생성할 때 사용할 수 있도록 함
    @Binds
    fun bindViewModelFactory(factory: ViewModelFactory): ViewModelProvider.Factory
}</code></pre>
<p>Set을 이용한 multibinding은 Map multibinding과 매우 유사합니다. 차이점은 Map multibinding은 Map에 넣을 때 사용할 key를 <code>@MapKey</code>를 이용하여 정의하고 <code>@IntoMap</code>을 할 때 정의한 Map key를 사용해야 했지만 Set에는 key가 없습니다.</p>
<h2 id="reference">Reference</h2>
<p>[1] &quot;Multibindings,&quot; Dagger, last modified n.d., accessed Jun 20, 2022, <a href="https://dagger.dev/dev-guide/multibindings">https://dagger.dev/dev-guide/multibindings</a>.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[DI] Dagger2 - Scope, Qualifier, Provider, Lazy]]></title>
            <link>https://velog.io/@errored_pasta/DI-Dagger2-Scope-Qualifier-Provider-Lazy</link>
            <guid>https://velog.io/@errored_pasta/DI-Dagger2-Scope-Qualifier-Provider-Lazy</guid>
            <pubDate>Sun, 19 Jun 2022 14:09:01 GMT</pubDate>
            <description><![CDATA[<h2 id="scope"><a href="https://docs.oracle.com/javaee/6/api/javax/inject/Scope.html">Scope</a></h2>
<blockquote>
<p>Identifies scope annotations. A scope annotation applies to a class containing an injectable constructor and governs how the injector reuses instances of the type.</p>
</blockquote>
<p> Dependency injection을 할 때, 필요할 때 마다 생성하는 것이 아니라 동일한 dependency를 주입해주어야 할 때가 있습니다. 그런 경우에 <code>Scope</code>를 정의하여 동일한 Component객체내에서 같은 객체를 재사용 하도록 할 수 있습니다.</p>
<p><code>@Scope</code> annotation은 직접 Component나 dependency에 적용하는 것이 아니라 사용자가 원하는 Scope annotation 클래스를 정의할 때 사용하는 것입니다.</p>
<pre><code class="language-kotlin">// ActivityComponent에서 사용할 Scope annotation class
@Scope
@Retention(AnnotationRetention.RUNTIME)
annotation class ActivityScope</code></pre>
<p>위와 같이 Scope를 정의하였으면 적용해야할 dependency와 Component에 적용을 해주어야 합니다.</p>
<pre><code class="language-kotlin">@Module
object ActivityModule {

    // Dependency를 ActivityScope가 적용된 Component에서
    // 단일 객체를 재사용하도록 지정
    @Provides
    @ActivityScope
    fun provideMyDependency(): MyDependency = MyDependency()
}</code></pre>
<pre><code class="language-kotlin">// Component에서도 ActivityScope를 적용
@Subcomponent(modules = [ActivityModule::class])
@ActivityScope
interface MainActivityComponent {

    @Subcomponent.Factory
    interface Factory {
        fun create(): MainActivityComponent
    }

    fun inject(activity: MainActivity)
}</code></pre>
<p>이렇게 MainActivityComponent와 적용이 필요한 dependency에 <code>ActivityScope</code>를 적용하면 <code>MyDependency</code> 객체는 동인한 MainActivityComponent객체에서 provide할 때 동일한 객체를 재사용하여 제공하게 됩니다.</p>
<h2 id="qualifier"><a href="https://docs.oracle.com/javaee/7/api/javax/inject/Qualifier.html">Qualifier</a></h2>
<blockquote>
<p>Identifies qualifier annotations.</p>
</blockquote>
<p>하나의 type에 대해 다른 객체를 주입해야 하는 경우가 발생할 수 있습니다. 하지만 하나의 type에 대해 두가지 이상의 dependency를 provide하는 방법이 존재할 경우 bound multiple times에러가 발생하게 됩니다.</p>
<p>예를 들어 Coroutine의 dispatcher를 provide하는 module이 아래와 같이 정의되어 있다고 가정해봅시다.</p>
<pre><code class="language-kotlin">@Module
object DispatcherModule {

    @Provides
    fun provideIoDispatcher(): CoroutineDispatcher = Dispatchers.IO

    @Provides
    fun provideDefaultDispatcher(): CoroutineDispatcher = Dispatchers.Default
}</code></pre>
<p>하나는 IO dispatcher를 provide하고 하나는 Default dispatcher를 provide합니다. 그러나 두 dispatcher의 type은 같아서 빌드시 에러가 나게 됩니다.</p>
<pre><code>error: [Dagger/DuplicateBindings] kotlinx.coroutines.CoroutineDispatcher is bound multiple times</code></pre><p>동일한 type에 대해 dependency를 구분지어주기 위해서 <code>Qualifier</code> annotation 클래스를 정의해주어야 합니다.</p>
<pre><code class="language-kotlin">// IO dispatcher를 구분하기 위한 qualifier
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class IoDispatcher</code></pre>
<p>이제 정의해준 qualifier를 provide와 주입받을 곳에 적용해주면 됩니다.</p>
<pre><code class="language-kotlin">@Module
object DispatcherModule {

    @Provides
    @IoDispatcher
    fun provideIoDispatcher(): CoroutineDispatcher = Dispatchers.IO

    @Provides
    fun provideDefaultDispatcher(): CoroutineDispatcher = Dispatchers.Default
}</code></pre>
<pre><code class="language-kotlin">class ConversionRepositoryImpl @Inject constructor(
    private val api: ExchangeRateApi,
    // IO dispatcher를 주입받음
    @IoDispatcher private val ioDispatcher: CoroutineDispatcher
) : ConversionRepository {
    ...
}</code></pre>
<p>위의 repository에서 <code>@IoDispatcher</code> annotation을 제거하게 되면 IO dispatcher가 아니라 Default dispatcher를 주입받게 됩니다.</p>
<h2 id="provider와-lazy">Provider와 Lazy</h2>
<p>T라는 type의 dependency를 주입받을 때 T라는 type으로만 주입받을 수 있는 것이 아니라 Provider&amp;ltT&gt; 혹은 Lazy&amp;ltT&gt;로도 주입받을 수 있습니다.</p>
<h3 id="providerltt"><a href="https://docs.oracle.com/javaee/6/api/javax/inject/Provider.html">Provider&amp;ltT&gt;</a></h3>
<blockquote>
<p>Provides instances of T. </p>
</blockquote>
<p><code>Provider&lt;T&gt;</code>는 type T에 대해서 다수의 객체를 생성할 때 사용됩니다. 대표적으로 factory에서 생성할 객체마다 다른 dependency를 주입해야 할 때 사용할 수 있습니다.</p>
<pre><code class="language-kotlin">class Factory @Inject constructor(
    private val provider: Provider&lt;MainViewModel&gt;
) : ViewModelProvider.Factory {

    @Suppress(&quot;UNCHECKED_CAST&quot;)
    override fun &lt;T : ViewModel&gt; create(modelClass: Class&lt;T&gt;): T {
        return provider.get() as T
    }
}</code></pre>
<p>위는 MainViewModel을 생성할 때 사용할 MainViewModel.Factory입니다. 여기서 Factory가 <code>Provider&lt;MainViewModel&gt;</code>을 주입받아서 새로운 MainViewModel을 생성할 때 사용합니다.</p>
<p>Provider는 무작정 새로운 instance를 생성하는 것이 아니라 Scope에 맞게 생성합니다.</p>
<pre><code class="language-kotlin">@Module
object ActivityModule {

    // Dependency를 ActivityScope가 적용된 Component에서
    // 단일 객체를 재사용하도록 지정
    @Provides
    @ActivityScope // @ActivityScope는 MainActivityComponent에도 적용
    fun provideMyDependency(): MyDependency = MyDependency()
}</code></pre>
<pre><code class="language-kotlin">class MainActivity : AppCompatActivity() {

    @Inject
    lateinit var myDependencyProvider: Provider&lt;MyDependency&gt;

    @FlowPreview
    override fun onCreate(savedInstanceState: Bundle?) {
        (application as ConversionApplication).appComponent
            .getMainActivityComponentFactory()
            .create(this)
            .inject(this)

        super.onCreate(savedInstanceState)
        setContentView(binding.root)

        // 아래의 둘은 같은 객체를 갖게 된다.
        myDependencyProvider.get()
        myDependencyProvider.get()
    }
}</code></pre>
<p>위의 예제에서 myDependencyProvider는 get()을 호출하였을 때 같은 객체를 return하게 됩니다. 그 이유는 MyDependency와 MainActivityComponent에 <code>@ActivityScope</code>가 적용이 되었으므로 동일한 Component 객체내에서 같은 MyDependency를 반환하기 때문입니다.</p>
<p>만약 MyDependency에 아무런 Scope가 적용되지 않았다면 get()을 호출할 때 마다 새로운 객체를 생성해서 return하게 됩니다.</p>
<h3 id="lazyltt"><a href="https://dagger.dev/api/latest/dagger/Lazy.html">Lazy&amp;ltT&gt;</a></h3>
<blockquote>
<p>A handle to a lazily-computed value. Each Lazy computes its value on the first call to get() and remembers that same value for all subsequent calls to get().</p>
</blockquote>
<p><code>Lazy&lt;T&gt;</code>는 <a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/lazy.html">Kotlin의 lazy</a>와 비슷하게 type T의 객체를 필요할 때 생성하기 위해서 사용됩니다. Provider와 마찬가지로 get()을 이용하여 객체를 가져올 수 있습니다.</p>
<h4 id="provider와-차이점">Provider와 차이점</h4>
<p>Lazy와 Provider의 차이점은 Provider의 경우 get()을 호출할 때 마다 Scope에 맞게 새로운 객체를 생성합니다. 하지만 Lazy의 경우 처음 한번만 객체를 생성한 이후에는 생성한 객체를 memory에 cache하고 해당 객체를 return합니다.</p>
<h2 id="reference">Reference</h2>
<p>[1] &quot;Using Scopes,&quot; Android Developers, last modified n.d., accessed Jun 19, 2022, <a href="https://developer.android.com/codelabs/android-dagger#8">https://developer.android.com/codelabs/android-dagger#8</a>.</p>
<p>[2] &quot;@Provides annotation and Qualifiers,&quot; Android Developers, last modified n.d., accessed Jun 19, 2022, <a href="https://developer.android.com/codelabs/android-dagger#14">https://developer.android.com/codelabs/android-dagger#14</a>.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[DI] Dagger2 - Module, Component, Inject]]></title>
            <link>https://velog.io/@errored_pasta/DI-Dagger2-Module-Component-Inject</link>
            <guid>https://velog.io/@errored_pasta/DI-Dagger2-Module-Component-Inject</guid>
            <pubDate>Wed, 15 Jun 2022 14:20:29 GMT</pubDate>
            <description><![CDATA[<p>Android에서 dependency injection을 할 때 많이 사용하는 framework중 하나로 Dagger2가 있습니다. Dagger2는 Square에서 개발하여 현재 Google에서 유지보수를 하는 library로 compile time에 코드 생성을 통해 dependency injection을 이루어냅니다.
Dagger2에서 가장 중요한 두가지는 <code>Module</code>과 <code>Component</code>입니다.</p>
<h2 id="gradle">Gradle</h2>
<p>우선 Dagger2를 사용하기 위해서 module의 <code>build.gradle</code>에 아래와 같이 dependency를 추가해주어야 합니다.</p>
<pre><code class="language-kotlin">plugins {
  id &#39;kotlin-kapt&#39;
}

dependencies {
    implementation &#39;com.google.dagger:dagger:2.x&#39;
    kapt &#39;com.google.dagger:dagger-compiler:2.x&#39;
}
</code></pre>
<h2 id="module"><a href="https://dagger.dev/api/latest/dagger/Module.html">Module</a></h2>
<blockquote>
<p>Annotates a class that contributes to the object graph.</p>
</blockquote>
<p>Dagger의 Module은 dependency를 가지고 있는 클래스로 dependency들을 이용하여 object graph를 구성합니다.
Module은 dependency를 제공하는 방법을 알고있거나 dependency를 특정 type으로 bind하는 방법을 알고있어야 합니다.</p>
<h3 id="provides"><a href="https://dagger.dev/api/latest/dagger/Provides.html">Provides</a></h3>
<blockquote>
<p>Annotates methods of a module to create a provider method binding.</p>
</blockquote>
<p><code>@Provides</code> annotation은 Module내에서 dependency를 생성하는 method에 적용시켜야합니다. 필요시 다른 dependency를 parameter로 받아서 사용할 수 있습니다.</p>
<pre><code class="language-kotlin">@Module
object NetworkModule {

    ...

    // Function body에 ExchangeRateApi type의 
    // dependency를 어떻게 생성할지 정의
    // 생성시 OkHttpClient가 필요하므로 parameter로 받음
    // Parameter로 받을 OkHttpClient instance 또한
    // Dagger에서 어떻게 제공할지 알고 있어야함
    @Provides
    @Singleton
    fun provideExchangeRateApi(
        okHttpClient: OkHttpClient
    ): ExchangeRateApi = Retrofit.Builder()
        .baseUrl(BASE_URL)
        .client(okHttpClient)
        .addConverterFactory(GsonConverterFactory.create())
        .build()
        .create(ExchangeRateApi::class.java)

}</code></pre>
<h3 id="binds"><a href="https://dagger.dev/api/latest/dagger/Binds.html">Binds</a></h3>
<blockquote>
<p>Annotates abstract methods of a Module that delegate bindings.</p>
</blockquote>
<p><code>@Binds</code> annotation은 parameter로 받은 dependency를 어떤 type으로 binding할지 정의한 abstract method에 적용시켜야 합니다.</p>
<pre><code class="language-kotlin">@Module
interface RepositoryModule {

    // ConversionRepositoryImpl을 ConversionRepository로 bind
    @Binds
    fun bindConversionRepository(repositoryImpl: ConversionRepositoryImpl): ConversionRepository
}</code></pre>
<h2 id="component"><a href="https://dagger.dev/api/latest/dagger/Component.html">Component</a></h2>
<blockquote>
<p>Annotates an interface or abstract class for which a fully-formed, dependency-injected implementation is to be generated from a set of modules().</p>
</blockquote>
<p><code>Component</code>는 Dagger2가 <code>Module</code>을 통해서 어떻게 dependency들을 주입할지 구현할 때 사용할 interface 혹은 abstract class입니다.
우선 <code>@Component</code>를 interface에 적용하고 <code>Component</code>에 필요한 <code>dependency</code>들을 가진 Module들을 <code>modules</code>로 지정해주어야 합니다. 그 다음, method로 어떤 dependency들을 제공할지 정의하면 됩니다.</p>
<pre><code class="language-kotlin">@Component(
    // 어떤 module들을 사용할지
    modules = [
        NetworkModule::class,
        DispatcherModule::class,
        ViewModelFactoryModule::class,
        RepositoryModule::class
    ]
)
interface AppComponent {
    // 어떤 dependency를 제공할지
    fun provideViewModelFactory(): ViewModelProvider.Factory
}</code></pre>
<p>위와같이 Component를 정의하고 build를 하게되면 Dagger prefix가 붙은 클래스가 자동으로 생성되어 사용할 수 있게 됩니다.</p>
<pre><code class="language-kotlin">class ConversionApplication : Application() {
    val appComponent: AppComponent by lazy {
        // AppComponent앞에 Dagger가 붙은 DaggerAppComponent
        DaggerAppComponent.create()
    }
}</code></pre>
<p>이제 생성한 AppComponent를 이용하여 필요한 dependency를 주입받을 수 있습니다.</p>
<pre><code class="language-kotlin">class MainActivity : AppCompatActivity() {

    private lateinit var viewModelFactory: ViewModelProvider.Factory
    private val viewModel by viewModels&lt;MainViewModel&gt; { viewModelFactory }

    override fun onCreate(savedInstanceState: Bundle?) {
        // ViewModelProvider.Factory를 주입받음
        viewModelFactory =
            (application as ConversionApplication).appComponent.provideViewModelFactory()

        super.onCreate(savedInstanceState)
        setContentView(binding.root)

        ...
    }

}</code></pre>
<h3 id="subcomponent"><a href="https://dagger.dev/api/latest/dagger/Subcomponent.html">Subcomponent</a></h3>
<p>이전의 <a href="https://velog.io/@errored_pasta/DI-Pure-Dependency-Injection">Pure DI</a>에서 MainActivityContainer가 AppContainer에서 필요한 dependency를 가져왔던 것 처럼 Component에서 dependency를 제공하기 위해서 다른 Component에서 필요한 dependency를 가져와야 하는 경우도 존재합니다. 이를 가능하도록 하려면 <code>Subcomponent</code>를 이용하여야 합니다.</p>
<blockquote>
<p>A subcomponent that inherits the bindings from a parent Component or Subcomponent.</p>
</blockquote>
<p><code>Subcomponent</code>는 부모 Component(혹은 Subcomponent)로부터 dependency를 상속받아서 사용할 수 있는 Component입니다.
Subcomponent는 Component와 마찬가지로 interface나 abstract class에 <code>@Subcomponent</code> annotation을 적용하고 해당 Subcomponent에서 필요한 Module이 있다면 <code>modules</code>로 지정해주면 됩니다.</p>
<pre><code class="language-kotlin">@Subcomponent
interface MainActivityComponent {
    fun provideViewModelFactory(): ViewModelProvider.Factory
}</code></pre>
<p>여기에서는 ViewModelProvider.Factory를 AppComponent에서 가져올 것이므로 modules에 <code>ViewModelFactoryModule</code>을 추가하지 않았습니다.
그리고 Subcomponent를 사용하기 위해서는 부모 Component가 Subcomponent를 어떻게 생성할지 알아야 합니다.
이를 위해서 Subcomponent 내부에 <code>@Subcomponent.Factory</code> 혹은 <code>@Subcomponent.Builder</code> annotation을 적용한 interface를 통해 Subcomponent를 생성하는 method를 정의하고 해당 Factory 혹은 Builder를 부모 Component에서 가져올 수 있도록 하여야 합니다.</p>
<pre><code class="language-kotlin">@Subcomponent
interface MainActivityComponent {

    // MainActivityComponent를 생성할 때 사용할 Factory
    @Subcomponent.Factory
    interface Factory {
        fun create(): MainActivityComponent
    }

    fun provideViewModelFactory(): ViewModelProvider.Factory
}</code></pre>
<pre><code class="language-kotlin">@Singleton
@Component(
    modules = [
        NetworkModule::class,
        DispatcherModule::class,
        ViewModelFactoryModule::class,
        RepositoryModule::class
    ]
)
interface AppComponent {
    // 부모 Component인 AppComponent에서
    // MainActivityComponent를 생성할 때 사용할
    // MainActivityComponent.Factory를 가져올 수 있도록 한다.
    fun getMainActivityComponentFactory(): MainActivityComponent.Factory
}</code></pre>
<p>마지막으로 부모 Component에서 사용하는 Module에 어떤 Subcomponent를 가지는지 명시해주면 됩니다.
이를 위한 새로운 Module을 생성하여도 되지만 ViewModelProvider.Factory가 MainActivity에서 사용되므로 <code>ViewModelFactoryModule</code>에 <code>subcomponents</code>로 MainActivityComponent를 명시해주었습니다.</p>
<pre><code class="language-kotlin">// 모듈이 AppComponent에서 사용되고
// AppComponent가 MainActivityComponent를 Subcomponent로 가진다.
@Module(subcomponents = [MainActivityComponent::class])
interface ViewModelFactoryModule {

    @Binds
    fun bindMainViewModelFactory(factory: MainViewModel.Factory): ViewModelProvider.Factory
}</code></pre>
<p>이제 MainActivity에서 MainActivityComponent를 생성하여 원하는 dependency를 주입받을 수 있게 됩니다.</p>
<pre><code class="language-kotlin">class MainActivity : AppCompatActivity() {

    private lateinit var viewModelFactory: ViewModelProvider.Factory
    private val viewModel by viewModels&lt;MainViewModel&gt; { viewModelFactory }

    override fun onCreate(savedInstanceState: Bundle?) {
        // MainActivityComponent에서 dependency를 주입받음
        viewModelFactory = (application as ConversionApplication).appComponent
            .getMainActivityComponentFactory()
            .create()
            .provideViewModelFactory()

        super.onCreate(savedInstanceState)
        setContentView(binding.root)

        ...
    }
}</code></pre>
<h2 id="inject"><a href="https://docs.oracle.com/javaee/6/api/javax/inject/Inject.html">Inject</a></h2>
<blockquote>
<p>Identifies injectable constructors, methods, and fields.</p>
</blockquote>
<p>이제 Module과 Component를 이용하여 필요한 dependency를 주입받을 수 있게 되었습니다.
하지만 주입받을 dependency가 많을 경우 Component에 provide하는 method를 정의하는 것과 method를 호출하여 dependency들을 property로 지정하는 것이 번거로울 수 있습니다.</p>
<p><code>@Inject</code>를 이용하여 필요한 dependency들을 Dagger가 자동으로 주입하도록 할 수 있습니다.</p>
<pre><code class="language-kotlin">@Subcomponent
interface MainActivityComponent {

    @Subcomponent.Factory
    interface Factory {
        fun create(): MainActivityComponent
    }

    // MainActivity를 parameter로 받아서
    // @Inject가 적용된 dependency를 주입
    fun inject(activity: MainActivity)
}</code></pre>
<p>우선 Component에 DI를 할 클래스를 parameter로 받는 method를 정의해주어야 합니다. MainActivityComponent의 경우 MainActivity에 DI를 할 것이므로 위와 같이 정의를 해주었습니다.</p>
<pre><code class="language-kotlin">class MainActivity : AppCompatActivity() {

    // 주입 받을 dependency에 @Inject annotation을 적용
    // Dagger에서 코드 생성으로 주입할 수 있도록 public
    @Inject
    lateinit var viewModelFactory: ViewModelProvider.Factory
    private val viewModel by viewModels&lt;MainViewModel&gt; { viewModelFactory }

    override fun onCreate(savedInstanceState: Bundle?) {
        (application as ConversionApplication).appComponent
            .getMainActivityComponentFactory()
            .create()
            .inject(this)

        super.onCreate(savedInstanceState)
        setContentView(binding.root)

        ...
    }
}</code></pre>
<p>이제 MainActivityComponent에서 주입받아야 하는 dependency에 <code>@Inject</code> annotation을 적용해주고 inject method를 호출하기만 하면 됩니다.</p>
<h2 id="external-link">External Link</h2>
<p>[1] <a href="https://github.com/ErroredPasta/Dependency-Injection">https://github.com/ErroredPasta/Dependency-Injection</a></p>
<h2 id="reference">Reference</h2>
<p>[1] &quot;Dagger,&quot; Dagger, last modified n.d., accessed Jun 15, 2022, <a href="https://dagger.dev/dev-guide/">https://dagger.dev/dev-guide/</a>.</p>
<p>[2] &quot;Using Dagger in Android apps,&quot; Android Developers, last modified Dec 03, 2021, accessed Jun 15, 2022, <a href="https://developer.android.com/training/dependency-injection/dagger-android">https://developer.android.com/training/dependency-injection/dagger-android</a>.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[DI] Pure Dependency Injection]]></title>
            <link>https://velog.io/@errored_pasta/DI-Pure-Dependency-Injection</link>
            <guid>https://velog.io/@errored_pasta/DI-Pure-Dependency-Injection</guid>
            <pubDate>Sat, 11 Jun 2022 14:35:04 GMT</pubDate>
            <description><![CDATA[<p>Pure dependency injection은 어떠한 library의 도움 없이 직접 dependency injection을 하는 것을 말합니다.
Dependency injection을 할 때 클래스 내부에 필요한 dependency의 생성을 외부로 전가하게 됩니다. 클래스의 생성을 계속 외부로 전가하는 것은 불가능하며 언젠가는 생성을 해야합니다. </p>
<h2 id="composition-root">Composition Root</h2>
<p><img src="https://velog.velcdn.com/images/errored_pasta/post/5afd372e-998a-4baf-8f7e-6f0a0abb25c3/image.png" alt=""></p>
<blockquote>
<p>A Composition Root is a single, logical location in an application
where modules are composed together.</p>
</blockquote>
<p>Composition root은 application에서 module이 구성되는 단일의 logical한 장소입니다. 그러므로 필요한 클래스의 생성은 composition root에서 이루어져야 합니다. 그렇게 함으로써 클래스간의 관계를 관리하기 용이해집니다.
Composition root은 application의 entry point에 최대한 가까이에 위치해야합니다.</p>
<h2 id="entry-point">Entry Point</h2>
<p>Entry point는 진입점이라는 의미로 프로그램이 시작되는 지점을 의미합니다. Android에서는 유저가 생성하지 못하는 Application이나 Activity, Fragment같은 component에 DI를 하기위해서 entry point에서 최대한 가까운 곳(onCreate 등)에서 필요한 dependency들을 주입받아야 합니다.
Dagger Hilt에서는 아래와 같은 component들에 대해서 기본적으로 DI를 관리합니다.</p>
<p><img src="https://velog.velcdn.com/images/errored_pasta/post/fae0b13d-6628-463e-8d7b-ec0b9e17fba7/image.svg" alt=""></p>
<h2 id="예시el1"><a href="https://github.com/ErroredPasta/Dependency-Injection/tree/pure-di">예시[EL1]</a></h2>
<p>보통 Activity에서 자신의 상태를 나타내고 business logic을 처리하기 위해 ViewModel을 생성하여야 합니다. 그리고 ViewModel을 생성하기 위해서는 Repository 혹은 Use case가 필요하고 또 Repository를 생성하기위해서는 DataSource가 필요하게 됩니다.
Repository, Use case등은 app 전반에 걸쳐서 사용되므로 AppContainer에 필요한 dependency들을 정의하면 아래와 같습니다.</p>
<pre><code class="language-kotlin">class AppContainer {

    // [No getter dependency]
    // Container와 함께 생명주기를 같이할 dependency
    // AppContainer는 Application에서 생성되므로
    // singleton으로 처리할 dependency이다.
    private val okHttpClient: OkHttpClient by lazy {
        OkHttpClient.Builder()
            .addInterceptor(HttpLoggingInterceptor().apply {
                level = HttpLoggingInterceptor.Level.BODY
            }).build()
    }

    private val exchangeRateApi: ExchangeRateApi by lazy {
        Retrofit.Builder()
            .baseUrl(BASE_URL)
            .client(okHttpClient)
            .addConverterFactory(GsonConverterFactory.create())
            .build()
            .create(ExchangeRateApi::class.java)
    }

    private val ioDispatcher: CoroutineDispatcher get() = Dispatchers.IO

    // [Getter dependency]
    // 생명주기를 관리하지 않을 dependency
    // 필요할 때마다 새로운 객체를 생성한다.
    private val conversionRepository: ConversionRepository
        get() = ConversionRepositoryImpl(
            api = exchangeRateApi,
            ioDispatcher = ioDispatcher
        )

    private val mainViewModelProvider: Provider&lt;MainViewModel&gt;
        get() = Provider { MainViewModel(repository = conversionRepository) }

    val viewModelFactory: ViewModelProvider.Factory
        get() = MainViewModel.Factory(provider = mainViewModelProvider)

    companion object {
        private const val BASE_URL = &quot;https://v6.exchangerate-api.com/&quot;
    }
}</code></pre>
<p>여기에서 getter가 있는 dependency도 있고 없는 dependency도 있는 것을 볼 수 있습니다. Getter를 지정해준 dependency는 필요할 때마다 새롭게 생성되고 없는 dependency는 <code>AppContainer</code>와 함께 수명을 함께하며 계속 재사용됩니다. <code>AppContainer</code>는 Application에서 사용되므로 getter가 없는 dependency는 singleton으로 처리할 것들입니다.</p>
<p>이렇게 <code>AppContainer</code>를 정의하였으면 Application에서 해당 container를 가지도록 해주면 됩니다.</p>
<pre><code class="language-kotlin">class ConversionApplication : Application() {
    val appContainer: AppContainer by lazy {
        AppContainer()
    }
}</code></pre>
<p><code>appContainer</code>를 lateinit var로 하여 onCreate()에서 생성해주어도 됩니다. 단, lateinit var로 할 경우 setter를 private하게하여 다른 component에서 변경할 수 없도록 하는 것이 좋습니다.</p>
<p>이제 남은 일은 <code>MainActivity</code>에서 필요한 dependency를 주입받는 것입니다. <code>MainActivity</code>에서 현재 ViewModel을 생성하기 위해 ViewModelProvider.Factory만 필요하여 <code>AppContainer</code>에서 바로 사용하여도 되지만 <code>MainActivity</code>에서 사용할 container를 정의하고 해당 container를 이용하여 dependency를 주입 받아보겠습니다.</p>
<p><code>MainActivityContainer</code>에서 필요한 dependency중 <code>AppContainer</code>에서 가져올 수 있는 것들은 가져오도록 하면 아래와 같이됩니다.</p>
<pre><code class="language-kotlin">class MainActivityContainer(
    private val appContainer: AppContainer
) {

    // get from appContainer
    val viewModelFactory: ViewModelProvider.Factory get() = appContainer.viewModelFactory
}</code></pre>
<p>마지막으로 <code>MainActivityContainer</code>를 생성하기 위해서 Factory interface를 정의해주고 <code>AppContainer</code>에서 <code>MainActivityContainer.Factory</code>를 property로 가지도록 하여 <code>MainActivityContainer</code>를 생성할 수 있게 해주면 됩니다.</p>
<pre><code class="language-kotlin">class MainActivityContainer(
    private val appContainer: AppContainer
) {

    ...

    // MainActivityContainer를 생성할 Factory interface
    interface Factory {
        fun create(): MainActivityContainer
    }
}</code></pre>
<pre><code class="language-kotlin">class AppContainer {

    ...

    val mainActivityContainerFactory = object : MainActivityContainer.Factory {
        override fun create(): MainActivityContainer =
            MainActivityContainer(appContainer = this@AppContainer)
    }

    ...
}</code></pre>
<pre><code class="language-kotlin">class MainActivity : AppCompatActivity() {

    ...

    private val mainActivityContainer by lazy {
        (application as ConversionApplication).appContainer
            .mainActivityContainerFactory.create()
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        // Composition root
        viewModelFactory = mainActivityContainer.viewModelFactory

        super.onCreate(savedInstanceState)
        setContentView(binding.root)

        ...
    }

    ...
}</code></pre>
<h2 id="external-link">External Link</h2>
<p>[1] <a href="https://github.com/ErroredPasta/Dependency-Injection/tree/pure-di">https://github.com/ErroredPasta/Dependency-Injection/tree/pure-di</a></p>
<h2 id="reference">Reference</h2>
<p>[1] Mark Seemann and Steven van Deursen, Dependency Injection Principles, Practices, and Patterns (n.p.: Manning Publications, 2019), 85.</p>
<p>[2] &quot;Manual dependency injection,&quot; Android Developers, last modified Oct 27, 2021, accessed Jun 11, 2022, <a href="https://developer.android.com/training/dependency-injection/manual">https://developer.android.com/training/dependency-injection/manual</a>.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[DI] Dependency Injection]]></title>
            <link>https://velog.io/@errored_pasta/DI-Dependency-Injection</link>
            <guid>https://velog.io/@errored_pasta/DI-Dependency-Injection</guid>
            <pubDate>Tue, 07 Jun 2022 17:18:22 GMT</pubDate>
            <description><![CDATA[<h2 id="dependency-injection">Dependency Injection</h2>
<p>A클래스에서 B클래스가 필요한 경우 A클래스가 B클래스에 의존한다고 하며 B클래스가 A클래스의 dependency가 됩니다. Dependency injection이란 클래스에 필요한 dependency를 클래스 내부에서 생성하는 것이 아니라 외부에서 주입을 받는 것을 말합니다.</p>
<h2 id="dependency-injection-이점">Dependency Injection 이점</h2>
<ul>
<li><p><strong>Decreased Coupling</strong>
클래스간의 coupling을 줄일 수 있습니다. 하지만 이는 dependency injection으로만 이루어지는 것이 아니라 dependency를 concrete type이 아니라 abstract type으로 받아야 합니다.</p>
</li>
<li><p><strong>Reduced Boilerplate Code</strong>
외부에서 dependency를 주입받기 때문에 내부에서 생성할 필요가 없어집니다. 그래서 dependency를 생성하기 위한 boilerplate code가 감소합니다.</p>
</li>
<li><p><strong>Concurrent Development</strong>
클래스에서 abstract한 type으로 dependency를 주입받을 경우, 서로 알아야 하는 것은 dependency의 abstract type이므로 해당 클래스의 개발과 dependency 클래스의 개발을 동시에 할 수 있습니다.</p>
</li>
<li><p><strong>Testable</strong>
Dependency를 쉽게 교체할 수 있으므로 test double instance를 클래스에 넘겨주어 test할 수 있습니다.</p>
</li>
<li><p><strong>Increased Reusability</strong>
이점 또한 dependency를 쉽게 교체할 수 있으므로 클래스가 필요한 곳에서 dependency를 생성하여 주입해주기만 하면 사용이 가능합니다.</p>
</li>
</ul>
<h2 id="dependency-injection-patterns">Dependency Injection Patterns</h2>
<h3 id="constructor-injection">Constructor Injection</h3>
<p><img src="https://velog.velcdn.com/images/errored_pasta/post/6ce7ab8d-be2b-4f58-b4cc-2fccc5dbc6c3/image.png" alt=""></p>
<p>클래스의 constructor에 필요한 dependency들을 정의하여 생성시 주입받도록 하는 방식입니다.  위의 그림은 constructor injection을 이용하여 클래스를 생성하는 sequence diagram으로 필요한 dependency인 ProduceService를 생성하여 HomeController의 constructor에 주입하는 것을 볼 수 있습니다. 기본적으로 constructor injection을 사용하는 것이 좋습니다.</p>
<h3 id="property-injection">Property Injection</h3>
<p><img src="https://velog.velcdn.com/images/errored_pasta/post/959d0667-8ac1-4a55-9e27-92f79a44b71d/image.png" alt=""></p>
<p>Dependency를 클래스의 property에 주입하는 방식입니다. 보통 property를 private하게 하고 setter를 public하게 하여 주입받으므로 setter injection이라고도 불립니다. Property injection은 dependency의 좋은 기본 구현체가 존재하고 다른 구현체를 주입하는 것이 optional할 경우 혹은 클래스의 생성이 개발자에 의해 이루어지지 않을 때 사용해야합니다. Android의 경우 Activity, Fragment 등은 android framework에 의해 생성되므로 property injection을 사용합니다.</p>
<h3 id="method-injection">Method Injection</h3>
<p><img src="https://velog.velcdn.com/images/errored_pasta/post/34530bfc-c772-48ff-a821-802e7b637ba9/image.png" alt=""></p>
<p>Method injection은 method의 parameter로 method에 필요한 dependency를 주입하는 방식입니다. Method call을 할 때마다 dependency가 달라지는 경우 method injection을 사용합니다.</p>
<h2 id="어떤-dependency를-주입할까">어떤 Dependency를 주입할까</h2>
<p>Dependency는 기준에 따라 stable dependency와 volatile dependency로 나눌 수 있습니다.</p>
<h3 id="stable-dependency">Stable Dependency</h3>
<p>아래 조건들을 만족하는 경우 stable dependency라고 합니다.</p>
<ul>
<li><p><strong>이미 존재하는 클래스 혹은 모듈</strong></p>
</li>
<li><p><strong>새로운 버전에서 큰 변화가 없다고 예상되는 경우</strong></p>
</li>
<li><p><strong>결정적인 알고리즘(deterministic algorithm)을 가진 타입</strong></p>
</li>
<li><p><strong>다른 클래스 혹은 모듈로 교체, decorate, intercept되지 않다고 예상되는 경우</strong>
Interception의 경우 interceptor 클래스를 생성하여 수행할 경우 stable하다고 분류될 수도 있습니다.</p>
</li>
</ul>
<h3 id="volatile-dependency">Volatile Dependency</h3>
<p>아래 조건 중 하나라도 만족하면 volatile dependency입니다.</p>
<ul>
<li><p><strong>Runtime Environment를 구성하는 dependency</strong></p>
</li>
<li><p><strong>Dependency가 아직 존재하지 않고 개발중인 경우</strong></p>
</li>
<li><p><strong>Dependency가 모든 machine에 설치될 수 없는 경우</strong>
비용이 많이 드는 third-party library 혹은 모든 OS에서 사용이 가능하지 않은 dependency가 이에 속합니다.</p>
</li>
<li><p><strong>Dependency가 비결정적 알고리즘(nondeterministic algorithm)을 가진 경우</strong>
이 경우는 특히 unit testing에서 중요합니다. Unit test에서 모든 test들은 deterministic해야 하지만 nondeterministic algorithm을 가진 dependency를 교체할 수 없으면 test를 deterministic하게 할 수 없기 때문입니다. Nondeterministic algorithm은 보통 난수, 시간에 의존하는 알고리즘 등이 있습니다.</p>
</li>
</ul>
<p>Volatile dependency의 coupling을 abstract type을 도입하여 줄이고 dependency injection을 사용하여야 합니다.</p>
<h2 id="reference">Reference</h2>
<p>[1] &quot;Dependency injection,&quot; Wikipedia, last modified n.d., accessed Jun 8, 2022, <a href="https://en.wikipedia.org/wiki/Dependency_injection">https://en.wikipedia.org/wiki/Dependency_injection</a>.</p>
<p>[2] Mark Seemann and Steven van Deursen, <em>Dependency Injection Principles, Practices, and Patterns</em> (n.p.: Manning Publications, 2019), 26-27, 83-120.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Android] DataBinding - Binding Adapter]]></title>
            <link>https://velog.io/@errored_pasta/Android-DataBinding-Binding-Adapter</link>
            <guid>https://velog.io/@errored_pasta/Android-DataBinding-Binding-Adapter</guid>
            <pubDate>Wed, 25 May 2022 13:40:44 GMT</pubDate>
            <description><![CDATA[<h2 id="binding-adapter">Binding Adapter</h2>
<p>Binding adapter를 이용하여 값을 View에 어떻게 binding할지 지정해줄 수 있습니다. 대표적인 예로 아래와 같이 URL을 이용하여 이미지를 라이브러리를 통해 불러오는 경우가 많은데 이를 binding adapter를 이용하여 간단하게 할 수 있습니다.</p>
<pre><code class="language-kotlin">fun ImageView.load(
    uri: String,
    corner: Float = 0f,
    scaleType: Transformation&lt;Bitmap&gt; = CenterInside(),
    @DrawableRes placeholder: Int = R.drawable.ic_baseline_add_a_photo_24
) = Glide.with(this)
    .load(uri)
    .placeholder(placeholder)
    .transition(DrawableTransitionOptions.withCrossFade(factory)) // fade-in 애니메이션
    .diskCacheStrategy(DiskCacheStrategy.ALL)
    .apply {
        // 이미지의 라운딩 처리
        if (corner &gt; 0f) {
            transform(scaleType, RoundedCorners(corner.fromDpToPx()))
        }
    }.into(this)</code></pre>
<p>URL string과 DataBinding을 이용하여 이미지를 불러오기 위해서 알맞은 binding adapter를 지정해주어야 합니다.
Binding adapter는 Java의 경우 static method로 정의를 하고 Kotlin의 경우 top-level function이나 object의 method로 정의가 가능합니다. 단, binding adapter는 static해야 하므로 Kotlin으로 정의할 때 object의 method로 정의할 경우 <code>@JvmStatic</code> annotation을 적용하여야 합니다.</p>
<pre><code class="language-kotlin">@BindingAdapter(&quot;imageUrl&quot;)
fun setImageByUrl(imageView: ImageView, imageUrl: String?) {
    if (imageUri == null) return

    imageView.load(imageUrl)
}</code></pre>
<p>위와 같이 <code>@BindingAdapter</code> annotation으로 binding adapter를 지정해주어야 합니다. Annotation의 값으로 View에서 사용될 속성의 이름을 지정해주어야 합니다. Parameter는 아래와 같습니다.</p>
<ul>
<li>첫번째 parameter : Binding adapter를 적용할 View입니다. 해당 예제에서는 URL의 이미지를 ImageView에 적용하는 것이므로 ImageView로 지정하였습니다.</li>
<li>두번쨰 parameter : imageUrl 속성으로 받을 값입니다. 예제에서는 이미지 URL을 String으로 받습니다.</li>
</ul>
<p>Binding adapter를 지정해주었으면 layout xml에서 해당 속성을 사용할 수 있게 됩니다.</p>
<pre><code class="language-xml">&lt;ImageView
    android:id=&quot;@+id/updateMarketDetailImage&quot;
    imageUrl=&quot;@{updateMarketUiState.marketDetailImage}&quot;
     ... /&gt;</code></pre>
<h3 id="여러-속성-값을-받을-경우">여러 속성 값을 받을 경우</h3>
<p>여러개의 값을 View에 적용하여야 하는 경우도 존재합니다. 위의 이미지 URL 예제에서 만약 rounding 처리를 하고 싶을 경우 불러올 이미지의 URL 값과 얼마나 이미지 rounding을 처리할지에 대한 값을 받아야 합니다.
이럴 경우 <code>@BindingAdapter</code>에 이미지 URL 속성 이름과 rounding 값을 받을 속성 이름을 지정해주면 됩니다.</p>
<pre><code class="language-kotlin">@BindingAdapter(value = [&quot;imageUrl&quot;, &quot;cornerRadius&quot;], requireAll = false)
fun setImageByUrl(imageView: ImageView, imageUri: String?, corner: Float) {
    if (imageUri == null) return

    imageView.load(imageUri, corner)
}</code></pre>
<p>여러 속성을 지정해주면 여러 속성의 값을 받아야하고 그에 대한 parameter들을 지정해주어야 합니다.</p>
<ul>
<li>첫번째 parameter : Binding adapter를 적용할 View입니다.</li>
<li>두번째 parameter : <code>@BindingAdapter</code>에 첫번째로 정의한 속성으로 받을 값입니다. 해당 예제에서는 imageUrl의 속성으로 String값을 받습니다.</li>
<li>세번째 parameter : 첫번째로 정의한 속성으로 받을 값입니다. cornerRadius의 속성으로 Float값을 받습니다.</li>
</ul>
<p>이미지 rounding의 경우 optional하기 때문에 requireAll을 false로 지정하였습니다. </p>
<blockquote>
<p>requireAll은 binding adapter가 호출되려면 모든 속성이 필요한지 지정합니다. true일 경우 value로 지정한 속성 값이 모두 할당되어야 호출이 됩니다. false일 경우에는 어느 하나의 속성이라도 할당이 될 경우 adapter가 호출됩니다. requireAll은 기본 값으로 true를 갖습니다.</p>
</blockquote>
<h2 id="reference">Reference</h2>
<p>[1] &quot;Binding adapters,&quot; Android Developers, last modified Oct 27, 2021, accessed May 25, 2022, <a href="https://developer.android.com/topic/libraries/data-binding/binding-adapters">https://developer.android.com/topic/libraries/data-binding/binding-adapters</a>.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Android] DataBinding - Two-way Data Binding]]></title>
            <link>https://velog.io/@errored_pasta/Android-DataBinding-Two-way-Data-Binding</link>
            <guid>https://velog.io/@errored_pasta/Android-DataBinding-Two-way-Data-Binding</guid>
            <pubDate>Wed, 25 May 2022 13:40:11 GMT</pubDate>
            <description><![CDATA[<p>기존의 DataBinding은 일방적으로 변수의 값을 View에 적용하는 것이였습니다. View의 값을 DataBinding 변수에 적용하며 View에 변수의 값을 적용하는 방법도 존재합니다.</p>
<h2 id="two-way-data-binding">Two-way data binding</h2>
<p>DataBinding 변수를 View에 적용시키며 변화하는 View의 값을 DataBinding변수에 적용하는 것을 two-way data binding이라고 합니다. 기존에는 DataBinding을 사용하면서 변하는 View의 값을 변수에 반영하기 위해서는 아래와 같이 값이 변할 때 동작을 정의한 method를 같이 설정해 주어야 했습니다.</p>
<pre><code class="language-xml">&lt;!-- one-way data binding --&gt;
&lt;CheckBox
    android:id=&quot;@+id/rememberMeCheckBox&quot;
    android:checked=&quot;@{viewmodel.rememberMe}&quot;
    android:onCheckedChanged=&quot;@{viewmodel.rememberMeChanged}&quot;
/&gt;</code></pre>
<p>Binding expression을 기존과 약간 다르게 <code>@={}</code>를 사용하여 two-way data binding을 적용할 수 있습니다.</p>
<pre><code class="language-xml">&lt;!-- two-way data binding --&gt;
&lt;CheckBox
    android:id=&quot;@+id/rememberMeCheckBox&quot;
    android:checked=&quot;@={viewmodel.rememberMe}&quot;
/&gt;</code></pre>
<p>위와 같이 설정하게 되면 binding class를 생성시 자동으로 InverseBindingListener를 생성하여 View의 값이 변경될 때 자동으로 변수에 반영해주게 됩니다.</p>
<pre><code class="language-java">private androidx.databinding.InverseBindingListener mboundView1androidTextAttrChanged = new androidx.databinding.InverseBindingListener() {
    @Override
    public void onChange() {
        // Inverse of post.title
        //         is post.setTitle((java.lang.String) callbackArg_0)
        java.lang.String callbackArg_0 = androidx.databinding.adapters.TextViewBindingAdapter.getTextString(mboundView1);
        // localize variables for thread safety
        // post.title
        java.lang.String postTitle = null;
        // post
        com.example.architectureexample.data.models.post.Post post = mPost;
        // post != null
        boolean postJavaLangObjectNull = false;



        postJavaLangObjectNull = (post) != (null);
        if (postJavaLangObjectNull) {




            post.setTitle(((java.lang.String) (callbackArg_0)));
        }
    }
};</code></pre>
<h2 id="reference">Reference</h2>
<p>[1] &quot;Two-way data binding,&quot; Android Developers, last modified Oct 28, 2021, accessed May 25, 2022, <a href="https://developer.android.com/topic/libraries/data-binding/two-way">https://developer.android.com/topic/libraries/data-binding/two-way</a>.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Android] DataBinding - Observable]]></title>
            <link>https://velog.io/@errored_pasta/Android-DataBinding-Observable</link>
            <guid>https://velog.io/@errored_pasta/Android-DataBinding-Observable</guid>
            <pubDate>Wed, 25 May 2022 13:38:39 GMT</pubDate>
            <description><![CDATA[<p>Data binding 변수의 특정 property가 변경될 경우 해당 변경을 반영하기 위해서는 변수를 새로 할당해 주어야 합니다. 만약 자주 변하는 property가 존재할 경우 계속 새로 data binding 변수를 할당하지 않고 해당 property를 observable하게 만들어 알아서 반영이 되도록 할 수 있습니다. </p>
<h3 id="observable-fields">Observable fields</h3>
<p>기본적으로 data binding library에서 제공되는 클래스들로 아래의 observable field 클래스들을 사용할 수 있습니다.</p>
<ul>
<li>ObservableBoolean</li>
<li>ObservableByte</li>
<li>ObservableChar</li>
<li>ObservableShort</li>
<li>ObservableInt</li>
<li>ObservableLong</li>
<li>ObservableFloat</li>
<li>ObservableDouble</li>
<li>ObservableParcelable</li>
<li>ObservableField&lt;T&gt;</li>
</ul>
<pre><code class="language-java">public class ObservableField&lt;T&gt; extends BaseObservableField implements Serializable {
    static final long serialVersionUID = 1L;
    private T mValue;

    ...

    /**
     * Set the stored value.
     *
     * @param value The new value
     */
    public void set(T value) {
        if (value != mValue) { // value changed
            mValue = value;
            notifyChange();
        }
    }
}</code></pre>
<p>위의 클래스 중 generic한 ObservableField&amp;ltT&gt;의 동작 방식을 보면 set(value)로 값을 전달해주고 이전 값과 다르다면 내부에서 notifyChange()를 호출하여 새롭게 binding을 하는 것을 볼 수 있습니다.</p>
<p>Observable field의 값을 읽거나 새로운 값을 할당하기 위해서는 각각 set(value), get() method를 사용하거나 Kotlin extension을 이용하여 LiveData처럼 value property를 이용하여 사용할 수도 있습니다.</p>
<pre><code class="language-kotlin">// kotlin extension property
var &lt;T&gt; ObservableField&lt;T&gt;.value
    get() = this.get()
    set(value) = this.set(value)

...

class MyClass(
    val intData: ObservableField&lt;Int&gt;
)

...

myClass.intData.value = 10 // write new int
Log.d(&quot;TAG&quot;, myClass.intData.value.toString()) // read int
</code></pre>
<h3 id="observable-collections">Observable collections</h3>
<p>Observable은 단순 field뿐만이 아니라 collection의 List와 Map도 지원합니다. 동작방식은 위의 ObservableField&amp;ltT&gt;와 비슷하게 put(key, value), clear()등 내용에 변화가 생기면 내부에서 notify하여 새로 binding하게 됩니다.</p>
<p>ObservableMap은 key type이 reference type일 때 적합합니다. </p>
<pre><code class="language-kotlin">ObservableArrayMap&lt;String, Any&gt;().apply {
    put(&quot;firstName&quot;, &quot;Google&quot;)
    put(&quot;lastName&quot;, &quot;Inc.&quot;)
    put(&quot;age&quot;, 17)
}</code></pre>
<pre><code class="language-xml">&lt;data&gt;
    &lt;import type=&quot;android.databinding.ObservableMap&quot;/&gt;
    &lt;variable name=&quot;user&quot; type=&quot;ObservableMap&amp;lt;String, Object&gt;&quot;/&gt;
&lt;/data&gt;
…
&lt;!-- user.lastName is &quot;Inc.&quot; --&gt;
&lt;TextView
    android:text=&quot;@{user.lastName}&quot;
    android:layout_width=&quot;wrap_content&quot;
    android:layout_height=&quot;wrap_content&quot;/&gt;

&lt;!-- user.age is 17 --&gt;
&lt;TextView
    android:text=&quot;@{String.valueOf(1 + (Integer)user.age)}&quot;
    android:layout_width=&quot;wrap_content&quot;
    android:layout_height=&quot;wrap_content&quot;/&gt;
</code></pre>
<p>ObservableList는 key type이 정수일 때 적합합니다.</p>
<pre><code class="language-kotlin">ObservableArrayList&lt;Any&gt;().apply {
    add(&quot;Google&quot;)
    add(&quot;Inc.&quot;)
    add(17)
}</code></pre>
<pre><code class="language-xml">&lt;data&gt;
    &lt;import type=&quot;android.databinding.ObservableList&quot;/&gt;
    &lt;import type=&quot;com.example.my.app.Fields&quot;/&gt;
    &lt;variable name=&quot;user&quot; type=&quot;ObservableList&amp;lt;Object&gt;&quot;/&gt;
&lt;/data&gt;
…
&lt;TextView
    android:text=&#39;@{user[Fields.LAST_NAME]}&#39;
    android:layout_width=&quot;wrap_content&quot;
    android:layout_height=&quot;wrap_content&quot;/&gt;
&lt;TextView
    android:text=&#39;@{String.valueOf(1 + (Integer)user[Fields.AGE])}&#39;
    android:layout_width=&quot;wrap_content&quot;
    android:layout_height=&quot;wrap_content&quot;/&gt;</code></pre>
<h3 id="observable-objects">Observable objects</h3>
<p>BaseObservable 클래스를 상속받아 사용자가 정의한 클래스의 property 값이 변경될 때 data binding이 자동으로 되도록 할 수 있습니다.</p>
<pre><code class="language-kotlin">class User : BaseObservable() {

    @get:Bindable
    var firstName: String = &quot;&quot;
        set(value) {
            field = value
            notifyPropertyChanged(BR.firstName)
        }

    @get:Bindable
    var lastName: String = &quot;&quot;
        set(value) {
            field = value
            notifyPropertyChanged(BR.lastName)
        }
}</code></pre>
<p>생성된 ViewDataBinding 클래스에서 봤던 것 처럼 클래스가 BaseObservable을 상속받고, getter에 @Bindable annotation을 적용시키고, setter에서 값을 할당 후 notifyPropertyChanged를 이용하여 @Bindable annotation을 적용시킨 property가 변경된 것을 알려주어야 합니다.</p>
<h3 id="livedata">LiveData</h3>
<p>DataBinding에 Observable뿐만 아니라 LiveData도 사용가능합니다. LiveData를 observe하기 위해 lifecycleOwner가 필요하므로 아래와 같이 지정해주어야 합니다.</p>
<pre><code class="language-kotlin">class ViewModelActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        // Inflate view and obtain an instance of the binding class.
        val binding: UserBinding = DataBindingUtil.setContentView(this, R.layout.user)

        // Specify the current activity as the lifecycle owner.
        binding.lifecycleOwner = this
    }
}</code></pre>
<p>이렇게 lifecycleOwner를 지정해주면 ViewDataBinding.LiveDataListener에서 이전 owner를 제거하고 새로운 owner를 이용하여 observe하게 됩니다.</p>
<pre><code class="language-java">@Override
public void setLifecycleOwner(@Nullable LifecycleOwner lifecycleOwner) {
    LifecycleOwner previousOwner = getLifecycleOwner();
    LifecycleOwner newOwner = lifecycleOwner;
    LiveData&lt;?&gt; liveData = mListener.getTarget();
    if (liveData != null) {
        if (previousOwner != null) {
            liveData.removeObserver(this);
        }
        if (newOwner != null) {
            liveData.observe(newOwner, this);
        }
    }
    if (newOwner != null) {
        mLifecycleOwnerRef = new WeakReference&lt;LifecycleOwner&gt;(newOwner);
    }
}</code></pre>
<h2 id="reference">Reference</h2>
<p>[1] &quot;Work with observable data objects,&quot; Android Developers, last modified n.d., accessed May 25, 2022, <a href="https://developer.android.com/topic/libraries/data-binding/observability">https://developer.android.com/topic/libraries/data-binding/observability</a>.</p>
<p>[2] &quot;Bind layout views to Architecture Components,&quot; Android Developers, last modified n.d., accessed May 25, 2022, <a href="https://developer.android.com/topic/libraries/data-binding/architecture#livedata">https://developer.android.com/topic/libraries/data-binding/architecture#livedata</a>.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[BOJ] 16472번 : 고냥이]]></title>
            <link>https://velog.io/@errored_pasta/BOJ-16472%EB%B2%88-%EA%B3%A0%EB%83%A5%EC%9D%B4</link>
            <guid>https://velog.io/@errored_pasta/BOJ-16472%EB%B2%88-%EA%B3%A0%EB%83%A5%EC%9D%B4</guid>
            <pubDate>Tue, 24 May 2022 17:58:20 GMT</pubDate>
            <description><![CDATA[<h2 id="코드">코드</h2>
<pre><code class="language-kotlin">import java.lang.*
import java.util.*

var n = 0
var str = &quot;&quot;
var result = 0

val map = HashMap&lt;Char, Int&gt;()

fun main() {
    input()
    func()
    println(result)
}

fun input() {
    val sc = Scanner(System.`in`)

    n = sc.nextInt()
    str = sc.next()

    sc.close()
}

fun func() {
    var left = 0
    var right = 0

    while (true) {
        val length = map.keys.size

        if (length &lt;= n) {
            if (result &lt; (right - left))
                result = right - left

            if (right == str.length) return

            val count = map[str[right]] ?: 0
            map[str[right]] = count + 1

            ++right
        } else {
            val count = map[str[left]]!!

            if (count == 1) map.remove(str[left])
            else map[str[left]] = count - 1

            ++left
        }
    }
}
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Design Principle] Dependency Inversion Principle]]></title>
            <link>https://velog.io/@errored_pasta/Design-Principle-Dependency-Inversion-Principle</link>
            <guid>https://velog.io/@errored_pasta/Design-Principle-Dependency-Inversion-Principle</guid>
            <pubDate>Mon, 23 May 2022 07:55:32 GMT</pubDate>
            <description><![CDATA[<h2 id="정의">정의</h2>
<blockquote>
<ul>
<li>High-level modules should not depend on low-level modules. Both should depend on abstractions.</li>
<li>Abstractions should not depend on details. Details should depend on abstractions.</li>
</ul>
</blockquote>
<p>Dependency Inversion Principle(이하 DIP)의 정의는 위와 같이 2가지가 있습니다. 번역을 해보면 high-level 모듈은 low-level 모듈에 의존해서는 안되고 둘 다 abstraction에 의존해야 하며 abstraction은 detail에 의존해서는 안되고 detail이 abstraction에 의존해야 한다는 뜻입니다.
<br></p>
<h3 id="high-level-module과-low-level-module">High-level module과 Low-level module</h3>
<p>DIP의 정의에서 high-level module과 low-level module이 있는 것을 볼 수 있습니다. 그렇다면 high-level module은 무엇이고 low-level module은 무엇일까요?
이를 알아내기 위해서는 우선 level에 대한 정의가 필요합니다. Uncle Bob은 Clean Architecture책에서 level의 정의가 다음과 같다고 했습니다.</p>
<blockquote>
<p>A strict definition of “level” is “the distance from the inputs and outputs.”</p>
</blockquote>
<p>Level의 정의는 I/O와 떨어진 거리입니다. 즉, I/O를 하는 module은 가장 low-level이며 다른 module들을 거칠수록 high-level이 된다는 의미입니다.</p>
<p><img src="https://velog.velcdn.com/images/errored_pasta/post/2e5be1e7-5990-45f9-8cba-0902f7f21f37/image.png" alt="">
위의 그림에서 Translate module은 I/O 연산을 담당하는 Read Char module과 Write Char module로부터 1의 거리만큼 떨어져 있는 것을 볼 수 있습니다. 그러므로 해당 예제에서 가장 level이 높은 module은 Translate module입니다.</p>
<h3 id="clean-architecture와-dip">Clean Architecture와 DIP</h3>
<p>이제 DIP의 정의에서 불명확한 것이 없어 보입니다. 그러면 DIP는 왜 사용하는 걸까요?
이는 <a href="https://velog.io/@errored_pasta/Architecture-Clean-Architecture">clean architecture</a>와 연관이 있습니다.</p>
<p><img src="https://velog.velcdn.com/images/errored_pasta/post/14d06e27-bcf1-48b8-b421-789c028c9f50/image.png" alt=""></p>
<p>Clean architecture는 시스템을 layer로 나누어 관심사를 분리하는 것이 목적입니다. 이렇게 나뉜 layer들은 level이 존재하며 위 그림에서 안쪽으로 갈수록 high-level이며 바깥으로 갈수록 low-level입니다.
Clean architecture에서 dependency는 바깥 원에서 안쪽 원으로만 가져야 하는 dependency rule이 있다고 설명했습니다. 만약 dependency rule이 깨지는 상황에서는 DIP를 이용하여 dependency의 high-level layer가 low-level layer에 직접적으로 의존하지 않도록 해야 하며 그 이유는 아래와 같습니다.</p>
<ul>
<li><p><strong>재사용성</strong>
Entity layer와 use case layer는 business logic을 담고 있어서 동일한 business logic의 application들에서 재사용이 가능합니다. 하지만 해당 layer들이 low-level layer로의 직접적인 dependency를 가질 경우 재사용이 힘들어집니다.
예를 들어 entity layer가 android framework의 dependency를 가질 경우 해당 entity layer는 android application내에서만 재사용이 가능하게 됩니다.</p>
</li>
<li><p><strong>Low-level module의 변경에 대처 가능</strong>
Low-level module에 변경사항이 생길 경우 high-level module에 영향을 끼치지 않고 abstraction의 구현체를 변경하여 대처를 할 수 있습니다. 이는 <a href="https://velog.io/@errored_pasta/Design-Principle-Open-Closed-Principle">open-closed principle</a>과 연관이 있습니다. 단, abstraction에 변화가 생길 경우에는 high-level module에 영향을 끼칠 수도 있습니다.</p>
</li>
</ul>
<h2 id="reference">Reference</h2>
<p>[1] Robert Martin, <em>Agile software development: principles, patterns, and practices</em> (n.p.: Pearson, 2013), 127-134.</p>
<p>[2] Robert Martin, <em>Clean Architecture: A Craftsman’s Guide to Software Structure and Design</em> (n.p.: Prentice Hall, 2017), 87-91, 183-187.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[BOJ] 15650번 : N과 M (2) ]]></title>
            <link>https://velog.io/@errored_pasta/BOJ-15650%EB%B2%88-N%EA%B3%BC-M-2-x5xmfdc9</link>
            <guid>https://velog.io/@errored_pasta/BOJ-15650%EB%B2%88-N%EA%B3%BC-M-2-x5xmfdc9</guid>
            <pubDate>Sat, 21 May 2022 18:29:06 GMT</pubDate>
            <description><![CDATA[<h2 id="문제">문제</h2>
<p>자연수 N과 M이 주어졌을 때, 아래 조건을 만족하는 길이가 M인 수열을 모두 구하는 프로그램을 작성하시오.</p>
<ul>
<li>1부터 N까지 자연수 중에서 중복 없이 M개를 고른 수열</li>
<li>고른 수열은 오름차순이어야 한다.</li>
</ul>
<pre><code>- 시간 제한 : 1초
- 메모리 제한 : 512MB</code></pre><h2 id="입력">입력</h2>
<p>첫째 줄에 자연수 N과 M이 주어진다. (1 ≤ M ≤ N ≤ 8)</p>
<h2 id="출력">출력</h2>
<p>한 줄에 하나씩 문제의 조건을 만족하는 수열을 출력한다. 중복되는 수열을 여러 번 출력하면 안되며, 각 수열은 공백으로 구분해서 출력해야 한다.</p>
<p>수열은 사전 순으로 증가하는 순서로 출력해야 한다.</p>
<h2 id="접근-방법">접근 방법</h2>
<p>M과 N의 최대가 8이므로 오름차순으로 나열하는 조건을 제외한 최악의 경우를 고려하더라도 8! = 40,320가지의 경우를 탐색하면 되므로 시간 제한내에 해결이 가능합니다.</p>
<h2 id="코드">코드</h2>
<pre><code class="language-java">import java.util.Scanner;

public class Main {

    private static int n;
    private static int m;
    private static int[] selected;

    private static StringBuilder sb = new StringBuilder();

    public static void main(String[] args) {
        input();
        func(1, 1);
        System.out.println(sb);
    }

    /**
     * rec번째 숫자를 [start, n]의 범위에서 고른다.
     * 성공적으로 모든 자리의 숫자를 고르면
     * StringBuilder를 이용하여 결과를 저장한다.
     * @param rec 숫자를 정할 자릿수
     * @param start 고를 숫자의 시작 범위
     */
    static void func(int rec, int start) {
        // 모든 자리 숫자를 골랐을 경우
        if (rec == m + 1) {

            for (int i : selected) {
                sb.append(i).append(&quot; &quot;);
            }

            sb.append(&#39;\n&#39;);
            return;
        }

        for (int i = start; i &lt;= n; ++i) {
            selected[rec - 1] = i;

            // 다음 자리 숫자를 고른다.
            func(rec + 1, i + 1);
        }
    }

    private static void input() {
        Scanner scanner = new Scanner(System.in);

        n = scanner.nextInt();
        m = scanner.nextInt();
        selected = new int[m];

        scanner.close();
    }
}
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[BOJ] 2230번 : 수 고르기]]></title>
            <link>https://velog.io/@errored_pasta/BOJ-2230%EB%B2%88-%EC%88%98-%EA%B3%A0%EB%A5%B4%EA%B8%B0</link>
            <guid>https://velog.io/@errored_pasta/BOJ-2230%EB%B2%88-%EC%88%98-%EA%B3%A0%EB%A5%B4%EA%B8%B0</guid>
            <pubDate>Sat, 21 May 2022 17:25:40 GMT</pubDate>
            <description><![CDATA[<h2 id="문제">문제</h2>
<p>N개의 정수로 이루어진 수열 A[1], A[2], …, A[N]이 있다. 이 수열에서 두 수를 골랐을 때(같은 수일 수도 있다), 그 차이가 M 이상이면서 제일 작은 경우를 구하는 프로그램을 작성하시오.</p>
<p>예를 들어 수열이 {1, 2, 3, 4, 5}라고 하자. 만약 M = 3일 경우, 1 4, 1 5, 2 5를 골랐을 때 그 차이가 M 이상이 된다. 이 중에서 차이가 가장 작은 경우는 1 4나 2 5를 골랐을 때의 3이 된다.</p>
<pre><code>- 시간 제한 : 2초
- 메모리 제한 : 128MB</code></pre><h2 id="입력">입력</h2>
<p>첫째 줄에 두 정수 N, M이 주어진다. 다음 N개의 줄에는 차례로 A[1], A[2], …, A[N]이 주어진다.</p>
<h2 id="출력">출력</h2>
<p>첫째 줄에 M 이상이면서 가장 작은 차이를 출력한다. 항상 차이가 M이상인 두 수를 고를 수 있다.</p>
<h2 id="제한">제한</h2>
<ul>
<li>1 ≤ N ≤ 100,000</li>
<li>0 ≤ M ≤ 2,000,000,000</li>
<li>0 ≤ |A[i]| ≤ 1,000,000,000</li>
</ul>
<h2 id="접근-방법">접근 방법</h2>
<p>두 수의 차이 중 M 이상이면서 최소를 구하는 문제입니다. 배열로 받은 입력에서 두 수를 나타내는 pointer들을 설정하여 배열을 탐색하며 구할 수 있으므로 two pointers를 이용하여 문제를 해결할 수 있습니다.</p>
<p>하나의 pointer는 작은 수를 가르키게하고 다른 pointer는 큰 수를 가르키게하여 차이가 M 이상이면 작은 수를 가르키는 pointer가 더 큰 수를 가르키게 하고, M보다 크면 큰 수를 가르키는 pointer가 더 큰 수를 가르키게 하여야 합니다. 그러기 위해서는 먼저 배열을 정렬하여야 합니다.</p>
<pre><code class="language-kotlin">// 두 수의 차이가 m 이상 일 경우
if (diff &gt;= m) {
    // result보다 작으면 result를 update
    if (diff &lt; result) {
        result = diff
    }

    // 차이가 m과 같으면 더이상 탐색은 무의미
    if (diff == m) return

    // 차이가 더 작아야 하므로 left가 더 큰 수를 가르키게 한다.
    ++left
} else {
    // 더 이상 차이를 구할 수 없으므로 return
    if (right == n - 1) return

    // 차이가 더 커야 하므로 right가 더 큰 수를 가르키게 한다.
    ++right
}</code></pre>
<p>이 방법으로 문제를 해결하면 정렬에 O(N log N), two pointers 이용하여 탐색에 O(N)의 시간을 소요하여 문제를 해결할 수 있습니다.</p>
<h2 id="코드">코드</h2>
<pre><code class="language-kotlin">import java.util.*
import java.lang.*

var n = 0
var m = 0

var _numbers: IntArray? = null
val numbers get() = _numbers!!

var result = 2_000_000_000

fun main() {
    input()
    func()
    println(result)
}

fun input() {
    val sc = Scanner(System.`in`)

    n = sc.nextInt()
    m = sc.nextInt()

    _numbers = IntArray(n) {
        sc.nextInt()
    }

    sc.close()
}

fun func() {
    Arrays.sort(numbers)

    var left = 0 // 작은 수를 가르킬 pointer
    var right = 1 // 큰 수를 가르킬 pointer

    while (true) {
        val diff = numbers[right] - numbers[left]

        // 두 수의 차이가 m 이상 일 경우
        if (diff &gt;= m) {
            // result보다 작으면 result를 update
            if (diff &lt; result) {
                result = diff
            }

            // 차이가 m과 같으면 더이상 탐색은 무의미
            if (diff == m) return

            // 차이가 더 작아야 하므로 left가 더 큰 수를 가르키게 한다.
            ++left
        } else {
            // 더 이상 차이를 구할 수 없으므로 return
            if (right == n - 1) return

            // 차이가 더 커야 하므로 right가 더 큰 수를 가르키게 한다.
            ++right
        }
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Design Principle] Interface Segregation Principle]]></title>
            <link>https://velog.io/@errored_pasta/Design-Principle-Interface-Segregation-Principle</link>
            <guid>https://velog.io/@errored_pasta/Design-Principle-Interface-Segregation-Principle</guid>
            <pubDate>Sat, 21 May 2022 17:23:24 GMT</pubDate>
            <description><![CDATA[<h2 id="정의">정의</h2>
<blockquote>
<p>Clients should not be forced to depend on methods that they do not use.</p>
</blockquote>
<p>&quot;Client는 사용하지 않는 method에 강제로 의존해서는 안된다.&quot;가 interface segregation principled(이하 ISP)의 정의입니다.</p>
<h2 id="atm-ui">ATM UI</h2>
<p>은행 ATM이 있다고 가정해봅시다. 은행 ATM의 주요 기능은 예금, 인출, 송금이 있고 각각의 transaction이 존재합니다. 각각의 기능마다 고객에게 금액을 요청을 해야하고 인출같은 경우 계좌에 있는 금액보다 더 많은 금액을 요청할 경우 잔액이 부족하다고 알려야 합니다.</p>
<p><img src="https://velog.velcdn.com/images/errored_pasta/post/f39c4dd2-5197-45e1-ae8c-9e63cab62eaa/image.png" alt=""></p>
<p>위와 같이 각각의 transaction이 UI interface를 사용하여 고객에게 금액을 요청하거나 잔액 부족을 알리게 됩니다. 하지만 이와 같이 UI interface 하나에 모든 기능을 정의하게 되면 interface가 너무 커지게 됩니다. 이와 같은 interface를 fat interface라 부릅니다.</p>
<h2 id="fat-interface">Fat Interface</h2>
<p>Fat interface는 아래와 같은 문제점들을 가지게 됩니다.</p>
<ul>
<li>Interface를 수정할 경우 해당 interface를 사용하는 모든 클래스들을 모두 수정해야 한다.</li>
<li>Interface에 새로운 method를 정의할 경우 기존에 해당 interface를 사용하는 클래스에 영향을 끼치게 된다.</li>
<li>필요없는 method를 구현해야하는 경우가 발생한다.</li>
</ul>
<p>위의 ATM 예제에서 이러한 문제들을 해결하기 위해 interface를 transaction들에 필요한 기능들만 가지도록 나눌필요가 있습니다.</p>
<p><img src="https://velog.velcdn.com/images/errored_pasta/post/877dcdc8-bde5-44e2-9bd7-20071f0b02ad/image.png" alt=""></p>
<p>Interface를 transaction에 맞게 나누게 되면 transaction의 UI 동작을 수정하더라도 서로 영향을 끼치지 않고 새로운 기능의 UI를 추가하기 쉬워집니다.</p>
<h2 id="reference">Reference</h2>
<p>[1] Robert Martin, <em>Agile software development: principles, patterns, and practices</em> (n.p.: Pearson, 2013), 135-145.</p>
<p>[2] Robert Martin, <em>Clean Architecture: A Craftsman’s Guide to Software Structure and Design</em> (n.p.: Prentice Hall, 2017), 83-86.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Design Principle] Liskov Substitution Principle]]></title>
            <link>https://velog.io/@errored_pasta/Design-Principle-Liskov-Substitution-Principle</link>
            <guid>https://velog.io/@errored_pasta/Design-Principle-Liskov-Substitution-Principle</guid>
            <pubDate>Wed, 18 May 2022 14:30:39 GMT</pubDate>
            <description><![CDATA[<h2 id="정의">정의</h2>
<blockquote>
<p>What is wanted here is something like the following substitution property: If
for each object o1 of type S there is an object o2 of type T such that for all
programs P defined in terms of T, the behavior of P is unchanged when o1 is
substituted for o2 then S is a subtype of T.</p>
</blockquote>
<p>&quot;각각의 S타입의 object o1에 대해, T타입의 object o2가 존재하고, T에 대해 정의된 모든 프로그램 P에서 o1이 o2로 치환될 때, P의 동작이 변하지 않으면 S는 T의 subtype이다.&quot;가 Liskov substitution principle(이하 LSP)의 정의입니다. 이를 간단히 요약해보면 아래와 같습니다.</p>
<blockquote>
<p>Subtypes must be substitutable for thier base types.</p>
</blockquote>
<p>&quot;Subtype은 자신의 base type에 치환 가능해야 한다.&quot;가 간단히 요약한 LSP의 정의입니다.</p>
<h3 id="rectangle과-square">Rectangle과 Square</h3>
<p>LSP의 유명한 예제로 Rectangle과 Square 예제가 있습니다.
정사각형은 직사각형의 특수한 형태로 모든 변의 길이가 같은 정사각형입니다. 그러므로 정사각형과 직사각형을 나타내는 클래스를 생성할 때 아래와 같이 Square 클래스가 Rectangle 클래스를 상속받도록 정의할 수 있습니다.</p>
<p><img src="https://velog.velcdn.com/images/errored_pasta/post/b80c2326-c53b-44f3-9867-e211dd85b9be/image.png" alt=""></p>
<p>정사각형은 높이와 밑변의 길이가 항상 같으므로 Rectangle의 setHeight와 setWidth를 override하여 width와 height가 동시에 변경되도록 정의하면 다음과 같습니다.</p>
<pre><code class="language-java">class Square extends Rectangle {
    public Square(int side) {
        super(side, side);
    }

    @Override
    public void setHeight(int height) {
        super.setHeight(height);
        super.setWidth(height);
    }

    @Override
    public void setWidth(int width) {
        super.setWidth(width);
        super.setHeight(width);
    }
}</code></pre>
<p>아직까진 별 문제가 없어보입니다. 하지만 아래와 같이 Rectangle 클래스를 parameter로 받아 사용하는 클래스가 있다고 가정해봅시다.</p>
<pre><code class="language-java">class RectangleConsumer {
    public void assertArea(Rectangle r) {
        r.setWidth(5);
        r.setHeight(2);

        assertThat(r.getArea(), is(10)); // assertion error when r is Square
    }
}</code></pre>
<p>assertArea에 Rectangle object를 넘겨주면 아무런 문제가 없지만 Square object를 넘겨주면 width와 height가 동시에 변경되므로 r.getArea()를 호출하면 4를 return하여 문제가 발생하게 됩니다. 그러므로 Rectangle은 자신의 subtype인 Square로 치환이 불가능하며 Square는 적절한 subtype이 아닙니다.</p>
<p>그러면 LSP를 만족하기 위해서는 어떤 것들을 지켜야 하는지 알아보겠습니다.</p>
<h2 id="supertype의-method-동작-보존">Supertype의 method 동작 보존</h2>
<p>Subtype($\sigma$)은 자신의 supertype($\tau$)의 method 동작을 보존해야 합니다. Supertype의 method를 $m_{\tau}$라 하고 이에 해당하는 subtype의 method를 $m_{\sigma}$라 하겠습니다.</p>
<h3 id="signature-rule">Signature Rule</h3>
<h4 id="contravariance-of-arguments">Contravariance of Arguments</h4>
<blockquote>
<p>$m_{\tau}$ and $m_{\sigma}$ have the same number of arguments. If the list of argument types of $m_{\tau}$ is $a_{i}$ and that of $m_{\sigma}$ is $\beta_{i}$, then $\forall i$. $\alpha_{i} \preceq \beta_{i}$.</p>
</blockquote>
<p>$m_{\tau}$와 $m_{\sigma}$는 같은 수의 argument를 가지고 $m_{\sigma}$의 argument들은 $m_{\tau}$의 argument들의 supertype이거나 같은 type이어야 합니다.</p>
<p><img src="https://velog.velcdn.com/images/errored_pasta/post/a7572a10-d627-4590-b0b5-3d89c4dcc388/image.png" alt=""></p>
<p>위와 같이 argument type이 정의되어 있고 $m_{\tau}$가 <code>Argument</code>를 받을 때, $m_{\sigma}$에서 $m_{\tau}$로 받은 <code>Argument</code>를 <code>SuperArgument</code>로 처리는 가능하지만 <code>SubArgument</code>로의 처리는 불가능한 경우가 존재합니다. 그러므로 프로그램의 동작을 보장할 수 없게 됩니다.</p>
<h4 id="covariance-of-result">Covariance of Result</h4>
<blockquote>
<p>Either both $m_{\tau}$ and $m_{\sigma}$ have a result or neither has. If there is a result, let $m_{\tau}$&#39;s result type be a $\alpha$ and $m_{\sigma}$&#39;s be $\beta$. Then $\beta \preceq \alpha$.</p>
</blockquote>
<p>$m_{\tau}$와 $m_{\sigma}$ 둘 다 결과를 가지지 않거나 둘 다 결과를 가져야 합니다. 만약 결과를 가지면 $m_{\sigma}$의 결과는 $m_{\tau}$의 결과의 subtype이거나 같은 type이어야 합니다.</p>
<p><img src="https://velog.velcdn.com/images/errored_pasta/post/35422cbb-8318-4f73-8660-d1ed9f0e7cd3/image.png" alt=""></p>
<p>이는 contravariance of arguments와 마찬가지로 $m_{\sigma}$이 <code>SubResult</code>를 return하면 <code>Result</code>로 처리가 가능하나 <code>SuperResult</code>는 처리할 수 없는 경우가 존재합니다.</p>
<h4 id="exception-rule">Exception Rule</h4>
<blockquote>
<p>The exceptions signaled by $m_{\sigma}$ are contained in the set of exceptions signaled by $m_{\tau}$.</p>
</blockquote>
<p>$m_{\sigma}$에서 발생하는 exception들은 $m_{\tau}$에서 발생하는 exception들에 속해야 합니다. 즉, $m_{\tau}$에서 발생하지 않는 exception을 $m_{\sigma}$에서 throw해서는 안됩니다.
Client는 $m_{\tau}$에서 발생하는 exception들은 예상하여 처리할 수 있지만 그 외의 exception이 발생하게 될 경우 exception을 적절히 처리할 수 없어 프로그램의 동작을 보장할 수 없게됩니다.</p>
<h3 id="methods-rule">Methods Rule</h3>
<ul>
<li><p>Pre-condition
코드를 실행하기 전에 만족해야하는 조건입니다. 만약 pre-condition을 만족하지 못했을 경우, 코드의 올바른 동작을 보장할 수 없습니다.</p>
</li>
<li><p>Post-condition
코드를 실행 후 만족해야하는 조건입니다. 코드의 동작과 정상적인 값을 return하는지 보장해야합니다.</p>
</li>
</ul>
<h4 id="pre-condition-rule">Pre-condition Rule</h4>
<blockquote>
<p>$m_{\tau}.pre[A(x_{pre})/x_{pre}] \Rightarrow m_{\sigma}.pre$</p>
</blockquote>
<p>$m_{\sigma}$의 pre-condition은 $m_{\tau}$의 pre-condition보다 강해져서는 안됩니다. 만약 이를 어기게 되면 client는 $m_{\tau}$의 pre-condition을 예상하고 코드를 작성하게 되는데 $m_{\sigma}$의 pre-condition이 더 강해지면 원래는 정상 동작해야할 코드가 제대로 동작하지 않는 경우가 발생하게 됩니다.</p>
<h4 id="post-condition-rule">Post-condition Rule</h4>
<blockquote>
<p>$m_{\sigma}.post \Rightarrow m_{\tau}.post[A(x_{pre})/x_{pre}, A(x_{post})/x_{post}]$</p>
</blockquote>
<p>$m_{\sigma}$의 post-condition은 $m_{\tau}$의 post-condition보다 약해져서는 안됩니다. 만약 $m_{\sigma}$가 더 약해질 경우 예상치 못한 return 값을 받아 client에서 처리하게 되는 경우가 발생합니다. 이렇게 예상치 못한 return 값을 받을 경우 client에서 해당 값을 어떻게 처리해야할지 모르므로 제대로된 동작을 보장할 수 없습니다.</p>
<h2 id="supertype의-property-보존">Supertype의 property 보존</h2>
<p>Subtype은 supertype의 property를 보존해야 하며 해당 property는 아래와 같습니다.</p>
<ul>
<li><p><strong>Invariant</strong>
Object의 life time동안 계속 만족해야하는 제약사항입니다. Invariant는 주로 software object가 정의되고 나서 해당 object가 현실에서 적용되는 rule을 나타냅니다.</p>
</li>
<li><p><strong>Constraint</strong>
Object-oriented model 혹은 system의 제약사항입니다. Constraint는 클래스 레벨에서 정의되어 있지만 적용은 object 레벨에서 이루어집니다.</p>
</li>
</ul>
<h3 id="invariant-rule">Invariant Rule</h3>
<blockquote>
<p>Subtype invariants ensure supertype invariants
$I_{\sigma} \Rightarrow I_{\tau}[A(x_{\rho})/x_{\rho}]$</p>
</blockquote>
<p>Subtype의 invariant는 supertype의 invariant를 보장해야합니다.</p>
<h3 id="constraint-rule">Constraint Rule</h3>
<blockquote>
<p>Subtype constraints ensure supertype constraints
$C_{\sigma} \Rightarrow C_{\tau}[A(x_{\rho})/x_{\rho}, A(x_{\psi})/x_{\psi}]$</p>
</blockquote>
<p>Subtype의 constraint는 supertype의 constraint를 보장해야합니다.</p>
<h2 id="rectangle과-square의-문제점">Rectangle과 Square의 문제점</h2>
<p>위의 Rectangle과 Square의 예제에서 Square는 Rectangle의 적절한 subtype이 아님을 알았습니다. 그러면 Square에서 어떤점 때문에 문제가 발생했을까요? 그 이유는 Square의 method가 Rectangle의 method보다 post-condition이 더 약하기 때문입니다.</p>
<p>Rectangle의 setWidth의 post-condition은 코드에서 명시하지는 않았지만 아래와 같습니다.</p>
<pre><code class="language-java">// old is the value of the Rectangle before SetWidth is called.
assert((this.width == width) &amp;&amp; (this.height == old.height)); </code></pre>
<p>하지만 Square의 setWidth에서는 height가 width와 동시에 변하므로 Rectangle의 setWidth의 post-condition 중 <code>this.height == old.height</code>가 지켜지지 않는 것을 볼 수 있습니다. 그러므로 Square의 setWidth의 post-condition은 Rectangle의 setWidth의 post-condition보다 약하고 그로 인하여 LSP를 위배하게 된 것입니다.</p>
<h2 id="reference">Reference</h2>
<p>[1] Barbara H. Liskov, Jeannette M. Wing, <em>Behavioral Subtyping Using Invariants and Constraints</em> (n.p.: n.d, 1999), 10.</p>
<p>[2] Robert Martin, <em>Agile software development: principles, patterns, and practices</em> (n.p.: Pearson, 2013), 111-125.</p>
<p>[3] Robert Martin, <em>Clean Architecture: A Craftsman’s Guide to Software Structure and Design</em> (n.p.: Prentice Hall, 2017), 78-82.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Design Principle] Open-Closed Principle]]></title>
            <link>https://velog.io/@errored_pasta/Design-Principle-Open-Closed-Principle</link>
            <guid>https://velog.io/@errored_pasta/Design-Principle-Open-Closed-Principle</guid>
            <pubDate>Tue, 17 May 2022 07:07:50 GMT</pubDate>
            <description><![CDATA[<h2 id="정의">정의</h2>
<blockquote>
<p>A software artifact should be open for extension but closed for modification.</p>
</blockquote>
<p>소프트웨어의 artifact(class, module, function, etc.)는 확장에 대해 open되어 있어야 하지만 수정에 대해서는 closed되어 있어야 하는 것이 open-closed principle(이하 OCP)의 정의 입니다.</p>
<h3 id="open과-closed">Open과 Closed</h3>
<p>정의에서 나온 open과 closed의 의미는 각각 아래와 같습니다.</p>
<ul>
<li><p><strong>Open for extension</strong>
요구 사항의 변경을 만족시키기 위해 module의 동작을 확장할 수 있어야 합니다. 즉, module의 동작을 알맞게 변경할 수 있어야 합니다.</p>
</li>
<li><p><strong>Closed for modification</strong>
Module의 동작을 확장하는 것은 소스 코드 혹은 바이너리 코드를 변경시키지 않습니다.</p>
</li>
</ul>
<p>Open과 closed의 의미가 모순적으로 보입니다. 동작을 수정하기 위해서 소스 코드를 수정하면 closed for modification을 어기게 되고, 소스 코드를 수정하지 못하게 되면 open for extension을 할 수 없게 되어 보입니다.
그렇다면 어떻게 소스 코드 혹은 바이너리 코드를 건드리지 않고 module의 동작을 확장할 수 있을까요?</p>
<h2 id="abstraction">Abstraction</h2>
<p>Abstraction을 이용하여 open for extension과 closed for modification을 지키면서 OCP를 만족할 수 있습니다.</p>
<p><img src="https://velog.velcdn.com/images/errored_pasta/post/24c48c88-cc67-4b50-a3cd-ebca8eb28542/image.png" alt=""></p>
<p>Abstraction을 이용하여 base 클래스를 생성하고 해당 base 클래스를 상속받은 자식 클래스에서 상세 동작들을 구현해줄 수 있습니다. 그래서 기존에 존재하는 구현체 클래스의 소스 코드를 수정하지 않고 새로운 구현체 클래스를 생성하여 손쉽게 module의 동작을 확장시킬수 있게 됩니다.</p>
<h3 id="strategy-pattern">Strategy Pattern</h3>
<p><img src="https://velog.velcdn.com/images/errored_pasta/post/fd306814-13d1-4407-a9f8-6751287e7b96/image.png" alt=""></p>
<p>위와 같이 Client와 Server의 관계가 있다고 가정하겠습니다. 이럴경우 Client가 concrete한 Server 클래스에 dependency를 가지므로 Server가 변경될 때, Client도 변경될 확률이 높습니다.</p>
<p><img src="https://velog.velcdn.com/images/errored_pasta/post/5bc23f58-2d5a-4303-9f5e-9770afb0f90e/image.png" alt=""></p>
<p>이를 OCP를 이용하여 해결할 수 있습니다. Client Interface를 생성하여 Server가 해당 interface를 구현하도록 하고 Server의 동작을 변경해야 할 경우 Client Interface를 구현하는 다른 구현체 클래스를 생성하여 변경할 수 있습니다. 이와 같은 design pattern을 strategy pattern이라 합니다.</p>
<h3 id="template-pattern">Template Pattern</h3>
<p><img src="https://velog.velcdn.com/images/errored_pasta/post/f79baccc-3e60-4895-99a8-fb15e39d3413/image.png" alt=""></p>
<p>만약 base 클래스가 위와 같이 interface가 아니라 동작의 기본적인 흐름을 가지고 있는 클래스인 경우에도 OCP를 이용하여 상세 동작은 해당 클래스를 상속받는 구현체 클래스에서 구현하도록 하는 template pattern을 사용할 수도 있습니다.</p>
<p>OCP는 object-oriented의 polymorphism, design pattern 중 strategy pattern, template pattern과 연관이 있는 것을 알 수 있습니다.</p>
<h2 id="적용-범위">적용 범위</h2>
<p>OCP는 안전하게 구현체를 변경할 수 있도록 해주므로 많은 곳에 적용하는 것이 좋다고 생각할 수도 있습니다. 하지만 이는 바람직하지 않은 생각입니다.</p>
<ul>
<li><p><strong>적절한 abstraction을 생성하기에 비용이 든다.</strong>
OCP를 적용하기 위해서는 적절한 abstraction을 생성하여야 합니다. Abstraction은 생성 및 수정 시 구현체의 생성 및 수정도 이루어져야 하므로 잘못된 abstraction을 생성하여 잦은 수정이 발생할 경우 더 많은 비용이 들게 됩니다. 그러므로 적절한 abstraction을 생성하여야 하는데 이 또한 비용이 드는 작업입니다.</p>
</li>
<li><p><strong>Abstraction은 코드의 복잡성을 증가시킨다.</strong>
Abstraction는 구현체를 캡슐화하므로 구현체가 어떻게 되어있는지 알 수 없는 경우가 존재하여 복잡성을 증가시키게 됩니다. 그러므로 OCP를 필요하지 않은 곳까지 적용하게 되면 코드의 복잡성이 감당하지 못하게 높이질 수 있습니다.</p>
</li>
</ul>
<p>Uncle Bob은 이런 쓸모없는 복잡성을 줄이기 위해 우선 OCP를 적용하지 않고 추후 변화가 발생 시 적절한 abstraction을 생성하여 같은 변화에 대해 유연하게 처리할 수 있도록 하여야 한다고 했습니다.</p>
<h2 id="reference">Reference</h2>
<p>[1] Robert Martin, <em>Agile software development: principles, patterns, and practices</em> (n.p.: Pearson, 2013), 99-110.</p>
<p>[2] Robert Martin, <em>Clean Architecture: A Craftsman’s Guide to Software Structure and Design</em> (n.p.: Prentice Hall, 2017), 69-75.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Design Principle] Single Responsibility Principle]]></title>
            <link>https://velog.io/@errored_pasta/Design-Principle-Single-Responsibility-Principle</link>
            <guid>https://velog.io/@errored_pasta/Design-Principle-Single-Responsibility-Principle</guid>
            <pubDate>Sat, 14 May 2022 03:43:38 GMT</pubDate>
            <description><![CDATA[<h2 id="정의">정의</h2>
<blockquote>
<p>A module should have one, and only one, reason to change.</p>
</blockquote>
<p>하나의 모듈은 단 하나의 변경 사유를 가져야 하는 것이 single responsibility principle(이하 SRP)의 정의입니다.</p>
<h3 id="reason-to-change">Reason to change</h3>
<p>소프트웨어가는 user와 stakeholder에 의해 변하게 됩니다. 즉, user와 stakeholder가 변경 사유가 되며 Uncle Bob은 다수의 user와 stakeholder를 하나의 actor라는 그룹으로 묶어서 다음과 같이 SRP를 재정의 하였습니다.</p>
<blockquote>
<p>A module should be responsible to one, and only one, actor.</p>
</blockquote>
<br>

<h2 id="srp를-어길시-발생할-수-있는-일들">SRP를 어길시 발생할 수 있는 일들</h2>
<h3 id="accidental-duplication">Accidental Duplication</h3>
<p>하나의 클래스에 여러 actor에 responsible할 경우 <strong>actor간 서로 예기치 않은 coupling</strong>이 발생할 수 있습니다.</p>
<p><img src="https://velog.velcdn.com/images/errored_pasta/post/6c8f14e9-ee96-4e68-a22f-9721c1b01f89/image.png" alt=""></p>
<p>위의 그림에서 Employee 클래스는 총 3개의 actor에 대해 responsible한 것을 볼 수 있습니다.</p>
<ul>
<li>calculatePay()는 CFO의 팀에서 사용</li>
<li>reportHours()는 COO의 팀에서 사용</li>
<li>save()는 CTO의 팀 DBA가 사용</li>
</ul>
<p>클래스의 3개의 method 중 calculatePay()와 reportHours()는 둘 다 시간에 관련되어 있어서 아래와 같이 시간을 계산하는 regularHours()를 호출하는 경우가 발생할 수 있습니다.</p>
<p><img src="https://velog.velcdn.com/images/errored_pasta/post/e5e3afad-b9f1-4714-8f15-6cc5582c886c/image.png" alt=""></p>
<p>이런 경우 regularHours()의 변경이 이루어질 경우 calculatePay() 혹은 reportHours()의 동작이 의도치 않게 변경될 수 있습니다.
예를 들어 CFO가 calculatePay()의 변경을 요구하여 regularHours()의 동작까지 수정해야 할 경우 COO의 팀에서 사용하는 reportHours()의 동작까지 변경될 수 있습니다.</p>
<h3 id="merges">Merges</h3>
<p>여러 actor에 responsible한 클래스는 merge가 자주 발생할 수 있습니다.</p>
<p>둘 이상의 actor가 클래스에 대해 수정을 하게 될 경우 해당 클래스에 대해 충돌이 발생하게 되면 수정사항에 대해 merge를 해야합니다.
요즘은 git같은 tool이 merge를 자동으로 처리해주는 경우도 있지만 모든 merge에 대해 처리할 수는 없으므로 manual merge가 일어날 수 있습니다. Manual merge는 사람이 하는 일이므로 실수가 발생할 여지가 있으며 이로인해 잘못 merge를 하게 되면 일부 변경사항이 적용이 되지 않아 클래스의 동작이 올바르지 않게 됩니다.</p>
<p>예를 들어 DBA가 Employee 클래스의 schema를 변경하기 위해 save()를 수정하고, COO팀이 hour report format을 변경하기 위해 reporHours()을 수정하게 되면 충돌이 발생할 수 있고 이는 manual merge로 이어질 수 있습니다.</p>
<h2 id="reference">Reference</h2>
<p>[1] Robert Martin, <em>Agile software development: principles, patterns, and practices</em> (n.p.: Pearson, 2013), 127-134.</p>
<p>[2] Robert Martin, <em>Clean Architecture: A Craftsman’s Guide to Software Structure and Design</em> (n.p.: Prentice Hall, 2017), 61-67.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[BOJ] 3273번 : 두 수의 합]]></title>
            <link>https://velog.io/@errored_pasta/BOJ-3273%EB%B2%88-%EB%91%90-%EC%88%98%EC%9D%98-%ED%95%A9</link>
            <guid>https://velog.io/@errored_pasta/BOJ-3273%EB%B2%88-%EB%91%90-%EC%88%98%EC%9D%98-%ED%95%A9</guid>
            <pubDate>Thu, 12 May 2022 14:56:28 GMT</pubDate>
            <description><![CDATA[<h2 id="코드">코드</h2>
<pre><code class="language-java">import java.lang.*;
import java.util.*;
import java.io.*;

public class Main {

    static int n, x;
    static int[] numbers;
    static int result = 0;

    public static void main(String[] args) {
        input();
        func();
        System.out.println(result);
    }

    static void input() {
        Scanner sc = new Scanner(System.in);

        n = sc.nextInt();
        numbers = new int[n];

        for (int i = 0; i &lt; n; ++i) {
            numbers[i] = sc.nextInt();
        }

        x = sc.nextInt();

        sc.close();
    }

    static void func() {
        Arrays.sort(numbers);

        int left = 0;
        int right = n - 1;

        while (left &lt; right) {
            int sum = numbers[left] + numbers[right];

            if (sum &lt; x) {
                ++left;
            } else {
                if (sum == x) {
                    ++result;
                    ++left;
                }

                --right;
            }
        }
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[BOJ] 2003번 : 수들의 합 2]]></title>
            <link>https://velog.io/@errored_pasta/BOJ-2003%EB%B2%88-%EC%88%98%EB%93%A4%EC%9D%98-%ED%95%A9-2</link>
            <guid>https://velog.io/@errored_pasta/BOJ-2003%EB%B2%88-%EC%88%98%EB%93%A4%EC%9D%98-%ED%95%A9-2</guid>
            <pubDate>Sat, 07 May 2022 15:44:16 GMT</pubDate>
            <description><![CDATA[<h2 id="문제">문제</h2>
<p>N개의 수로 된 수열 A[1], A[2], …, A[N] 이 있다. 이 수열의 i번째 수부터 j번째 수까지의 합 A[i] + A[i+1] + … + A[j-1] + A[j]가 M이 되는 경우의 수를 구하는 프로그램을 작성하시오.</p>
<pre><code>- 시간 제한 : 0.5초
- 메모리 제한 : 128MB</code></pre><h2 id="입력">입력</h2>
<p>첫째 줄에 N(1 ≤ N ≤ 10,000), M(1 ≤ M ≤ 300,000,000)이 주어진다. 다음 줄에는 A[1], A[2], …, A[N]이 공백으로 분리되어 주어진다. 각각의 A[x]는 30,000을 넘지 않는 자연수이다.</p>
<h2 id="출력">출력</h2>
<p>첫째 줄에 경우의 수를 출력한다.</p>
<h2 id="접근-방법">접근 방법</h2>
<h2 id="코드">코드</h2>
<pre><code class="language-java">import java.lang.*;
import java.util.*;
import java.io.*;

public class Main {

    static int result = 0;
    static int n, m;
    static int[] numbers;

    public static void main(String[] args) {
        input();
        func();
        System.out.println(result);
    }

    static void input() {
        Scanner sc = new Scanner(System.in);

        n = sc.nextInt();
        m = sc.nextInt();
        numbers = new int[n];

        for (int i = 0; i &lt; n; ++i) {
            numbers[i] = sc.nextInt();
        }

        sc.close();
    }

    static void func() {
        int left = 0;
        int right = 1;
        int sum = numbers[0];

        for (;;) {
            // 합이 m보다 작을 경우
            if (sum &lt; m) {
                // 더 이상 가능한 경우가 없으므로 return
                if (right == n) return;

                // 배열의 원소 하나를 추가 시킨다.
                sum += numbers[right++];

            // 합이 m보다 크거나 같을 경우
            } else {
                // m과 같으면 result를 증가
                if (sum == m) ++result;

                // 배열의 원소 하나를 제외 시킨다.
                sum -= numbers[left++];
            }
        }
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[BOJ] 2470번 : 두 용액]]></title>
            <link>https://velog.io/@errored_pasta/BOJ</link>
            <guid>https://velog.io/@errored_pasta/BOJ</guid>
            <pubDate>Sat, 07 May 2022 15:38:47 GMT</pubDate>
            <description><![CDATA[<h2 id="문제">문제</h2>
<p>KOI 부설 과학연구소에서는 많은 종류의 산성 용액과 알칼리성 용액을 보유하고 있다. 각 용액에는 그 용액의 특성을 나타내는 하나의 정수가 주어져있다.  산성 용액의 특성값은 1부터 1,000,000,000까지의 양의 정수로 나타내고, 알칼리성 용액의 특성값은 -1부터 -1,000,000,000까지의 음의 정수로 나타낸다.</p>
<p>같은 양의 두 용액을 혼합한 용액의 특성값은 혼합에 사용된 각 용액의 특성값의 합으로 정의한다. 이 연구소에서는 같은 양의 두 용액을 혼합하여 특성값이 0에 가장 가까운 용액을 만들려고 한다. </p>
<p>예를 들어, 주어진 용액들의 특성값이 [-2, 4, -99, -1, 98]인 경우에는 특성값이 -99인 용액과 특성값이 98인 용액을 혼합하면 특성값이 -1인 용액을 만들 수 있고, 이 용액이 특성값이 0에 가장 가까운 용액이다. 참고로, 두 종류의 알칼리성 용액만으로나 혹은 두 종류의 산성 용액만으로 특성값이 0에 가장 가까운 혼합 용액을 만드는 경우도 존재할 수 있다.</p>
<p>산성 용액과 알칼리성 용액의 특성값이 주어졌을 때, 이 중 두 개의 서로 다른 용액을 혼합하여 특성값이 0에 가장 가까운 용액을 만들어내는 두 용액을 찾는 프로그램을 작성하시오.</p>
<pre><code>- 시간 제한 : 1초
- 메모리 제한 : 128MB</code></pre><h2 id="입력">입력</h2>
<p>첫째 줄에는 전체 용액의 수 N이 입력된다. N은 2 이상 100,000 이하이다. 둘째 줄에는 용액의 특성값을 나타내는 N개의 정수가 빈칸을 사이에 두고 주어진다. 이 수들은 모두 -1,000,000,000 이상 1,000,000,000 이하이다. N개의 용액들의 특성값은 모두 다르고, 산성 용액만으로나 알칼리성 용액만으로 입력이 주어지는 경우도 있을 수 있다.</p>
<h2 id="출력">출력</h2>
<p>첫째 줄에 특성값이 0에 가장 가까운 용액을 만들어내는 두 용액의 특성값을 출력한다. 출력해야 하는 두 용액은 특성값의 오름차순으로 출력한다. 특성값이 0에 가장 가까운 용액을 만들어내는 경우가 두 개 이상일 경우에는 그 중 아무것이나 하나를 출력한다.</p>
<h2 id="접근-방법">접근 방법</h2>
<p>해당 문제의 모든 경우의 수를 살펴보려면 O(N^2)의 시간이 필요하며 최악의 경우 N = 100,000이므로 시간 제한을 초과하게 됩니다. 두 수를 더하여 0에 가까운 결과를 얻으려면 입력받은 용액을 정렬하여 가장 큰 수(<code>right</code>)와 가장 작은 수(<code>left</code>)를 더하고 합이 0보다 작으면 <code>left</code>다음의 수를 선택하여 합이 더 커지게 하고, 합이 0보다 크면 <code>right</code> 이전의 수를 선택하여 합이 더 작아지게 하는 것을 반복하여 합이 0에 가장 가까운 수들을 선택하면 됩니다. 이렇게 문제를 해결하면 O(N)의 시간에 문제를 해결하는 것이 가능합니다.</p>
<h2 id="코드">코드</h2>
<pre><code class="language-python">import sys

def get_input() : 
    global n, solutions

    input = sys.stdin.readline

    n = int(input())
    solutions = sorted(list(map(int, input().split(&quot; &quot;))))

def func() : 
    global resultLeft, resultRight

    absolutePh = 2_000_000_000
    left, right = 0, n - 1

    while left &lt; right : 
        _sum = solutions[left] + solutions[right]

        # 두 용액의 합의 절댓값이 최솟값일 경우
        if abs(_sum) &lt; absolutePh : 
            resultLeft, resultRight = left, right
            absolutePh = abs(_sum)

        # 합이 0보다 작으면 작은 수를 선택해서 확인
        if _sum &lt; 0 : 
            left += 1

        # 합이 0보다 크면 right 이전의 수를 선택해서 확인
        elif _sum &gt; 0 : 
            right -= 1

        # 합이 0이면 중단
        else : 
            break

if __name__ == &quot;__main__&quot; : 
    get_input()
    func()
    print(f&quot;{solutions[resultLeft]} {solutions[resultRight]}&quot;)</code></pre>
<h2 id="주의할-점">주의할 점</h2>
<p>Java로 해당 문제를 풀 경우, <code>Scanner</code>를 이용하여 입력을 받으면 시간 초과가 발생합니다. 그러므로 <code>BufferedReader</code>와 <code>StringTokenizer</code>를 이용하여 입력을 받아서 처리해야합니다.</p>
]]></description>
        </item>
    </channel>
</rss>