2017年9月18日月曜日

Kotlin メモ : Class Delegation を使って Adapter の処理を委譲する

ベースクラスの異なる 2つの Adapter があります

1. ArrayAdapter を継承した FavoriteAdapter
  • FavoriteAdapter は任意の型のデータを取りうる
  • その型のデータに対する date(T), balance(T) の実装が必要
abstract class FavoriteAdapter<T>(context: Context, objects: List<T>) : ArrayAdapter<T>(context, 0, objects) { abstract fun date(data: T): String abstract fun balance(data: T): Int private val inflater = LayoutInflater.from(context) override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { val view: View = convertView ?: ItemViewHolder .create(inflater, parent) .also { it.view.tag = it } .view getItem(position)?.let { (view.tag as ItemViewHolder).bind(date(it), balance(it)) } return view } } 2. CursorAdapter を継承した HistoryAdapter
  • CursorAdapter は任意の型のデータを取りうる
  • その型のデータに対する date(T), balance(T) の実装が必要
  • BaseData からその型のデータに変換するメソッドの実装が必要
abstract class HistoryAdapter<T>(context: Context) : CursorAdapter(context, null, 0) { abstract fun date(data: T): String abstract fun balance(data: T): Int abstract fun create(data: BaseData): T private val inflater: LayoutInflater = LayoutInflater.from(context) override fun newView(context: Context, c: Cursor, parent: ViewGroup) { return ItemViewHolder.create(inflater, parent).also { it.view.tag = it }.view } override fun bindView(view: View, context: Context, c: Cursor) { val baseData = convert(c) val data = create(baseData) (view.tag as ItemViewHolder).bind(date(data), balance(data)) } private fun convert(c: Cursor): BaseData { ... } }

Delegation なし

データとして MyData をとる Adapter を用意してみましょう。 class MyDataAdapter(context: Context, objects: List<MyData>) : FavoriteAdapter<MyData>(context, objects) { override fun date(data: MyData) = DateFormat.format(context.getString(R.string.format_date), data.getDate()).toString() override fun balance(data: MyData) = data.getBalance() } class MyDataAdapter(context: Context) : HistoryAdapter<MyData>(context) { override fun date(data: MyData) = DateFormat.format(context.getString(R.string.format_date), data.getDate()).toString() override fun balance(data: MyData) = data.getBalance() override fun create(data: BaseData): MyData = MyData(data) } val adapter: FavoriteAdapter<*> = MyDataAdapter(context, list) val adapter: HistoryAdapter<*> = MyDataAdapter(context) FavoriteAdapter を継承した MyDataAdapter と HistoryAdapter を継承した MyDataAdapter をそれぞれ用意しました。しかし2つの Adapter の処理はほぼ同じなので、1つのクラスにまとめるのがよいでしょう。

そこで、まずは通常の Delegation パターンで実装してみます。

Delegation パターン

Adapter<T> インターフェースを用意し、FavoriteAdapter と HistoryAdapter に abstract で定義していたメソッドを Adapter のメソッドに置き換えます。 interface Adapter<T> { fun date(data: T): String fun balance(data: T): Int fun create(data: BaseData): T } class FavoriteAdapter<T>(context: Context, objects: List<T>, private val adapter: Adapter<T>) : ArrayAdapter<T>(context, 0, objects) { fun date(data: T): String = adapter.date(data) fun balance(data: T): Int = adapter.balance(data) ... } class HistoryAdapter<T>(val context: Context, private val adapter: Adapter<T>) : CursorAdapter(context, null, 0) { fun date(data: T): String = adapter.date(data) fun balance(data: T): Int = adapter.balance(data) fun create(data: BaseData): T = adapter.create(data) ... } Adapter を継承した MyDataAdapter を用意します。 class MyDataAdapter(private val context: Context) : Adapter<MyData> { override fun date(data: MyData) = DateFormat.format(context.getString(R.string.format_date3), data.getDate()).toString() override fun balance(data: MyData) = data.getBalance() override fun create(data: BaseData): MyData = MyData(data) } val adapter: FavoriteAdapter<*> = FavoriteAdapter(context, list, MyDataAdapter(context)) val adapter: HistoryAdapter<*> = HistoryAdapter(context, MyDataAdapter(context)) FavoriteAdapter と HistoryAdapter を継承しなくてよくなりました。

一方、FavoriteAdapter と HistoryAdapter の date() や balance() メソッドでは、Adapter のメソッドをそのまま呼び出しているだけです。
Class Delegation を使うと、このような明示的な記述をしなくてよくなります。

Class Delegation

FavoriteAdapter と HistoryAdapter も Adapter<T> を実装し、by を使ってコンストラクタでもらった adapter に処理を委譲します。 class FavoriteAdapter<T>(context: Context, objects: List<T>, private val adapter: Adapter<T>) : ArrayAdapter<T>(context, 0, objects), Adapter<T> by adapter { ... } class HistoryAdapter<T>(val context: Context, private val adapter: Adapter<T>) : CursorAdapter(context, null, 0), Adapter<T> by adapter { ... } Class Delegation により、FavoriteAdapter と HistoryAdapter では date() や balance() の明示的な記述をしなくてよくなりました。


0 件のコメント:

コメントを投稿