• No se han encontrado resultados

Proceso de implantación del programa

3.4. Curso de formación del profesorado

Many developers believe that one of the most important qualities of good code is the lack of duplication. There’s even a special name for this principle: Don’t Repeat Yourself (DRY). But when you write in Java, following this principle isn’t always trivial. In many cases, it’s possible to use the Extract Method refactoring feature of your IDE to break longer methods into smaller chunks, and then to reuse those chunks. But this can make code more difficult to understand, because you end up with a class with many small methods and no clear relationship between them. You can go even further and group the extracted methods into an inner class, which lets you maintain the structure, but this approach requires a significant amount of boilerplate.

Kotlin gives you a cleaner solution: you can nest the functions you’ve extracted in the containing function. This way, you have the structure you need without any extra syntactic overhead.

Let’s see how to use local functions to fix a fairly common case of code duplication.

In the following example, a function saves a user to a database, and you need to make sure the user object contains valid data:

NOTE Note

You can now see that extension functions are a powerful way to extend the APIs of existing libraries and to adapt them to the idioms of your new language—something called the "Pimp my Library"

pattern.9And indeed, a large portion of the Kotlin standard library is made up of extension functions for standard Java classes. The Anko library 10, also built by JetBrains, provides extension functions that make the Android API more Kotlin-friendly. You can also find many community-developed libraries that provide Kotlin-friendly wrappers around major third-party libraries such as Spring.

Footnote 9 Martin Odersky, "Pimp My Library," Artima Developer, October 9, 2006,http://http://mng.bz/86Qh.

Footnote 10 https://github.com/kotlin/anko.

class User(val id: Int, val name: String, val address: String)

fun saveUser(user: User) { if (user.name.isEmpty()) {

throw IllegalArgumentException(

"Cannot save user ${user.id}: Name is empty") }

if (user.address.isEmpty()) { throw

IllegalArgumentException(

"Cannot save user ${user.id}: Address is empty") }

// Save user to the database }

>>> saveUser(User(1, "", ""))

java.lang.IllegalArgumentException: Cannot save user 1: Name is empty

Field validation is duplicated.

The amount of duplicated code here is fairly small, and you probably won’t want to have a full-blown method in your class that handles one special case of validating a user.

But if you put the validation code into a local function, you can get rid of the duplication and still maintain a clear code structure. Here’s how it works:

Declares a local function to validate any field

Calls the local function to validate the specific fields

This looks better. The validation logic isn’t duplicated, and you can easily add more validations if you need to add other fields to User as the project evolves. But having to pass theUser object to the validation function is somewhat ugly. The good news is that

class User(val id: Int, val name: String, val address: String) fun saveUser(user: User) {

fun validate(user: User, value: String, fieldName: String) { if (value.isEmpty()) {

throw IllegalArgumentException(

"Cannot save user ${user.id}: $fieldName is empty") } }

validate(user, user.name, "Name") validate(user, user.address, "Address") // Save user to the database

}

it’s entirely unnecessary, because local functions have access to all parameters and variables of the enclosing function. Let’s take advantage of that and get rid of the extra

Userparameter:

Now you don’t duplicate the "user" parameter of the saveUser function.

You can access parameters of the outer function directly.

To improve this example even further, you can move the validation logic into an extension function of theUserclass:

You can access properties of User directly.

Calls the extension function

class User(val id: Int, val name: String, val address: String) fun saveUser(user: User) {

fun validate(value: String, fieldName: String) { if (value.isEmpty()) {

throw IllegalArgumentException(

"Can't save user ${user.id}: " +

"$fieldName is empty") } }

validate(user.name, "Name") validate(user.address, "Address") // Save user to the database }

>>> saveUser(User(1, "", ""))

java.lang.IllegalArgumentException: Cannot save user 1: Name is empty

class User(val id: Int, val name: String, val address: String) fun User.validateBeforeSave() {

fun validate(value: String, fieldName: String) { if (value.isEmpty()) {

throw IllegalArgumentException(

"Can't save user $id: empty $fieldName") } }

// Save user to the database }

Extracting a piece of code into an extension function turns out to be surprisingly useful. Even though User is a part of your codebase and not a library class, you don’t want to put this logic into a method ofUser because it’s not relevant to any other places where User is used. Therefore, the API of the class contains only the essential methods used everywhere, so the class remains small and easy to wrap your head around. On the other hand, functions that primarily deal with a single object and don’t need access to its private data can access its members without extra qualification, as in the previous example.

Extension functions can also be declared as local functions, so you could go even further and put User.validateBeforeSave() as a local function in saveUser(). But deeply nested local functions are usually fairly hard to read; so, as a general rule, we don’t recommend using more than one level of nesting.

Having looked at all the cool things you can do with functions, in the next chapter we’ll look at what you can do with classes.

3.7 Summary

Kotlin doesn’t define its own collection classes and instead extends the Java collection classes with a richer API.

Defining default values for function parameters greatly reduces the need to define overloaded functions, and the named-argument syntax makes calls to functions with many parameters much more readable.

Functions and properties can be declared directly in a file, not just as members of a class, allowing for a more flexible code structure.

Extension functions and properties let you extend the API of any class, including classes defined in external libraries, without modifying its source code and with no runtime overhead.

Infix calls provide a clean syntax for calling operator-like methods with a single argument.

Kotlin provides a large number of convenient string-handling functions for both regular expressions and plain strings.

Triple-quoted strings provide a clean way to write expressions that would require a lot of noisy escaping and string concatenation in Java.

Local functions help you structure your code more cleanly and eliminate duplication.