How to write Claen code
if를 써서 분기를 나누지말고 When을 사용하자.
//Don't
fun getProvinceByCityName(cityName: String): String {
val cityNameLower = cityName.toLowerCase()
if (cityName == "karachi" || cityName == "hyderabad") {
return "Sindh"
}
if (cityName == "lahore" || cityName == "faisalabad") {
return "Punjab"
}
if (cityName == "islamabad") {
return "Islamabad"
}
return "unknown"
}
//Do
fun getProvinceByCityName(cityName: String) = when (cityName.toLowerCase()) {
"karachi", "hyderabad" -> "Sindh"
"lahore", "faisalabad" -> "Punjab"
"islamabad" -> "Islamabad"
else -> "unknown"
}
Top-Level (Extension) Functions for Utility Functions
//Don't
object StringUtil {
fun countAmountOfZ(string: String): Int{
return string.length - string.replace("z", "").length
}
}
StringUtil.countAmountOfX("zFunzWithzKotlinz")
//Do
fun String.countAmountOfZ(): Int {
return length - replace("z", "").length
}
"zFunzWithzKotlinz".countAmountOfX()
객체 생성시 default값을 바로 넣을 수 있다.
//Don't
val config = Search()
.setTerm("game of thrones")
.setRecursive(true)
.setFollowSymlinks(true)
//Do
val config2 = Search(
term = "game of thrones",
recursive = true,
followSymlinks = true
)
apply()를 사용하자.
//Don't
val dataSource = BasicDataSource()
dataSource.name = "User 1"
dataSource.url = "www.xyz.com"
dataSource.username = "username"
dataSource.password = "password"
//Do
val dataSource = BasicDataSource().apply {
name = "User 1"
url = "www.xyz.com"
username = "username"
password = "password"
}
Don’t Overload for Default Arguments
//Don't
fun find(name: String){
find(name, true)
}
fun find(name: String, recursive: Boolean){}
//Do
fun find(name: String, recursive: Boolean = true){}
Nullablity를 가볍게 따루자. 엘비스를 이용하여. if == null을 피하자.
//Don't
if (order == null || order.customer == null || order.customer.address == null){
throw IllegalArgumentException("Invalid Order")
}
val city = order.customer.address.city
//Do
val city =
order?.customer?.address?.city ?: throw IllegalArgumentException("Invalid Order")
//Don't
if (service !is MyService) {
throw IllegalArgumentException("No MyService")
}
service.getData()
//Do
service as? MyService ?: throw IllegalArgumentException("No MyService")
service.getData()
//Don't
order!!.customer!!.address!!.city
let()을 활용하자.
findOrder()?.let { dun(it.customer) }
//or
findOrder()?.customer?.let(::dun)
Concise Mapping with Single Expression Functions
// Don't
fun mapToDTO(entity: SnippetEntity): SnippetDTO {
val dto = SnippetDTO(
code = entity.code,
date = entity.date,
author = "${entity.author.firstName} ${entity.author.lastName}"
)
return dto
}
// Do
fun mapToDTO(entity: SnippetEntity) = SnippetDTO(
code = entity.code,
date = entity.date,
author = "${entity.author.firstName} ${entity.author.lastName}"
)
val dto = mapToDTO(entity)
// Do
fun SnippetEntity.toDTO() = SnippetDTO(
code = code,
date = date,
author = "${author.firstName} ${author.lastName}"
)
val dto = entity.toDTO()
Sealed Class를 활용하여 예외처리를 하자.
// Definition
sealed class UserProfileResult {
data class Success(val userProfile: UserProfileDTO) : UserProfileResult()
data class Error(val message: String, val cause: Exception? = null) : UserProfileResult()
}
// Usage
val avatarUrl = when (val result = client.requestUserProfile(userId)) {
is UserProfileResult.Success -> result.userProfile.avatarUrl
is UserProfileResult.Error -> "http://domain.com/defaultAvatar.png"
}