Reader small image

You're reading from  Object???Oriented Programming with Swift 2

Product typeBook
Published inJan 2016
Reading LevelIntermediate
Publisher
ISBN-139781785885693
Edition1st Edition
Languages
Tools
Right arrow
Author (1)
Gaston C. Hillar
Gaston C. Hillar
author image
Gaston C. Hillar

Gaston C. Hillar is Italian and has been working with computers since he was 8 years old. Gaston has a Bachelor's degree in computer science (graduated with honors) and an MBA. Currently, Gaston is an independent IT consultant and a freelance author who is always looking for new adventures anywhere in the world. He was a senior contributing editor at Dr. Dobb's, and has written more than a hundred articles on software development topics. He has received the prestigious Intel Black Belt Software Developer award eight times. He has written many articles about Java for Oracle Java Magazine. Gaston was also a former Microsoft MVP in technical computing. He lives with his wife, Vanesa, and his two sons, Kevin and Brandon.
Read more about Gaston C. Hillar

Right arrow

Chapter 7. Object-Oriented Programming and Functional Programming

In this chapter, we will refactor an existing code that doesn't use an object-oriented programming approach and make it easier to understand, expand, and maintain. We will discuss functional programming and how Swift implements many functional programming concepts. We will work with many examples of how to mix functional programming with object-oriented programming.

Refactoring code to take advantage of object-oriented programming


Sometimes, we are extremely lucky and have the possibility to follow best practices as we kick off a project. If we start writing object-oriented code from scratch, we can take advantage of all the features that we used in our examples throughout the book. As the requirements evolve, we might need to further generalize or specialize the blueprints. However, as we started our project with an object-oriented approach and by organizing our code, it is easier to make adjustments to the code.

Most of the time, we aren't extremely lucky and have to work on projects that don't follow best practices, and we, in the name of agility, generate pieces of code that perform similar tasks but without decent organization. Instead of following the same bad practices that generate error-prone, repetitive, and difficult-to-maintain code, we can use the features provided by Xcode and additional helper tools to refactor existing code and generate...

Understanding functions as first-class citizens


Swift is a multiparadigm programming language, and one of its supported programming paradigms is functional programming. Functional programming favors immutable data and, therefore, avoids state changes. The code written with a functional programming style is as declarative as possible, and it is focused on what it does instead of how it must do it.

As it happens in many modern programming languages, functions are first-class citizens in Swift. You can use functions as arguments for other functions or methods. We can easily understand this concept with a simple example: array filtering. However, take into account that we will start by writing imperative code with functions as first-class citizens, and then, we will create a new version for this code that uses a functional approach in Swift through a filter operation.

The following lines declare the applyFunctionToNumbers function that receives an array of Int and numbers and a function type,...

Working with function types within classes


The following lines declare a myFunction variable with a function type—specifically, a function that receives an Int argument and returns a Bool value. The variable works in the same way as an argument that specifies a function type for a function:

var myFunction: (Int -> Bool)
myFunction = divisibleBy5
let myNumber = 20
print("Is \(myNumber) divisible by 5: \(myFunction(myNumber))")

Then, the code assigns the divisibleBy5 function to myFunction. It is very important to understand that the line doesn't call the divisibleBy5 function and save the result of this call in the myFunction variable. Instead, it just assigns the function to the variable that has a function type. The lack of a parenthesis after the function name makes the difference.

Then, the code prints whether the Int value specified in the myNumber constant is divisible by 5 or not using the myFunction variable to call the referenced function with myNumber as an argument.

The following...

Creating a functional version of array filtering


The collections included in Swift allow us the use of higher order functions—that is, functions that take other functions and use them to perform transformations on datasets. For example, an array provides us with the filter, map, and reduce methods.

As previously explained, the preceding code represents an imperative version of array filtering. We can achieve the same goal with a functional approach using the filter method included in all the types that conform to the SequenceType protocol. The Array<Element> struct conforms to the SequenceType protocol and many other protocols.

Tip

As it happens in most modern languages, Swift supports closures, which are also known as anonymous functions. Closures are self-contained blocks of functionality that we can pass around and use within our code as functions without names. Closures automatically capture everything we reference, such as variables and functions that aren't defined within the closure...

