Dominaezzz
06/05/2021, 1:06 PMFlow<T>
class members with Flow
?
class Server {
val notificationsFlow: Flow<Notification>
val notifications: Flow<Notification>
}
Do people prefer notifications
or notificationsFlow
?Adam Powell
06/05/2021, 2:09 PMnotifications
Dominaezzz
06/05/2021, 2:14 PMchao
06/05/2021, 2:55 PMLiveData
to Flow
that we may have both notificationsLiveData
and notificationsFlow
melatonina
06/05/2021, 4:02 PMprivate val somethingMutableFlow = MutableStateFlow<Something>(Something.ZERO)
val somethingFlow : StateFlow<Something> get() = somethingMutableFlow
var something by somethingMutableFlow
private set
is common for me.Erik
06/06/2021, 7:30 AMitems
, not itemsFlow
. If I have a mutable shared flow nearby, then that's called mutableItems
next to items
.louiscad
06/06/2021, 11:17 AMFlow<List<Notification>>
Erik
06/06/2021, 11:27 AMnotificationLists
Dominaezzz
06/06/2021, 11:28 AMnotifications
still. It's all in the semantics I guess.ursus
06/06/2021, 7:30 PMOliver.O
06/08/2021, 7:54 AMnotificationBatches
Erik
06/08/2021, 9:35 AMList
interface clearly defines the contract:
A generic ordered collection of elements.Sound like a good description of the the use case to me: there's notifications and they're ordered. No need for a new word like 'batch'. What if a future type
Batch
is introduced in your code base. Then you'd have to remember that notificationBatches
is actually not a good name any more. Admittedly, the same applies to the notificationLists
name if you'd ever refactor to Flow<Collection<Notification>>
or something, but that's a refactor directly on this variable's type, so easier to see/remember. It's just the nature of refactoring.
So I stay by notificationLists
(if you're using and expecting flows by default) or even notificationListFlow
. There's a strong type system for a reason: use types explicitly, either by specifying them, or using them in the name if you rely on type inference.Oliver.O
06/08/2021, 10:07 AMIterable
? Once you're exposing too much, people will depend on it, limiting your refactoring options.Erik
06/08/2021, 10:18 AMWhy imply ordering?The question was how to name
Flow<List<Notification>>
, so the List
type and therefore the ordering of elements was imposed, not implied.Collection
or another wrapper typeOliver.O
06/08/2021, 10:41 AMList
is part of the implementation or the use-site contract, as all we have is a simple variable declaration. So with respect to naming, I went with as little implied coupling as possible. "Batch" has a lesser meaning. While in many cases that would not be a good thing, in this case it's exactly what I intended. 🙂Erik
06/08/2021, 10:50 AMNotificationBatch
or something similar. Then you can perfectly name the variable like you suggested! (And that would still be Hungarian notation 😉)Oliver.O
06/08/2021, 11:00 AMList
is what describes your purpose best, then go with notificationLists
. Once we had really figured out the true intention of the example, we might actually agree on naming! 😄ursus
06/08/2021, 12:01 PMval foo: Flow<Foo>
but also a suspend function which selects foo 1 time as normal.. how to call this.. loadFoo()
, findFoo()
?
or other way around, call the flow fooFlow
and foo()
for the suspend function?Erik
06/08/2021, 1:10 PMsuspend fun readFoo(): Foo
- fun readFoos(): Flow<Foo>
And otherwise I don't think it's too bad to just name them fooFlow
and foo
. Or maybe even name the flow just foos
.Oliver.O
06/08/2021, 2:44 PMloadFoo()
, findFoo()
, readFoo()
as these names would imply something imperative, not returning a value. So go with private val _foo: Flow<Foo>
and suspend fun foo(): Foo
. See Choose good names, an underestimated chapter in the Kotlin coding conventions. I know this case is less than ideal and a suspending get()
looks preferable, but its not there yet. That a function and property exist side-by-side, serving the same purpose, is really just an implementation artifact. There is no perfect naming solution to overcome this. There should ideally be just one thing.ursus
06/09/2021, 4:17 AMOliver.O
06/09/2021, 8:53 PMprivate val _foo: Flow<Foo>
If you really need both in public, the accessor function and the variable, I would reconsider the design. While I would not blindly adapt the design for the sake of naming, in my experience problematic designs often manifest themselves in naming difficulties. Your mileage may vary, but that's what I would look at.ursus
06/10/2021, 11:21 AMOliver.O
06/10/2021, 7:35 PMursus
06/11/2021, 4:56 PMinterface FooDao {
fun foos(): Flow<List<Foo>>
fun fooByIdFlow(id: Strong): Flow<Foo?>
suspend fun fooById(id: String) : Foo?
suspend fun saveFoos(foos: List<Foo>)
}
and its implementation is driven by sqldelight, for exampleOliver.O
06/12/2021, 10:26 PMimport kotlinx.coroutines.flow.*
import kotlinx.coroutines.*
class Foo(id: String, name: String)
sealed class FooCriteria {
object All : FooCriteria()
class Id(value: String) : FooCriteria()
class Name(value: String) : FooCriteria()
}
interface Storage {
fun foos(criteria: FooCriteria): Flow<Foo>
suspend fun addOrUpdate(foos: Iterable<Foo>)
}
fun storage(): Storage = TODO()
fun main() = runBlocking {
storage().foos(FooCriteria.All).collect {
println(it)
}
storage().foos(FooCriteria.Id("#23")).collect {
println(it)
}
}
ursus
06/13/2021, 9:45 AMOliver.O
06/14/2021, 12:33 PMStorage
in this case is your entire database, not a single-object's persistence API. As the above interface does not even have to be a database (could be flat file or whatever), it is named just Storage
. There is really no special trick involved. In the end it is more about choosing the "right" abstraction. A DAO is just a name for a bunch of functions without clear boundaries. Storage (or, more specific, database) is a better understood abstraction (you can almost instantly visualize it). And yes, I would not care spreading ORM imports, that would not be a factor if the ORM is the proper abstraction.
Keep in mind that all of this is mostly about how you think about stuff, how easily you can understand it initially and then pick it up again after a 6 month pause. The perception also changes depending on what you are used to. You may not follow if you were used to a different set of abstractions. In that case, the quality would show if you try to introduce those abstractions to new people, unfamiliar with the chosen concepts.