MVPでRecyclerViewをいい感じに使いたい

## 概要

MVPアーキテクチャでRecyclerViewを使用したときに、
データ管理の方法に悩みました。
そのときの対応方法を書きます。

## 背景

RecyclerViewを使用するときに、
RecyclerView.Adapter内でデータを保持する実装が一般的だと思います。

ただ、RecyclerView.Adapterはクラス名からもわかるように、Viewに属するクラスです。
MVPで言えば、Vです。
VにMを持たせて操作させたくありません。
PがMを持ってVの更新をしたいです。

それをするために少し奮闘しました。

## どうやって乗り切ったのか

### RecyclerView.Adapterの実装

ほぼ空っぽに。

```SampleListAdapter.kt
class SampleListAdapter(
        private val contentManager: ContentManager,
        private val contentViewCreator: ContentViewCreator,
        private val bindListener: OnBindListener
) : RecyclerView.Adapter<ViewHolder>() {

    interface ContentManager {
        fun getItemCount(): Int
        fun getItemViewType(position: Int): Int
    }

    interface ContentViewCreator {
        fun createContentView(context: Context): SampleContentView
        fun createCampaignContentView(context: Context): CampaignContentView
    }

    interface OnBindListener {
        fun onBindContentView(holder: SampleViewHolder, position: Int)
        fun onBindCampaignContentView(holder: CampaignViewHolder, position: Int)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
            when (findByViewType(viewType)) {
                default -> SampleViewHolder(contentViewCreator.createContentView(parent.context))
            }

    override fun getItemCount(): Int = contentManager.getItemCount()

    override fun getItemViewType(position: Int) = contentManager.getItemViewType(position)

    override fun onBindViewHolder(holder: ViewHolder, position: Int) =
            when (holder) {
                is SampleViewHolder -> bindListener.onBindContentView(holder, position)
                else -> throw IllegalArgumentException()
            }
}
```

### Viewの実装

ちょっとボリューミーだけど、こんな感じ。

```SampleFragment.kt
interface SampleView : BaseView {
    fun notifyData()
    fun setDataCount(count: Int)
    fun onBindContentViewHolder(holder: SampleViewHolder, model: SampleModel, position: Int)
}

class SampleFragment : Fragment, SampleView {

    fun setAdapter() {
        recyclerView.adapter = SampleListAdapter(
            object : SampleListAdapter.ContentManager {
                override fun getItemCount(): Int {
                    return presenter.getItemCount()
                }

                override fun getItemViewType(position: Int): Int {
                    return presenter.getViewType(position)
                }
            },
            object : SampleListAdapter.ContentViewCreator {
                override fun createContentView(context: Context): SampleContentView {
                    return SampleContentView(context)
                }
            },
            object : SampleListAdapter.OnBindListener {
                override fun onBindContentView(holder: SampleViewHolder, position: Int) {
                    presenter.onBindContentView(holder, position)
                }
            }
        )
    }
}
```

### Presenterの実装

データが必要なところだけ、Presenterに返すようにする。

PresenterにViewHolderへの参照が入ってしまうのは致し方なし...
getter追加も苦肉の策...

```SamplePresenter.kt
interface SamplePresenter : BasePresenter<SampleView> {
    fun getItemCount(): Int
    fun getViewType(position: Int): Int
    fun onBindContentView(viewHolder: SampleViewHolder, position: Int)
}
```

## 終わりに

若干の無茶はしつつも..
目的の **PがMを持ってVの更新をしたい** がRecylerViewでできました。

結構ざっくりとしかコードを載せていので、
これだけを見ると意味がわからない人も多くいる気がします。

もし要望が多ければ、サンプルプロジェクトを作って
GitHub にアップするかも..しないかも..しれないので、コメントいただければと。