Zac Sweers
07/07/2025, 3:26 PMMarshall Mann-Wood
07/08/2025, 3:32 AMZac Sweers
07/08/2025, 4:28 AMRick Ratmansky
07/08/2025, 9:04 PMMarshall Mann-Wood
07/09/2025, 12:48 AMclass Foo(@Inject private val factory: GodFactory) {
private val bar = factory.get<Bar>()
fun doWork() {
val baz = factory.get<Baz>()
baz.doThing()
}
}
Another option would be to remove the injection of the GodFactory
and make it available through some other mechanism, such as static availability.
class Foo {
private val bar = GodFactory.get<Bar>()
fun doWork() {
val baz = GodFactory.get<Baz>()
baz.doThing()
}
}
Making it statically available starts to look very similar to a Service LocatorMarshall Mann-Wood
07/09/2025, 12:53 AMclass FooTest {
@Before fun setUp() {
GodObject.overrideDependency(Bar::class, mock()) // doesn't exist in release builds
}
@After fun tearDown() {
GodObject.removeOverride(Bar::class) // doesn't exist in release builds
}
}
I'm not super familiar with how Dagger/Hilt do this, but I can't imagine it's much different to what I described.Zac Sweers
07/09/2025, 1:38 AM// Guice/Dagger/KI/Metro
class HttpClient @Inject constructor(private val cache: Cache)
@Test fun example() {
val httpClient = HttpClient(Cache.NONE)
}
No mocking frameworks necessary, let alone static mocking. No precursors either, you're just working with the standard language construction features.
vs
// service locator
class HttpClient {
private val cache: Cache by inject()
}
@Test fun example() {
// You have to know about the service locator implementation detail of the class to safely stand it up
ServiceLocator.set(Cache::class.java, Cache.NONE)
val httpClient = HttpClient()
}
Perhaps what you're thinking of is the notion/existence of a component (Dagger 2, KI) or graph (Dagger 1, Metro) instance that acts as a holder of objects at a certain scope. Injectable types do have to have a path in the binding graph back to a root in this class, but you generally abstract this away pretty quickly in whatever architecture you're using (ViewModelProvider.Factory, activity providers, etc). Hilt brings a lot of these as first party conveniences too.Zac Sweers
07/09/2025, 1:40 AMZac Sweers
07/09/2025, 1:41 AMLazy
handles this fine enough for performance-critical areas. In Metro we could explore something like Jesse described at the compiler level to automatically transform these if we really wanted.Zac Sweers
07/09/2025, 1:43 AMMarshall Mann-Wood
07/10/2025, 1:09 AMGodFactory
.Marshall Mann-Wood
07/10/2025, 1:14 AMMarshall Mann-Wood
07/10/2025, 1:16 AMI'm curious how you enforce hierarchy with thisI'm going to leave @Rick Ratmansky to decide if/how we can answer this, as it gets into "I don't know exactly how much I can say." Closed source do be fun sometimes
Rick Ratmansky
07/12/2025, 12:19 PMRick Ratmansky
07/12/2025, 12:21 PMRick Ratmansky
07/12/2025, 12:23 PMRick Ratmansky
07/12/2025, 12:24 PMgildor
07/18/2025, 3:22 AMmattinger
07/25/2025, 7:10 PMe: [ksp] Found conflicting entry point declarations. Getter methods on the component with the same name and signature must be for the same binding key since the generated component can only implement the method once. Found:
anvil.component.com.xfinity.digitalhome.container.digitalhomecomponent.MergedCommerceAppComponent_0044d15d.Factory anvil.component.com.xfinity.digitalhome.container.digitalhomecomponent.MergedCommerceAppComponent_0044d15d.ParentComponent.commerceComponentFactory()
com.xfinity.dh.commerce.buyflow.di.CommerceAppComponent.Factory com.xfinity.dh.commerce.buyflow.di.CommerceAppComponent.FactoryCreator.commerceComponentFactory()
e: [ksp] @Component.Factory types must have exactly one abstract method. Already found: com.xfinity.digitalhome.container.DigitalHomeComponent com.xfinity.digitalhome.container.DigitalHomeComponent.Factory.create(com.xfinity.digitalhome.app.DigitalHomeApplication, com.xfinity.digitalhome.utils.manager.LocaleManager, com.xfinity.digitalhome.config.PartnerConfigSettings, com.xfinity.digitalhome.features.launch.logging.LaunchTrackingManager)
This is using a very standard MergeComponent
along with a ContributesSubcomponent
along with a contributed interface to the app scope to get the factory:
@ContributesSubcomponent(
parentScope = AppScope::class,
scope = CommerceAppScope::class
)
interface CommerceAppComponent {
@ContributesSubcomponent.Factory
interface Factory {
fun create(): CommerceAppComponent
}
@ContributesTo(AppScope::class)
interface FactoryCreator {
fun commerceComponentFactory(): Factory
}
I'm not sure what's going on, but i also get a lot of starvation when i try to run our CI tasks which is compiling 6 different branding flavors in parallel.
This seems to be related to parallism, because when i run with --max-workers=1 it seems to not occurmattinger
07/25/2025, 8:25 PMjava.nio.file.FileAlreadyExistsException: /Users/xxxxxxxx/Work/comcast/xfinity-android/app/build/generated/ksp/comcastDebug/java/com/xfinity/digitalhome/features/launch/login/NoAppAuthBrowserFragment_MembersInjector.java
at java.base/sun.nio.fs.UnixFileSystem.copy(Unknown Source)
at java.base/sun.nio.fs.UnixFileSystemProvider.copy(Unknown Source)
at java.base/java.nio.file.Files.copy(Unknown Source)
at com.google.devtools.ksp.common.IncrementalUtilKt.copyWithTimestamp(IncrementalUtil.kt:80)
at com.google.devtools.ksp.AbstractKotlinSymbolProcessingExtension.updateFromShadow(KotlinSymbolProcessingExtension.kt:493)
at com.google.devtools.ksp.AbstractKotlinSymbolProcessingExtension.doAnalysis(KotlinSymbolProcessingExtension.kt:391)
mattinger
07/25/2025, 8:33 PMmattinger
07/30/2025, 4:46 PM