LoginSignup
7
10

More than 5 years have passed since last update.

AndroidのActivity、Fragmentのビルダーパターンを考え直す in Kotlin

Last updated at Posted at 2016-12-16

いつもActivityやFragmentのビルダーパターンを作っていると不便だと感じる点も少なくないです、特に

現状のビルダーパターン

ActivityやFragmentなど、値を受け取る場合はStaticな関数を作って受け取る値の型などを明示することが、一般的なパターンとなっています。
また、こうするとKEYの値なども1つのファイル内のみで完結します。

Activity

Activityの場合は、Intentの作成時に、このビルダーパターンを使用します。

SampleActivity.kt
class SampleActivity : Activity() {

    companion object {
        private const val KEY_SAMPLE = "sample"

        fun createIntent(context: Context, sample: Sample): Intent =
            Intent(context, SampleActivity::class.java).apply {
                putExtra(KEY_SAMPLE, sample)
            }
    }

    //// パースなど

}

Fragment

Fragmentの場合は、インスタンスの作成時に、このビルダーパターンを使用します。

SampleFragment.kt
class SampleFragment : Fragment() {

    companion object {
        private const val KEY_SAMPLE = "sample"

        fun createInstance(sample: Sample): SampleFragment =
            SampleFragment().apply {
                arguments = Bundle().apply {
                    putSerializable(KEY_SAMPLE, sample)
                }
            }
    }

    //// パースなど

}

問題点

それぞれのFragmentの機能などが1つのClass内に閉じ込められて、お互いが疎な状態となっています。しかし、ActivityでもSampleを使用し、FragmentでもSampleを使用する場合や、ただFragmentに値を渡す場合など同じKeyを定義しなくてはいけず冗長になりがちです。

せめて、Keyの値だけでも、共有したいですよね。

新しいビルダーパターン

新しいパターンでは、KotlinのExtentionを使用します。なので、JavaではUtilクラスを作ることになってしまいますが、そこは置き換えて考えてください。
方針としては、ActivityとFragment共通で1つのConstantsファイルを持つようにし、Constantsファイルが全ての依存を受け持つようにします。

SampleActivity.kt
class SampleActivity : Activity() {

    companion object;

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val sample: Sample = parseSample()
    }

}
SampleFragment.kt
class SampleFragment : Fragment() {

    companion object;

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val sample: Sample = parseSample()

    }

}
SampleConstants.kt
// Scene Key
private object Key {
    const val SAMPLE = "sample"
}

// Instance Builder
fun SampleActivity.Companion.createIntent(context: Context, sample: Sample): Intent =
        Intent(context, SampleActivity::class.java).apply {
            putExtra(Key.SAMPLE, sample)
        }

fun SampleFragment.Companion.createInstance(sample: Sample): SampleFragment =
        SampleFragment().apply {
            arguments = Bundle().apply {
                putSerializable(Key.SAMPLE, sample)
            }
        }

// Parser
fun SampleActivity.parseSample(): Sample =
         intent.getSerializableExtra(Key.SAMPLE) as Sample

fun SampleFragment.parseSample(): Sample =
         arguments.getSerializable(Key.SAMPLE) as Sample

簡単に解説をすると、まずKeyの値を定義します。この時、privateにするとファイルプライベートとなるため、他のクラスからは参照できません。
次に、それぞれのActivityとFragmentのCompanionObjectにExtentionで、ビルダーメソッドを作成します。
最後に、Parserを使用して、値のパースをします。

More Tips

今回はキーの値が増えるためやりませんでしたが、ただFragmentに値を渡すだけであればActivityに渡す値もBundleとしてあげることで、Parserとビルダーの一部を共通化出来ます。
いままでだと、ActivityにBundleで値を渡した際に、値を使用したくなった際にFragment側を見に行く必要があり、参照関係が汚くなりがちでした。
ですが、今回はConstantsが全ての値の参照を管理し、スコープも1つ閉じ込めることが出来ました。

最近の画面ごとに、必要な依存をまとめる設計には相性がいいと思いますので、よければ参考までに試してみてください!

7
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
10