I have a feature in my application which has some private state information and some public state to share. How can I get rid of the mutable private state variable? How do I get the private state into the chain?
I just recently learned about functional programming and wanted to transform this feature to a more fp-like approach.
This is my approach so far as a simple example.
sealed class PublicState {
data class Data(val a:Int, val b:Int):PublicState()
object Pending : PublicState()
}
data class PrivateState(val a:Int, val b:Int, val x:Int)
sealed class Action {
data class InputC(val c:Int):Action()
data class InputD(val d:Int):Action()
}
sealed class Update {
data class A(val a:Int):Update()
data class B(val b:Int):Update()
object Working : Update()
}
class Feature {
val actions = PublishSubject.create<Action>()
val state = BehaviorSubject.create<PublicState>()
private var privateState = PrivateState(0,0,1)
init {
val startState = privateState.toPublicState()
actions.flatMap {action ->
when (action) {
is Action.InputC -> handleC(action)
is Action.InputD -> handleD(action)
}
}.scan(startState, ::reduce)
.subscribe(state)
}
fun reduce(previousState:PublicState, update: Update):PublicState {
// can't use previousState because Pending has not all information
// I don't want to add the information to pending because state is undefined while pending
return when (update) {
is Update.A -> privateState.copy(a = update.a).toPublicState()
is Update.B -> privateState.copy(b = update.b).toPublicState()
Update.Working -> PublicState.Pending
}
}
fun doAction(action: Action) {
actions.onNext(action)
}
private fun handleC(action:Action.InputC):Observable<Update> {
return Observable.fromCallable {
// time consuming work which uses x
val result = privateState.x + privateState.a + action.c
Update.A(result) as Update
}.startWith(Update.Working)
}
private fun handleD(action:Action.InputD):Observable<Update> {
return Observable.fromCallable {
// time consuming work which uses x
val result = privateState.x + privateState.b + action.d
Update.B(result) as Update
}.startWith(Update.Working)
}
}
private fun PrivateState.toPublicState(): PublicState {
return PublicState.Data(a, b)
}
In reality there are a lot more state variables than a, b and x. But if I want them in the chain, I have a gigantic State variable and all of it gets exposed. It feels easier with the mutable variable.
Do you have any suggestion how to solve this? I am also open for other patterns, if you think this is a wrong approach.
My goal is to keep some private state and expose just the PublicState.
FP does not deal with private states. Why would you care about keeping something private? Because someone else, from an outer world, could intentionally or not mutate that one and bring entire object into disrepair, right? But there are no mutations in the FP. So you're safe.
Thus your quesiton reduces to the "how to handle state". Well, let me know if you want me to answer.
Related
Consider a sealed class State.
sealed class State {
object Unknown : State()
object Loading : State()
object Success : State()
data class Failure(val exception: Exception)
}
I have a stateflow where consumers can actively listen to the state updates.
val state:State = MutableStateFlow(State.Unknown)
Now, I also want to have a simple suspend method which waits till the state reaches either Success or Failure, so consumers who just need the result once need not be aware of the stateflow.
How to achieve this?
Although you already came up with a working solution, you might want to make use of the built-in Flow.first { ... } operator for simplicity.
suspend fun waitForResult(): State {
val resultStates = setOf(State.Success::class, State.Failure::class)
return state.first { it::class in resultStates }
}
I was able to come up with the following extension function which looks to be working fine.
suspend fun waitForResult(): State {
val resultStates = setOf(State.Success::class, State.Failure::class)
return state.waitForStates(resultStates)
}
suspend fun <T : Any> StateFlow<T>.waitForStates(states: Set<KClass<out T>>): T = coroutineScope {
var currentValue = value
// not needed for correctness, just an optimisation
if (currentValue::class in states) {
return currentValue
}
coroutineScope {
collect {
if (it::class in states) {
currentValue = it
cancel()
}
}
}
return currentValue
}
So, I have an enum called Level. That enum is actually just a wrapper for some other Level. Now I need to access that wrapped value (currently a protected property) in a different class called Log which sits in the same package. Obviously I do not want to completely expose that property by making it internal or public, but I need to access that wrapped value in my Log class.
How to I do that?
As Kotlin doesn't provide anything similar to package-private visibility, everything I tried failed. I'm already aware of the possibility to put both classes in one file, but that only allows me to gain exclusive access to the classes themselves, not their properties. And because I need to have both classes public that won't help either. So if anyone knows a workaround, I would be more than happy to hear it, because even though I really like Kotlin, this might be the reason for me to drop the language.
Both classes I mentioned look as follows:
Level.kt
enum class Level(protected val level: java.util.logging.Level) {
/** Useful for stuff */
OFF(CustomLevel("OFF", Int.MAX_VALUE)),
ASSERT(CustomLevel("ASSERT", 1200)),
FATAL(CustomLevel("FATAL", 1100)),
ERROR(CustomLevel("ERROR", 1000)),
WARN(CustomLevel("WARN", 900)),
INFO(CustomLevel("INFO", 800)),
DEBUG(CustomLevel("DEBUG", 700)),
ALL(CustomLevel("ALL", Int.MIN_VALUE));
private class CustomLevel(name: String, value: Int) : java.util.logging.Level(name, value)
}
Log.kt
object Log {
private val DEFAULT_CONSOLE_VERBOSITY = Level.ERROR
private val DEFAULT_FILE_VERBOSITY = Level.ALL
#JvmStatic
var consoleVerbosity: Level
get() = Level.findLevel(consoleHandler.level)
set(value) {
consoleHandler.level = value.level // The property I need to access
}
#JvmStatic
var fileVerbosity: Level
get() = Level.findLevel(fileHandler.level)
set(value) {
fileHandler.level = value.level // The property I need to access
}
private val consoleHandler = ConsoleHandler()
init {
consoleHandler.formatter = SimpleFormatter()
consoleHandler.level = DEFAULT_CONSOLE_VERBOSITY.level
}
private val fileHandler = FileHandler()
init {
fileHandler.formatter = SimpleFormatter()
fileHandler.level = DEFAULT_FILE_VERBOSITY.level
}
}
I am running the latest stable version of Kotlin (1.4.31)
As a workaround you may define an extension function/property for Log class in the scope of Level class:
enum class Level(private val level: java.util.logging.Level) {
//...
//Option 1
companion object {
fun Log.getLevelOf(level: Level) = level.level
}
//Option 2
val Log._level get() = level
}
Also you may define extension property for Level class in the scope of Log class for more natural usage:
object Log {
//...
private val Level.level : java.util.logging.Level
get() = consoleHandler.level = Level.run { getLevelOf(this#level) } // For Option 1
get() = with(this) { _level } // For Option 2
}
Downside of this approach is a tough coupling between these classes.
You just have to use extension functions like this:
fun Level.toLevel() = this.level
That allows you to access protected properties of other classes.
You cannot access a private class from another class but you can access a class from a class that is packed inside a file. So the workaround is to make fun in public class to access the private class which is in the same file.
But the highlight is that you cannot write a class inside an enum class in Kotlin.
I still don't know how you managed to write this code down in an IDE, because it will show an error.
I've a question about, how would you handle this case?
Imagine that you have to do a validation of an object and that validation should have a sort of importance, in this case we only have 3 validations, each one can result Valid or his own QualityCheck enum value.
This is the method example in kotlin and the validations
sealed class Validation {
abstract fun validate(bobject: ObjectToCheck): QualityCheck
object VeryImportantValidation : Validation() {
override fun validate(bobject: ObjectToCheck): QualityCheck =
if (isValid(bobject.valueX)) QualityCheck.Valid
else QualityCheck.VeryImportantInvalid
}
object SecondMostImportant : Validation() {
override fun validate(bobject: ObjectToCheck): QualityCheck =
if (isValid(bobject.valueNotSoImportant)) QualityCheck.Valid
else QualityCheck.SecondMostImportantInvalid
}
object NotSoImportant : Validation() {
override fun validate(bobject: ObjectToCheck): QualityCheck =
if (isValid(bobject.valueNothingImportant)) QualityCheck.Valid
else QualityCheck.NotSoImportantInvalid
}
}
fun getQualityCheck(object: ObjectToCheck): QualityCheck =
if (VeryImportantValidation.validate(object) === QualityCheck.Valid) {
if (SecondMostImportant.validate(object) === QualityCheck.Valid) {
NotSoImportant(paymentsRepository.getSystemPayments()).validate(object)
} else {
QualityCheck.SecondMostImportantInvalid
}
} else {
QualityCheck.VeryImportantInvalid
}
I think this is not scalable neither easy to read/understand or modify if we would want to add a new one.
There is any kind to do this elegant and easier to include more validations?
If you invert your Boolean conditions, you can eliminate the nesting. Then you can change it to a when statement for simplicity:
fun getQualityCheck(object: ObjectToCheck): QualityCheck = when {
VeryImportantValidation.validate(object) !== QualityCheck.Valid ->
QualityCheck.VeryImportantInvalid
SecondMostImportant.validate(object) !== QualityCheck.Valid ->
QualityCheck.SecondMostImportantInvalid
else ->
NotSoImportant(paymentsRepository.getSystemPayments()).validate(object)
}
Validation like this is a perfect candidate for the "Rules engine pattern"... mostly known as a for loop.
You just set up a List<Validation> with all of the validations you want to run and iterate over them calling the validate method. You have 2 options, collect all errors (doing a fold on the list), or stop the loop after the first error with a asSequence().map().takeWhile().
I forgot to say, you don't need to seal the Validation class. What is your intent with that?
Scalability/Extensibility would depend from situation to situation and a code cannot be open to all types of changes. One rule of thumb is to keep it as simple as possible and when a requirement is changed we ensure that the code is open to such kind of changes.
Also, I agree with #Augusto. Your use of the sealed class is not really how it is intended to be used.
Anyways let's look at how it would be easier to add a new validation, change the severity of the violation, or have several validations with the same severity.
Lets define an interface for Validations.
interface Validation {
fun validate(value: Int): Boolean
}
Now let's define a few Validations
class LimitValidation: Validation{
override fun validate(value: Int) = value < 100
}
class PositiveValidation: Validation {
override fun validate(value: Int) = value > 0
}
class EvenValidation: Validation {
override fun validate(value: Int) = value % 2 == 0
}
Let's say you have the following Violations
enum class Violation {
SEVERE,
MODERATE,
TYPICAL
}
We can make use of sealed class to define the quality.
sealed class Quality {
object High : Quality()
data class Low(val violation: Violation) : Quality()
}
We can create a class responsible for checking the Quality.
class QualityEvaluator {
private val violationMap: MutableMap<KClass<*>, Violation> = mutableMapOf()
init {
violationMap[LimitValidation::class] = Violation.SEVERE
violationMap[PositiveValidation::class] = Violation.MODERATE
violationMap[EvenValidation::class] = Violation.TYPICAL
}
fun evaluateQuality(value: Int, validations: List<Validation>) : Quality {
val sortedValidations = validations.sortedBy(::violationFor)
sortedValidations.forEach {
if(!it.validate(value)) {
return Quality.Low(violationFor(it))
}
}
return Quality.High
}
private fun <T: Validation> violationFor(validation: T): Violation {
return if (violationMap.containsKey(validation::class)) {
requireNotNull(violationMap[validation::class])
} else {
Violation.TYPICAL
}
}
}
Finally, we can use all this like so:
val validations = listOf(LimitValidation(), PositiveValidation(), EvenValidation())
when(val quality = QualityEvaluator().evaluateQuality(8, validations)) {
is Quality.High -> println("Quality is High")
is Quality.Low -> println("Quality is Low. Violation: ${quality.violation}")
}
I am trying to understand how to hide a base constructor parameter in a subclass in kotlin. How do you put a facade over a base constructor? This doesn't work:
import com.android.volley.Request
import com.android.volley.Response
class MyCustomRequest(url: String)
: Request<String>(Request.Method.POST, url, hiddenListener) {
private fun hiddenListener() = Response.ErrorListener {
/* super secret listener */
}
...
}
I think I understand the problem:
During construction of a new instance of a derived class, the base
class initialization is done as the first step (preceded only by
evaluation of the arguments for the base class constructor) and thus
happens before the initialization logic of the derived class is run.
I'm trying to solve this problem for Volley, where I need my custom request to be be a Request so that it can be passed into a RequestQueue. It would be easier of RequestQueue took in some kind of interface but since it doesn't I have to subclass. There are other ways I can hide these complexities from the caller, but this limitation has come up for me other times in Kotlin and I'm not sure how to solve it.
I am not familiar with volley but I tried to come up with an example that should give you some insight how to solve your problem. What you can do is use a companion object:
interface MyListener {
fun handleEvent()
}
open class Base<T>(anything: Any, val listener: MyListener) { // this would be your Request class
fun onSomeEvent() {
listener.handleEvent()
}
}
class Derived(anything: Any) : Base<Any>(anything, hiddenListener) { // this would be your MyCustomRequest class
private companion object {
private val hiddenListener = object : MyListener {
override fun handleEvent() {
// do secret stuff here
}
}
}
}
So if you apply this to your problem, the result should look something like this:
class MyCustomRequest(url: String)
: Request<String>(Request.Method.POST, url, hiddenListener) {
private companion object {
private val hiddenListener = Response.ErrorListener {
/* super secret listener */
}
}
...
}
A different way would be to use a decorator, create your Request withing that decorator and just delegate the calls to it:
class Decorator(anything: Any) {
private var inner: Base<Any>
private val hiddenListener: MyListener = object : MyListener {
override fun handleEvent() { }
}
init {
inner = Base(anything, hiddenListener)
}
}
And once again for your example that would look like this:
class MyCustomRequest(url: String) {
private var inner: Request<String>
private val hiddenListener = Response.ErrorListener {
/* super secret listener */
}
init {
inner = Request<String>(Request.Method.POST, url, hiddenListener)
}
...
}
I have a class where I either know a specific value on creation, or I need to generate it, which is somewhat expensive. Can I generate the value only when it's actually needed?
val expensiveProperty: A
constructor(expensiveProperty: A) {
this.expensiveProperty = expensiveProperty
}
constructor(value: B) {
// this doesn't work
this.expensiveProperty = lazy { calculateExpensiveProperty(value) }
}
It's possible, but with a twist:
class C private constructor(lazy: Lazy<A>) {
val expensiveProperty by lazy
constructor(value: B) : this(lazy { calculateExpensiveProperty(value) })
constructor(expensiveProperty: A) : this(lazyOf(expensiveProperty))
}
Note how I kept primary constructor private while leaving secondary constructors public.