Writing equivalent closures with simplified code


It is possible to omit the type for the closure's parameter and return type. The following lines show a simplified version of the previously shown code that generates the same result. Note that the closure code is really simplified and doesn't even include the return statement because it uses an implicit return. Swift evaluates the code we write after the in keyword and returns its evaluation as if we included the return statement before the expression. Swift infers the return type:

public func filterNumbersByCondition(condition: Int -> Bool) -> [Int] {
    return numbersList.filter({
       (number) in condition(number)
    })
}

We can go a step further and use the argument shorthand notation. This way, the closure omits the type for the parameters and its return type, takes advantage of implicit returns, and also uses the argument shorthand notation. The dollar sign followed by the argument number identifies each of the arguments for...

Creating a data repository with generics and protocols


Now, we want to create a repository that provides us with entities so that we can apply the functional programming features included in Swift to retrieve and process data from these entities. First, we will create an EntityProtocol protocol that defines the requirements for an entity. We want any class that conforms to this protocol to have a read-only id property of the Int type to provide a unique identifier for the entity:

public protocol EntityProtocol {
    var id: Int { get }
}

The next lines create a Repository<T> generic class that specifies that T must conform to the recently created EntityProtocol protocol in the generic type constraint. The class declares a getAll method that we will override in the subclasses:

public class Repository<T: EntityProtocol> {
    public func getAll() -> [T] {
        return [T]()
    }
}

The next lines create the Entity class, which is the base class for all the entities. The class...

Filtering arrays with complex conditions


We can use our new repository to restrict the results retrieved from more complex data. In this case, the getAll method returns an array of Game instances that we can use with the filter method to retrieve only the games that match certain conditions. The following lines declare a new getGamesWithHighestScoreGreaterThan method for our previously coded GameRepository class:

public func getGamesWithHighestScoreGreaterThan(score: Int) -> [Game] {
    return getAll().filter({ (game) in game.highestScore > score })
}

The getGamesWithHighestScoreGreaterThan method receives a score: Int argument and returns Array<Game>. The code calls the getAll and filter methods for the result with a closure that specifies the required condition for the games in the array to be returned in the new array. In this case, only the games whose highestScore value is greater than the score value received as an argument will appear in the resulting Array<Game>...

Using map to transform values


The map method takes a closure as an argument, calls it for each item in the array, and returns a mapped value for the item. The returned mapped value can be of a different type from the item's type.

The following lines declare a new getGamesNames method for our previously coded GameRepository class that performs the simplest map operation:

public func getGamesNames() -> [String] {
    return getAll().map({ game in game.name.uppercaseString })
}

The getGamesNames parameterless method returns Array<String>. The code calls the getAll method and calls the map method for the result with a closure that returns the name value for each game converted to uppercase. This way, the map method transforms each Game instance into a String with its name converted to uppercase. The result is an Array<String> generated by the call to the map method.

The following line uses the GameRepository instance called gameRepository to call the previously added getGamesNames...

Combining map with reduce


The following lines show an imperative code version of a for in loop that calculates the sum of all the highestScore values for the games:

var sum = 0
for game in gameRepository.getAll() {
    sum += game.highestScore
}
print(sum)

The code is very easy to understand. The sum variable has a starting value of 0, and each iteration of the for in loop retrieves a Game instance from the Array<Game> returned by the gameRepository.getAll method and increases the value of the sum variable with the value of the highestScore property.

We can combine the map and reduce operations to create a functional version of the previous imperative code to calculate the sum of all the highestScore values for the games. The next lines chain a call to map to a call to reduce to achieve this goal. Take a look at the following code:

let highestScoreSum = gameRepository.getAll().map({ $0.highestScore }).reduce(0, combine: {
    sum, highestScore in
    return sum + highestScore
})
print...

Chaining filter, map, and reduce


We can chain filter, map, and reduce. The following lines declare a new calculateGamesHighestScoresSum method for our previously coded GameRepository class that chains filter, map, and reduce calls:

public func calculateGamesHighestScoresSum(minPlayedCount: Int) -> Int {
    return getAll().filter({ $0.playedCount >= minPlayedCount }).map({ $0.highestScore }).reduce(0) {
        sum, highestScore in
        return sum + highestScore
    }
}

