いつもActivityやFragmentのビルダーパターンを作っていると不便だと感じる点も少なくないです、特に
現状のビルダーパターン
ActivityやFragmentなど、値を受け取る場合はStaticな関数を作って受け取る値の型などを明示することが、一般的なパターンとなっています。
また、こうするとKEYの値なども1つのファイル内のみで完結します。
Activity
Activityの場合は、Intentの作成時に、このビルダーパターンを使用します。
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の場合は、インスタンスの作成時に、このビルダーパターンを使用します。
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ファイルが全ての依存を受け持つようにします。
class SampleActivity : Activity() {
companion object;
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val sample: Sample = parseSample()
}
}
class SampleFragment : Fragment() {
companion object;
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val sample: Sample = parseSample()
}
}
// 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つ閉じ込めることが出来ました。
最近の画面ごとに、必要な依存をまとめる設計には相性がいいと思いますので、よければ参考までに試してみてください!