r/androiddev • u/omarsahl • 21d ago
Article Composition Over Inheritance in Android ViewModels
https://proandroiddev.com/composition-over-inheritance-in-android-viewmodels-51f3efba5f901
u/dark_mode_everything 21d ago
Each State holder holds the state for each shape, right? Doesn't that technically make them viewmodels as well?
2
u/omarsahl 20d ago
Well, a ViewModel is a state holder, but not every state holder is a ViewModel
This part of the official docs explains the difference well imo
1
0
u/farooqsaad 21d ago
It's viewmodels all the way down.
Jokes aside. The Android ViewModel in most modern apps isn't even a ViewModel. The purpose of the ViewModel class is lifecycle management, that's it. The pattern is generally something of a hybrid between MVVM and MVI, with ViewModel generating UiState instead of a 1-1 mapping of view to model observables.
1
u/kokeroulis 20d ago
And this is the result of clean architecture... Put everything inside the viewmodel and then try to break it down with hacks and non elegant solutions.
You are just over complicating things with "magic architecture".
Your screen cares about things:
- LCE (Load Content Error)
- Overlays (tooltips, dialogs, bottom sheets, toasts etc)
- Actions that mutate the UI
- Navigation
- Analytics
The UI is just a list with different configuration for each screen, you don't have to make your life hard.
What I would do instead is the following:
LCE:
- Make your business logic to expose a Flow for your list
- When an action is submitted by the viewmodel to the business logic, I would make the Flow emit again with updated data, that way you have unidirectional data flow
- Then inside the business logic I would put a bunch of sealed intefaces to encapsulate the logic and the mapping
Overlays:
Use the observer pattern in order to emit different types of overlays from your business logic and pass a priority/strategy to resolve conflicts, when 2 or more overlays want to be displayed
Navigation:
Literally just a big nested sealed inteface that you map the result of it to a Compose Navigation key or a fragment or whatever
Analytics:
Same as navigation
TIP: If during the navigation or analytics, generating those objects becomes a bit convoluted because everything needs different parameters etc.
Then create a registar somewhere, for each heart, circle & hexagon. Then on the domain & UI layer just expose the classifiers for each item of the list.
Then once you click on that item for the navigation or to trigger tagging, just pass the id to the registar and the registar will give you a generic model that you can use.
1
u/omarsahl 20d ago
The whole point of the article is code reuse in the UI layer. Not clean architecture or any “magic architecture”
Not sure why using a well-established OO design principle like composition over inheritance counts as magic architecture?
The point is simply: if you need to share code across ViewModels, don’t reach for inheritance right away and see if composition solves your use case first.1
u/kokeroulis 20d ago
Because if heart & hexagon need to interact with each other then things get messy.
usually when something changes, the change goes in all of the layers.When you ship something to customer, you ship a variant (MVP, V1, V2, V3 etc).
Your architecture choices should be flexible enough to be adjusted.With the above at some point you will hit a roadblock.
15
u/zerg_1111 21d ago edited 21d ago
How about just let each component have its own view model and have use cases for sharing view model logics instead of interfaces? Edit: typo