The calculateGamesHighestScoresSum method receives a minPlayedCount argument of the Int type and returns an Int value. The code calls the getAll and filter methods to generate a new Array<Game> with only the Game instances, whose playedCount value is greater than or equal to the value specified in the minPlayedCount argument. The code calls the map method to transform an Array<Game> into an Array<Int> with the values specified in the highestScore stored property. Then, the code calls the reduce method...

Solving algorithms with reduce


We can chain solve algorithms with reduce by following a functional approach. The following lines declare a new getSeparatedGamesNames method for our previously coded GameRepository class that solves an algorithm by calling the reduce method:

public func getSeparatedGamesNames(separator: String) -> String {
    let gamesNames = getGamesNames()
    return gamesNames.reduce("") {
        concatenatedGameNames, gameName in
        print(concatenatedGameNames)
        let separatorOrEmpty = (gameName == gamesNames.last) ? "" : separator
        return "\(concatenatedGameNames)\(gameName)\(separatorOrEmpty)"
    }
}

The getSeparatedGamesNames method receives a separator argument of the String type and returns a String value. The code calls the getGamesNames method and saves the result in the gamesNames reference constant. Then, the code calls the reduce method with an empty string as the initial value for an accumulated value. The code uses a trailing closure to...

Exercises


Add new methods to the GameRepository class we created in this chapter. Make sure you create a new method to solve each algorithm and that you use a functional programming approach:

  • Retrieve all the games whose average score is lower than a maximum average score received as an argument.

  • Generate a string with the first letter of each game name followed by the highest score value. Use a hyphen as a separator for each game name and highest score value pair. That last value pair shouldn't include a hyphen after it.

  • Calculate the minimum playedCount value.

  • Calculate the maximum playedCount value.

Test your knowledge


  1. The { (game: Game) -> Bool in game.highestScore == highestScore && game.playedCount == playedCount } closure is equivalent to:

    1. { $0.highestScore == highestScore && $1.playedCount == playedCount }

    2. { $0.highestScore == highestScore && $0.playedCount == playedCount }

    3. { 0 -> 0.highestScore == highestScore && 0.playedCount == playedCount }

  2. The closure { return condition($0) } is equivalent to:

    1. { (number: Int) -> Bool in return condition(number) }

    2. { (number -> Bool) -> Int in condition <- (number) }

    3. { 0 -> condition(number) }

  3. A function type specifies:

    1. The parameter and return types for the function.

    2. Only the parameter names required for the function.

    3. The required function name and the return value without any details about the parameters.

  4. Which of the following lines declare a variable with a function type:

    1. var condition: { 0 -> Int -> Bool }

    2. var condition: Int $0 returns Bool

    3. var condition: (Int -> Bool)

  5. After...

Summary


In this chapter, you learned how to refactor existing code to take full advantage of object-oriented code. We prepared the code for future requirements, reduced maintenance cost, and maximized code reuse.

We worked with many functional programming features included in Swift and combined them with everything we discussed so far about object-oriented programming. We analyzed the differences between imperative and functional programming approaches for many algorithms.

Now that you have learned about refactoring code to take advantage of object-oriented programming and include functional programming pieces in our object-oriented code, we are ready to extend and build object-oriented code, which is the topic of the next chapter.

lock icon
The rest of the chapter is locked
You have been reading a chapter from
Object???Oriented Programming with Swift 2
Published in: Jan 2016Publisher: ISBN-13: 9781785885693
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
undefined
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $15.99/month. Cancel anytime

Author (1)

author image
Gaston C. Hillar

Gaston C. Hillar is Italian and has been working with computers since he was 8 years old. Gaston has a Bachelor's degree in computer science (graduated with honors) and an MBA. Currently, Gaston is an independent IT consultant and a freelance author who is always looking for new adventures anywhere in the world. He was a senior contributing editor at Dr. Dobb's, and has written more than a hundred articles on software development topics. He has received the prestigious Intel Black Belt Software Developer award eight times. He has written many articles about Java for Oracle Java Magazine. Gaston was also a former Microsoft MVP in technical computing. He lives with his wife, Vanesa, and his two sons, Kevin and Brandon.
Read more about Gaston C. Hillar