Decorator Patterns in Kotlin - kotlin

I'm trying to practice decorator patterns in Kotlin and one of my exercises is to implement a decorator that returns the temperature in addition to an index which tells one the "count" of the measurement for that temperature, so f.e.
2.0
3.0
3.0
...
...
...
2.0
Main function has to look like this, so I can't just put a println in the thermometer itself. I tried making another function in the SensorLogger itself, but the given main function doesn't use any extern function. I also tried counting with a var that increments each time, but i can't figure out how to make the output look like the example I gave...
fun main() {
val thermometer = Thermometer(UpDownSensor())
thermometer.sensor = SensorLimits(2.0, 4.0, UpDownSensor())
thermometer.measure(10)
thermometer.sensor = SensorLogger(RoundValues(RandomSensor(2.0, 5.0)))
thermometer.measure(10)
}
interface Sensor {
fun getTemperature() : Double?
}
class RandomSensor(val minTemp : Double, val maxTemp: Double) : Sensor {
override fun getTemperature() = Random.nextDouble(minTemp, maxTemp)
}
class UpDownSensor : Sensor {
var temp = 2.0
fun rando() : Int = Random.nextInt(1, 4)
fun fluctuation() : Double {
if (rando() == 1) {
temp += 1
} else if (rando() == 2) {
temp -= 1
} else temp
return temp
}
override fun getTemperature() = fluctuation()
}
class Thermometer(var sensor : Sensor) {
fun measure(times : Int) {
repeat(times) {
println(sensor.getTemperature())
}
}
}
class SensorLogger (val decoSensor : Sensor) : Sensor {
override fun getTemperature() = decoSensor.getTemperature()!!
TODO()
}
class RoundValues (val decoSensor: Sensor) : Sensor {
override fun getTemperature() = Math.round(decoSensor.getTemperature()!! * 10.0) / 10.0
}
class SensorLimits (val minTemp : Double, val maxTemp : Double, val decoSensor: Sensor) : Sensor {
override fun getTemperature() : Double? {
if (decoSensor.getTemperature()!! in minTemp .. maxTemp) return decoSensor.getTemperature()
else return null
}
}

Related

Jetpack Paging 3.0 with Airbnb/epoxy only load the first page

I am trying to use Jetpack Paging 3.0 to get all the characters in Rick and Morty API using retrofit and display it using Airbnb/epoxy. But I only get the first page I've spent several hours trying to look for a solution but no luck.
Link to API
(Rick and Morty API)
CODE:
1 Retrofit
#GET("character/")
suspend fun getAllCharactersByPage(
#Query("page") pageIndex : Int
): Response<GetAllCharactersByPageResponse>
[2] Paging Source
class AllCharacterPagingSource(
private val repository: AllCharactersRepository) : PagingSource<Int, Character>() {
override suspend fun load(
params: LoadParams<Int>
): LoadResult<Int, Character> {
val pageNumber = params.key ?: 1
val prevKey = if (pageNumber == 1) null else pageNumber + 1
val response = NetworkLayer.apiClient.getAllCharactersByPage(pageNumber)
response.getException?.let {
return LoadResult.Error(it)
}
return LoadResult.Page(
data = response.body.results.map { CharacterMapper.buildFrom(it) },
prevKey = prevKey,
nextKey = getPageIndexFromNext(response.body.info.next)
)
}
override fun getRefreshKey(state: PagingState<Int, Character>): Int? {
return state.anchorPosition?.let {
state.closestPageToPosition(it)?.prevKey?.plus(1)
?: state.closestPageToPosition(it)?.nextKey?.minus(1)
}
}
private fun getPageIndexFromNext(next: String?): Int?{
return next?.split("?page=")?.get(1)?.toInt()
}
}
[3] View Model (Pager)
class AllCharactersViewModel : ViewModel() {
private val repository = AllCharactersRepository()
val flow = Pager(
PagingConfig(Constants.PAGE_SIZE, Constants.PREFETCH_SIZE, enablePlaceholders = false)
) {
AllCharacterPagingSource(repository)
}.flow.cachedIn(viewModelScope)
}
[4] Fragment (Submitting data)
iewLifecycleOwner.lifecycleScope.launch {
allCharactersViewModel.flow.collectLatest {
pagingEpoxyController.submitData(it)
}
}
binding.charactersEpoxyRecyclerView.setController(pagingEpoxyController)
[5] Epoxy Controller
class CharactersPagingEpoxyController : PagingDataEpoxyController<Character>() {
var context : Context? = null
override fun buildItemModel(currentPosition: Int, item: Character?): EpoxyModel<*> {
return CharacterCardModel(
character = item!!,
context = context!!,
onClick = { characterId ->
}
).id("characters_${item.id}")
}
data class CharacterCardModel(
val character : Character,
val context : Context,
val onClick: (Int) -> Unit
) : ViewBindingKotlinModel<CharacterCardContainerModelBinding>(R.layout.character_card_container_model) {
override fun CharacterCardContainerModelBinding.bind() {
Glide.with(context)
.load(character.image)
.into(imageView)
characterName.text = character.name
mirroredCharacterName.text = character.name
}
}
}
Thanks in advance!!!
I guess prevKey should be something like this
val previewPage= if (pageNumber ==1 ) null else pageNumber -1

How to solve this strong dependency between those classes?

I have two classes A and B.
Class A has several properties. One of them are an instance of class B.
At some point in the main function I will define an instance a of A. I will need to do some computation on its property of type B.
This computation, however, needs another property of a.
The result is a.property3.computation(a.property1,someValue). I think it's ugly.
Here is some "pseudo-code" (in Kotlin but I am facing the same problem using other languages as well):
class B {
val property : Map<String,Int>
fun computation(val parameter1: Int, val parametre2: Double) : Int {
//doing some computation
return result
}
}
class A {
var property1 : Int
var property2 : Stirng
var property3 : B
}
fun main (){
val someValue = 0.4 //It's here to show the idea that the function `computation` needs also some other arguments that does not come from `A`'s propery
val a = A()
val result = a.property3.computation(a.property1,someValue) // here is the line that bothers me !
}
I think you should use Builder design pattern to remove computation function from B class like this:
class B {
val properties : MutableMap<String,Int> = HashMap()
fun setProperty(name: String, value: Int) {
this.properties[name] = value
}
}
class A {
var property1 : Int = 0
var property2 : String = ""
var property3 : B = B()
}
class Calculator(builder: Builder) {
private val property1 = builder.property1
private val someValue = builder.someValue
private val properties = builder.properties
companion object {
fun builder() = Builder()
}
fun computation() : Int {
//doing some computation
val result = property1 + someValue.toInt() + properties.getOrDefault("value", 0)
return result
}
class Builder {
var property1: Int = 0
var someValue: Double = 0.0;
var properties : MutableMap<String,Int> = HashMap()
fun property1(property1: Int): Builder {
this.property1 = property1
return this
}
fun someValue(someValue: Double): Builder {
this.someValue = someValue
return this
}
fun properties(properties : Map<String,Int>): Builder {
this.properties.putAll(properties);
return this
}
fun build(): Calculator {
return Calculator(this)
}
}
}
fun main (){
val someValue = 0.4 //It's here to show the idea that the function `computation` needs also some other arguments that does not come from `A`'s propery
val a = A()
a.property1 = 10
a.property3.setProperty("value", 20)
val result = Calculator.builder()
.properties(a.property3.properties)
.property1(a.property1)
.someValue(someValue)
.build()
.computation()
println(result)
}
May be you want this?
fun main (){
val someValue = 0.4
val a = A()
val result = with(a) {
property3.computation(property1,someValue)
}
}

Data class toString name clash property / construction arg

Newby in Kotlin, I'm trying to implement a simple data class with constraint validation in fields.
This works great, but my solution is suboptimal since it exposes the private variables names defined in the class' definition in the toString representation, where I would prefer to have the properties:
data class MutablePointKt(private val _x: Int = 0, private val _y: Int = 0) {
private fun validatePositiveOrZero(some:Int) {
Validate.isTrue(some >= 0, "negative coordinate provided: $some")
}
var x: Int = 0
get() {
println(" > getting x: $field")
return field
}
set(value) {
validatePositiveOrZero(value)
field = value
}
var y: Int = 0
get() {
println(" > getting y: $field")
return field
}
set(value) {
validatePositiveOrZero(value)
field = value
}
init {
this.x = _x;
this.y = _y;
}
}
println(MutablePointKt(1, 2)) // prints "MutablePointKt(_x=1, _y=2)". how to print "MutablePointKt(x=1, y=2)" ?
Thank you !
EDIT:
I have a solution with
override fun toString(): String = ToStringBuilder.reflectionToString(this, KotlinToStringStyle()) and
class KotlinToStringStyle : ToStringStyle() {
private fun isFiltered(s: String?) = s?.startsWith("_") == true
override fun appendFieldStart(buffer: StringBuffer?, fieldName: String?) {
if (!isFiltered(fieldName))
super.appendFieldStart(buffer, fieldName)
}
override fun appendDetail(buffer: StringBuffer?, fieldName: String?, any: Any) {
if (!isFiltered(fieldName))
super.appendDetail(buffer, fieldName, any)
}
override fun appendFieldEnd(buffer: StringBuffer?, fieldName: String?) {
if (!isFiltered(fieldName))
super.appendFieldEnd(buffer, fieldName)
}
}
... but this is rather overkill, I would prefer a concise solution aka "the Kotlin way"

how to get the data from an getParcelableArrayListExtra<Parcelable> in kotlin

How to get the data from an getParcelableArrayListExtra in kotlin?.
I have a Parceable data class called FoodParceable
data class FoodParceable(val idCover: Int, val name: String, val
price: Double):Parcelable {
constructor(parcel: Parcel) : this(
parcel.readInt(),
parcel.readString(),
parcel.readDouble()
)
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeInt(idCover)
parcel.writeString(name)
parcel.writeDouble(price)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<FoodParceable> {
override fun createFromParcel(parcel: Parcel): FoodParceable {
return FoodParceable(parcel)
}
override fun newArray(size: Int): Array<FoodParceable?> {
return arrayOfNulls(size)
}
}
}
In the current Acitvity A, fill in the arrayListParceable and send it to activity B.
val arrayListParceable = ArrayList<FoodParceable>()
for (Food in listFood) {
arrayListParceable.add(FoodParceable(R.mipmap.ic_food_meat, "Carne", 9.99))
arrayListParceable.add(FoodParceable(R.mipmap.ic_food_meat, "Vegetales", 29.99))
arrayListParceable.add(FoodParceable(R.mipmap.ic_food_meat, "Frutas", 39.99))
}
val intent = Intent()
intent.putParcelableArrayListExtra(LIST_PRODUCT,arrayListParceable)
activity?.setResult(Activity.RESULT_OK, intent)
When I get the value in activity B, in list item, I can not enter its content.
if(resultCode == Activity.RESULT_OK){
if(data != null){
val listItems = data.getParcelableArrayListExtra<Parcelable>(LIST_PRODUCT)
listItems[0]. //ERROR
}
}
I am new to kotlin, thank you for your comments. Regards
You have to give Kotlin more information about the type of your object list. Try this:
val listItems : ArrayList<FoodParceable> = data.getParcelableArrayListExtra(LIST_PRODUCT)

Kotlin Comparator Compilation Error in IntelliJ

Using Kotlin 1.2.41-release and given a List<Pair<Int, Int>>, the following code generates a compilation error in Intellij, although Gradle command line build works.
sortedWith(compareBy({ it.first }, { it.second }))
Cannot choose among the following candidates without completing type inference.
public fun <T> compareBy(vararg selectors: (???) -> Comparable<*>?): kotlin.Comparator<???> defined in kotlin.comparisons
public fun <T> compareBy(vararg selectors: (Pair<Int, Int>) -> Comparable<*>?): kotlin.Comparator<Pair<Int, Int>> defined in kotlin.comparisons
How can I fix this?
Edit:
edges
.map {
it.either.run {
val p = this
val q = it.other(this)
val min = min(p, q)
if (min == p) p to q else q to p
}
}
.sortedWith(compareBy({ it.first }, { it.second }))
.toList()
where, edges is Iterable<Edge>
class Edge(private val v: Int, private val w: Int, val weight: Double) : Comparable<Edge> {
val either: Int
get() = v
fun other(vertex: Int): Int {
return if (v == vertex) w else v
}
override fun compareTo(other: Edge): Int {
return weight.compareTo(other.weight)
}
override fun toString(): String {
return "Edge(v=$v, w=$w, weight=$weight)"
}
}