Skip to content

Commit

Permalink
use chain of responsibility patter for module instance creation
Browse files Browse the repository at this point in the history
  • Loading branch information
Łukasz Paczos committed Feb 11, 2020
1 parent 33852a5 commit acaae3a
Showing 1 changed file with 69 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -59,23 +59,14 @@ object MapboxModuleProvider {
configurationClass.getMethod(MODULE_CONFIGURATION_DISABLED_CLASS.asGetterFun()).invoke(null) as String
val implClass = Class.forName("$implPackage.$implClassName")

instance = try {
createUsingNoArgConstructor(implClass)
} catch (ex: Exception) {
try {
getKotlinObjectRef(implClass)
} catch (ex: Exception) {
try {
getSingletonInstanceRef(implClass)
} catch (ex: Exception) {
try {
createUsingDefaultMapboxParams(implClass, type, paramsProvider)
} catch (ex: Exception) {
throw MapboxInvalidModuleException(type)
}
}
var foundInstance: Any? = null
for (creator in instanceCreators) {
foundInstance = creator.getInstance(implClass, type, paramsProvider)
if (foundInstance != null) {
break
}
}
instance = foundInstance ?: throw MapboxInvalidModuleException(type)
}

return instance as T
Expand All @@ -89,32 +80,75 @@ object MapboxModuleProvider {
}
}

private fun createUsingNoArgConstructor(implClass: Class<*>): Any {
// try to invoke a no-arg, public constructor
val constructor = implClass.getConstructor()
return constructor.newInstance()
private val noArgConstructorCreator = object : ModuleInstanceProvider {
override fun getInstance(implClass: Class<*>, type: MapboxModuleType, paramsProvider: (MapboxModuleType) -> Array<Pair<Class<*>?, Any?>>): Any? {
return try {
// try to invoke a no-arg, public constructor
val constructor = implClass.getConstructor()
constructor.newInstance()
} catch (ex: Exception) {
null
}
}
}

private fun getKotlinObjectRef(implClass: Class<*>): Any {
// try to create instance of Kotlin object
return implClass.getField("INSTANCE").get(null)
private val kotlinObjectReferenceProvider = object : ModuleInstanceProvider {
override fun getInstance(implClass: Class<*>, type: MapboxModuleType, paramsProvider: (MapboxModuleType) -> Array<Pair<Class<*>?, Any?>>): Any? {
return try {
// try to create instance of Kotlin object
implClass.getField("INSTANCE").get(null)
} catch (ex: Exception) {
null
}
}
}

private fun getSingletonInstanceRef(implClass: Class<*>): Any {
// try to get instance of singleton
return implClass.getMethod("getInstance").invoke(null)
private val singletonReferenceProvider = object : ModuleInstanceProvider {
override fun getInstance(implClass: Class<*>, type: MapboxModuleType, paramsProvider: (MapboxModuleType) -> Array<Pair<Class<*>?, Any?>>): Any? {
return try {
// try to get instance of singleton
implClass.getMethod("getInstance").invoke(null)
} catch (ex: Exception) {
null
}
}
}

private fun createUsingDefaultMapboxParams(
implClass: Class<*>,
type: MapboxModuleType,
paramsProvider: (MapboxModuleType) -> Array<Pair<Class<*>?, Any?>>
): Any {
// try find default arguments for Mapbox default module
val params = paramsProvider.invoke(type)
val constructor =
implClass.getConstructor(*params.map { it.first }.toTypedArray())
return constructor.newInstance(*params.map { it.second }.toTypedArray())
private val defaultMapboxModuleCreator = object : ModuleInstanceProvider {
override fun getInstance(implClass: Class<*>, type: MapboxModuleType, paramsProvider: (MapboxModuleType) -> Array<Pair<Class<*>?, Any?>>): Any? {
return try {
// try find default arguments for Mapbox default module
val params = paramsProvider.invoke(type)
val constructor =
implClass.getConstructor(*params.map { it.first }.toTypedArray())
constructor.newInstance(*params.map { it.second }.toTypedArray())
} catch (ex: Exception) {
null
}
}
}

/**
* Instance creators are called in order until the first one returns a non-null value.
*
* 1. Create using no-ard constructor
* 2. Get Kotlin `object` ref
* 3. Get singleton instance ref
* 4. Create using Mapbox default params
*/
private val instanceCreators: Array<ModuleInstanceProvider> = arrayOf(
noArgConstructorCreator,
kotlinObjectReferenceProvider,
singletonReferenceProvider,
defaultMapboxModuleCreator
)

private interface ModuleInstanceProvider {
fun getInstance(
implClass: Class<*>,
type: MapboxModuleType,
paramsProvider: (MapboxModuleType) -> Array<Pair<Class<*>?, Any?>>
): Any?
}

private fun String.asGetterFun() = "get${this[0].toUpperCase()}${this.substring(1)}"
Expand Down

0 comments on commit acaae3a

Please sign in to comment.