so simple there are obviously no bugs in it, or write code so complex that there are no obvious bugs in it. Tony Hoare The heart of software is its ability to solve domain-related problems for its user. Eric Evans
we don’t own. Before val name = mandatoryChild(node, "name") fun mandatoryChild(node: JsonNode, name: String): JsonNode = node.get(name) ?: throw MissingPropertyException(name) After val name = node.mandatoryChild(“name”) fun JsonNode.mandatoryChild(name: String): JsonNode = get(name) ?: throw MissingPropertyException(name)
to their use Before validateAddress(addressFrom(jsonNode)) After validateAddress(jsonNode.toAddress()) private fun JsonNode.toAddress() = Address( getRequired("street").asText(), getRequired("town").asText(), get("postcode")?.asPostCode(), getRequired("country").asCountryCode())
fun isInCountry(county: Country) = … fun isFreeDelivery() = … fun somethingTheUserInterfaceNeeds() = … fun somethingTheDatabaseNeeds() = … } Extension functions on our own types
… ) { fun isInCountry( … ) } Logic specific to a part of the business domain fun Address.isFreeDelivery() = UI Module fun Address.somethingTheUserInterfaceNeeds() = … Persistence Module fun Address.somethingTheDatabaseNeeds() = …
}) { Explaining variable val freeDeliveryAvailable = addressNodes.any { isFreeDelivery(addressFrom(it)) } if (freeDeliveryAvailable) { ... Extension function if (addressNodes.allowFreeDelivery()) { ... private inline fun List<JsonNode>.allowFreeDelivery() = any { it.toAddress().isFreeDelivery() }
newState = submission.copy(files=submission.files + newFile) After val newState = submission.plusFile(newFile) ... fun Submission.withNoFiles() = copy(files=emptyList()) fun Submission.withFile(f: File) = copy(files=listOf(f)) fun Submission.plusFile(f: File) = copy(files=files + f) Extension functions of data classes Use a common naming convention across the code base. E.g. "withXxx" replaces, "plusXxx" adds, "minusXxx" removes.
longer val submissionId = PathElement(::SubmissionId) val fileId = PathElement(::FileId) val submissionPath = "submission"/submissionId val submissionFilePath = submissionPath/"file"/fileId WTF?!?