2017年9月18日月曜日

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

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

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

Delegation なし

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

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

Delegation パターン

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

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

Class Delegation

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


0 件のコメント:

コメントを投稿