Android 疎結合について

# 概要
疎結合、密結合についてそれぞれ内容を整理する。

:::note info
**この記事を見て理解できること**
* 密結合とは何か
* 疎結合とは何か
* 密結合が良くない理由
:::

# 密結合とは
密結合は、異なるコンポーネント（クラスやモジュール）が強く依存し合っている状態を指します。

## 密結合になっている例
例えば、Recycler Viewを使用してそれぞれのitemでクリックイベントを用意したいケースがあるとします。
また、その場合の密結合になっているコード例は以下です。

```kotlin
class MyAdapter(
    private val items: List<String>, 
    private val fragment: MyFragment // Fragmentに依存
) : RecyclerView.Adapter<MyAdapter.ViewHolder>() {

    class ViewHolder(val binding: ItemViewBinding) : RecyclerView.ViewHolder(binding.root)

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val binding = ItemViewBinding.inflate(LayoutInflater.from(parent.context), parent, false)
        return ViewHolder(binding)
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.binding.textView.text = items[position]

        // Fragmentのメソッドを直接呼び出してクリックイベントを処理
        holder.binding.root.setOnClickListener {
            (fragment as MyFragment).onItemClicked(items[position])
        }
    }

    override fun getItemCount(): Int = items.size
}
```

```kotlin
class MyFragment : Fragment() {

    private lateinit var adapter: MyAdapter
    private var _binding: FragmentMyBinding? = null
    private val binding get() = _binding!!

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        _binding = FragmentMyBinding.inflate(inflater, container, false)

        val items = listOf("Item 1", "Item 2", "Item 3")

        // Fragment自体をAdapterに渡してインスタンス化（密結合）
        adapter = MyAdapter(items, this)
        binding.recyclerView.adapter = adapter

        return binding.root
    }

    // Adapterから直接呼ばれるメソッド
    fun onItemClicked(item: String) {
        println("Tight Coupling")
    }
}

```

上記からわかる通り、AdapterがFragmentに直接依存しており、Adapterの内部でFragmentのメソッドを直接呼び出してます。
これによる弊害については例えば以下が挙げられます。
* Fragmentの`onItemClicked()`を変更した時、Adapter側も修正が必要になる
* 違うFragmentで`MyAdapter`を使用する場合、都度、`setOnClickListner`を修正しなければならない

このことから、密結合は<font color="red">**再利用性の低下**</font>につながります。

# 疎結合とは
疎結合とは「コンポーネント同士の結合が緩く、独立している」ことを指します。実装をイメージすると「実態を知らなくても利用できる」状態のことを指します。
　→ここでのコンポーネント同士の結合がゆるいとは実装に依存せず、インターフェースに依存する結合関係のことを指します。

## 疎結合できている例
上記の例を疎結合する場合、以下のコードになります。

```kotlin
interface OnItemClickListener {
    fun onItemClicked(item: String)
}
```

```kotlin
class MyAdapter(
    private val items: List<String>, 
    private val listener: OnItemClickListener // インターフェースを通じてイベントを通知
) : RecyclerView.Adapter<MyAdapter.ViewHolder>() {

    class ViewHolder(val binding: ItemViewBinding) : RecyclerView.ViewHolder(binding.root)

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val binding = ItemViewBinding.inflate(LayoutInflater.from(parent.context), parent, false)
        return ViewHolder(binding)
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.binding.textView.text = items[position]

        // インターフェースを使用してクリックイベントを外部に通知
        holder.binding.root.setOnClickListener {
            listener.onItemClicked(items[position])
        }
    }

    override fun getItemCount(): Int = items.size
}
```

```kotlin
class MyFragment : Fragment(), OnItemClickListener {

    private lateinit var adapter: MyAdapter
    private var _binding: FragmentMyBinding? = null
    private val binding get() = _binding!!

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        _binding = FragmentMyBinding.inflate(inflater, container, false)

        val items = listOf("Item A", "Item B", "Item C")

        // Fragmentがインターフェースを実装し、Adapterに渡す（疎結合）
        adapter = MyAdapter(items, this)
        binding.recyclerView.adapter = adapter

        return binding.root
    }

    // インターフェースのメソッドを実装
    override fun onItemClicked(item: String) {
        println("Tight Coupling")
    }

    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }
}
```

上記では、AdapterとFragmentがインターフェースを介してやり取りしているため、疎結合になっており、双方を独立して変更可能になってます。


# 密結合が良くない理由
上記の説明でもありましたが、密結合によるデメリットをまとめると以下が挙げられます。

:::note warn
**密結合のデメリット**

* **メンテナンスが困難になる**
    * モジュール同士が強く依存しているため、1つのコンポーネントに変更を加えると他の部分にも影響が及びやすい
* **再利用性の低下**
* **柔軟性と拡張性の欠如**
    * システムやアプリケーションの一部を変更、拡張したり、新機能を追加する場合、密結合された部分は他の部分に大きく依存しているため、簡単に拡張することが難しい(例に記載した通り)
* **単体テストが難しくなる**
    * コンポーネントが互いに依存し合うと、単独でのテストが難しくなり、テスト範囲が広がるため、テストの複雑性が増す
:::

# まとめ
今回DIの説明等はなかったですが、密結合を避けるために、設計時には「疎結合」（loose coupling）を目指し、以下のアプローチを採用することが重要です。

* 依存性注入（Dependency Injection）
* インターフェースや抽象クラスの使用(今回の内容)
* モジュール化やコンポーネントベースの設計