Last active
August 29, 2022 17:26
-
-
Save arkivanov/7b0d20134cb6bdab4786f2002e765fbe to your computer and use it in GitHub Desktop.
Fragment IoC issue
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//region UserFragment module | |
class UserFragment(dataSource: UserDataSource) : Fragment() | |
interface UserDataSource { | |
fun getUser(): User | |
} | |
//endregion UserFragment module | |
//region ParentFragment module, depends on the UserFragment module | |
class ParentFragment : Fragment() { | |
private val fragmentFactory = FragmentFactoryImpl() | |
override fun onCreate(savedInstanceState: Bundle?) { | |
childFragmentManager.fragmentFactory = fragmentFactory | |
super.onCreate(savedInstanceState) | |
} | |
// Call this method when you need to show UserFragment displaying current user's info | |
private fun showCurrentUser() { | |
childFragmentManager.commit { | |
add(R.id.content, fragmentFactory.currentUserFragment()) | |
} | |
} | |
// Call this method when you need to show UserFragment displaying other user's info | |
private fun showOtherUser(id: String) { | |
childFragmentManager.commit { | |
add(R.id.content, fragmentFactory.otherUserFragment(id = id)) | |
} | |
} | |
} | |
internal class FragmentFactoryImpl : FragmentFactory() { | |
override fun instantiate(classLoader: ClassLoader, className: String): Fragment = | |
when (loadFragmentClass(classLoader, className)) { | |
UserFragment::class.java -> | |
// We need somehow distinguish between current and other users here, | |
// and call either currentUserFragment() or otherUserFragment(). | |
// In case of Other User we also need an id. | |
TODO() | |
else -> super.instantiate(classLoader, className) | |
} | |
fun currentUserFragment(): UserFragment = UserFragment(CurrentUserDataSource()) | |
fun otherUserFragment(id: String): UserFragment = UserFragment(OtherUserDataSource(id = id)) | |
} | |
internal class CurrentUserDataSource : UserDataSource { | |
override fun getUser(): User = TODO() // Load current user | |
} | |
internal class OtherUserDataSource(id: String) : UserDataSource { | |
override fun getUser(): User = TODO() // Load other user | |
} | |
//endregion ParentFragment module |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//region UserFragment module | |
class UserFragment(dataSourceFactory: (Mode) -> UserDataSource) : Fragment() { | |
private val mode by lazy { requireArguments().getParcelable<Mode>(KEY_MODE)!! } | |
private val dataSource by lazy { dataSourceFactory(mode) } | |
companion object { | |
private const val KEY_MODE = "MODE" | |
operator fun invoke(mode: Mode, dataSourceFactory: (Mode) -> UserDataSource): UserFragment = | |
UserFragment(dataSourceFactory).apply { | |
arguments = bundleOf(KEY_MODE to mode) | |
} | |
} | |
// This makes the UserFragment aware of the Mode in one dimension - Current/Other. | |
// It also makes impossible to reuse the UserFragment in a different context, | |
// supplying the UserDataSource based on an another dimension (e.g. based on gender, relationship with current user, etc.). | |
// You will need to add more variants here. | |
sealed class Mode : Parcelable { | |
@Parcelize | |
object Current : Mode() | |
@Parcelize | |
data class Other(val id: String) : Mode() | |
} | |
} | |
interface UserDataSource { | |
fun getUser(): User | |
} | |
//endregion UserFragment module | |
//region ParentFragment module, depends on the UserFragment module | |
class ParentFragment : Fragment() { | |
private val fragmentFactory = FragmentFactoryImpl() | |
override fun onCreate(savedInstanceState: Bundle?) { | |
childFragmentManager.fragmentFactory = fragmentFactory | |
super.onCreate(savedInstanceState) | |
} | |
// Call this method when you need to show UserFragment displaying current user's info | |
private fun showCurrentUser() { | |
childFragmentManager.commit { | |
add(R.id.content, fragmentFactory.currentUserFragment()) | |
} | |
} | |
// Call this method when you need to show UserFragment displaying other user's info | |
private fun showOtherUser(id: String) { | |
childFragmentManager.commit { | |
add(R.id.content, fragmentFactory.otherUserFragment(id = id)) | |
} | |
} | |
} | |
internal class FragmentFactoryImpl : FragmentFactory() { | |
override fun instantiate(classLoader: ClassLoader, className: String): Fragment = | |
when (loadFragmentClass(classLoader, className)) { | |
UserFragment::class.java -> UserFragment(::userDataSource) | |
else -> super.instantiate(classLoader, className) | |
} | |
fun currentUserFragment(): UserFragment = UserFragment(UserFragment.Mode.Current, ::userDataSource) | |
fun otherUserFragment(id: String): UserFragment = UserFragment(UserFragment.Mode.Other(id), ::userDataSource) | |
private fun userDataSource(mode: UserFragment.Mode): UserDataSource = | |
when (mode) { | |
is UserFragment.Mode.Current -> CurrentUserDataSource() | |
is UserFragment.Mode.Other -> OtherUserDataSource(id = mode.id) | |
} | |
} | |
internal class CurrentUserDataSource : UserDataSource { | |
override fun getUser(): User = TODO() // Load current user | |
} | |
internal class OtherUserDataSource(id: String) : UserDataSource { | |
override fun getUser(): User = TODO() // Load other user | |
} | |
//endregion ParentFragment module |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class ParentFragment : Fragment() { | |
private val fragmentFactory = FragmentFactoryImpl() | |
override fun onCreate(savedInstanceState: Bundle?) { | |
childFragmentManager.fragmentFactory = fragmentFactory | |
super.onCreate(savedInstanceState) | |
} | |
// Call this method when you need to show UserFragment displaying current user's info | |
private fun showCurrentUser() { | |
childFragmentManager.commit { | |
add(R.id.content, fragmentFactory.configurationBundle(FragmentFactoryImpl.Configuration.CurrentUser)) | |
} | |
} | |
// Call this method when you need to show UserFragment displaying other user's info | |
private fun showOtherUser(id: String) { | |
childFragmentManager.commit { | |
add(R.id.content, fragmentFactory.configurationBundle(FragmentFactoryImpl.Configuration.OtherUser(id = id))) | |
} | |
} | |
} | |
internal class FragmentFactoryImpl : FragmentFactory() { | |
override fun instantiate(bundle: Bundle): Fragment = | |
when (val configuration = bundle.getParcelable<Configuration>(KEY_CONFIGURATION)!!) { | |
is Configuration.CurrentUser -> currentUserFragment() | |
is Configuration.OtherUser -> otherUserFragment(id = configuration.id) | |
} | |
fun configurationBundle(configuration: Configuration): Bundle = | |
bundleOf(KEY_CONFIGURATION to configuration) | |
private fun currentUserFragment(): UserFragment = UserFragment(CurrentUserDataSource()) | |
private fun otherUserFragment(id: String): UserFragment = UserFragment(OtherUserDataSource(id = id)) | |
private companion object { | |
private const val KEY_CONFIGURATION = "CONFIGURATION" | |
} | |
sealed class Configuration : Parcelable { | |
@Parcelize | |
object CurrentUser : Configuration() | |
@Parcelize | |
data class OtherUser(val id: String): Configuration() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment