New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add usecase repository #2
Conversation
fun <R> success(value: R) = Result.Right(value) | ||
fun <R> error(error: Error) = Result.Left<R>(error) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've been using funKTionale and it fits perfectly as it is separated in tons of modules that we can load whenever we want (funktionale-either
is what we want here)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I took the Kotlin blog implementation :), I renamed from Either to Result because a lot of people don't know what Either means. But I will import funktionale-either.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is your opinion about limit the Left type to Error except open to any kind of type?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Either represents the disjunction mathematical concept where the left side of the expression can be an error or not. I'd not force the usage of an error in the left side.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we are going to use always for symbolizing an error? is not good limit the inheritance?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Most of the times we will use it with errors, but not always. In this project, we don't even need to use errors. If you want to port the original version of this kata you don't even need to use Either
or Result
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm asking for learn :) beacuse If the first time that I implement this kind of things. I like understand why we do some things. Original kata is not writting in kotlin, I like adjust the code to kotlin for learn and discover different ways to develop. Remember that the swift Kata is not similar to the Java one ;)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't read me wrong. I'm just trying to give you my feedback as you requested. If you read the Swift implementation of this kata we don't handle errors. If you have any doubt or any concrete question I did not answer, please let me know.
package com.karumi.ui.domain.model | ||
|
||
data class SuperHero(val name: String, | ||
val photo: String, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I remember well. The Marvel API has the picture field as optional.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the problem is if this is optional picasso crash, what do you prefer? check in ui or check in the respository layer?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you change the picture to optional you'll have to check if the picture is defined before using it in picasso.
|
||
class GetSuperHeroes { | ||
|
||
val mockSuperHero = SuperHero(name = "Super Peter", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the original implementation, we have a simple repository and the values it returns are private. I've read the repository will be your next PR but we should keep this as private.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, this line will be dissappear in the next PR, but yes, this is a bug.
|
||
fun get(): Result<List<SuperHero>> { | ||
return Result.success(arrayListOf(mockSuperHero)) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't even need {
}
here. You can use just a =
and return your value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, thank you!
|
||
private fun refreshSuperHeroes() { | ||
async { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The usage of async/await is so cool and so fancy that I love it ❤️ But I was wondering if we can easily configure an idling resource. I've been reading the documentation and I don't see any reference to this topic. If this is the case, we will need to create our own idling resource. For this kata this approach is ok, but w/o the idling resource I'd not use this library.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the kata is not mandatory, and I like try wrap the async/await in an object. Let me check one idea that I have :P
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the kata is not mandatory. That's why I don't request changes and just added comments 😃 I've created a new entry in our outreach ideas log for implementing this.
val result = await { getSuperHeroes.get() } | ||
view()?.hideLoading() | ||
when (result) { | ||
is Right -> view()?.showSuperHeroes(result.value) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are not implementing one of the two main kata behaviors. The empty case implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
because is out of the scope of the PR. I will add in a next iteration.
import com.karumi.ui.domain.Result | ||
import com.karumi.ui.domain.model.SuperHero | ||
|
||
class GetSuperHeroes { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As this language is less verbose you can create a file named usecases.kt
and move the two use cases we will have to this file. You can even name it domain
and move your abstract data types and use cases there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not for me, one of the good part of the Use cases takes a look and check all operations that we can do over our domain, if we put all together in one file this file can grow to fast and is not a benefict for read it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok
instance()) | ||
} | ||
|
||
bind<GetSuperHeroes>() with provider { GetSuperHeroes() } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you need to bind a class even when the constructor is empty?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes this is mandatory :( this is one of the things that I don't like from kodein.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then I'd not use this dependency injector. If we have to define a dependency every time we need it it's going to be a pain in the ass. Imagine you need to use this use case in two different places and this use case depends on a repository. Based on this library you'll need to write this code twice:
bind<GetSuperHeroes>() with provider { GetSuperHeroes(new SuperHeroesRepository()) }
When the dependency tree is simple, this is not an issue but in the future, this will be a pain in the ass. We could reduce the boilerplate by moving this declaration to a module configured just one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I have a general module, commons implementation must to be in general module. I am ok with you that it's not the best solution. I don't know why does not automatic.
} | ||
|
||
private fun renderSuperHeroPhoto(photo: String) { | ||
Picasso.with(context).load(photo).fit().centerCrop().into(photoImageView) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remember to check if the photo is defined. The Marvel API declares this attribute as optional but we are not handling it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I repeat the same question, if we manage the nullability here or in the api. And remember that the kata don't use internet the data comes from hardcoded info in the repositories.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Choose the one you prefer. By definition the picture is optional. If I were you, I'd declare the photo as an optional and I'd check the definition of the photo value in this method, in the UI layer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm trying to know if it's a best option resolve the nullabilities as soon as we found, in this case the api layer or manage in the ui layer. :) I trying to learn because you have more experience with this kind of things.
@@ -7,7 +7,7 @@ buildscript { | |||
jcenter() | |||
} | |||
dependencies { | |||
classpath 'com.android.tools.build:gradle:3.0.0-beta4' | |||
classpath 'com.android.tools.build:gradle:3.0.0-beta6' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to use the beta version?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, do yo ask the same in the previous PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't remember why it's needed, but don't worry. I'll review the previous PR.
Well done @flipper83 😃 As this project aims to be a reference for testing purposes I'd recommend you to start writing tests ASAP. Part of your design will be directly affected by your tests implementation. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is looking awesome, thank you for your work Jorge
fun <R> success(value: R) = Result.Right(value) | ||
fun <R> error(error: Error) = Result.Left<R>(error) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've been using funKTionale and it fits perfectly as it is separated in tons of modules that we can load whenever we want (funktionale-either
is what we want here)
isAvenger = true, | ||
photo = "https://avatars1.githubusercontent.com/u/4030704") | ||
|
||
fun get(): Result<List<SuperHero>> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have you considered implementing the invoke
operator so that they look like functions instead of having to implement a get
, execute
, run
method?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
of course https://kotlinlang.org/docs/reference/operator-overloading.html I will try it.
This pull request contains the first commit with information come from use cases.
I like update the PR step by steps because I have some code that I like your approval :)
This first commit contains usercases, list representation and resultValues.
Next commit will contains a list test and travis configuration.
After that I will start working on repository code.