Default implementations

As you learned in the previous chapter, a protocol defines a contract for any type that adopts it. If a protocol defines a method or a property, any type that adopts the protocol must implement that method or property.

Consider another example of a TeamRecord type:

struct BasketballRecord: TeamRecord { var wins: Int

var losses: Int

let seasonLength = 82

var winningPercentage: Double {

return Double(wins) / (Double(wins) + Double(losses))

}

}

Both BasketballRecord and BaseballRecord have identical implementations of winningPercentage. You can imagine that most of the TeamRecord types will implement this property the same way. That could lead to a lot of repetitive code.

Fortunately, Swift has a shortcut:

extension TeamRecord {

var winningPercentage: Double {

return Double(wins) / (Double(wins) + Double(losses))

}

}

While this is much like the protocol extension you defined in the previous example, it differs in that winningPercentage is a member of the TeamRecord protocol itself, whereas gamesPlayed wasn’t. Implementing a member of a protocol in an extension creates a default implementation for that member.

You’ve already seen default arguments to functions, and this is similar: If you don’t

implement winningPercentage in your type, it will use the default implementation provided by the protocol extension.

In other words, you no longer need to explicitly implement winningPercentage on types that adopt TeamRecord:

struct BasketballRecord: TeamRecord { var wins: Int

var losses: Int

let seasonLength = 82

}

let minneapolisFunctors = BasketballRecord(wins: 60, losses: 22) minneapolisFunctors.winningPercentage

Default implementations let you add a capability to a protocol while greatly reducing repeated or “boilerplate” code.

A default implementation doesn’t prevent a type from implementing a protocol member on its own. Some team records may require a slightly different formula for the winning percentage, such as a sport that includes ties as a possible outcome:

struct HockeyRecord: TeamRecord { var wins: Int

var losses: Int var ties: Int

// Hockey record introduces ties, and has

// its own implementation of winningPercentage var winningPercentage: Double {

return Double(wins) / (Double(wins) + Double(losses) + Double(ties))

}

}

Now, if you call winningPercentage on a TeamRecord that’s a HockeyRecord value type, it will calculate the winning percentage as a function of wins, losses and ties. If you call winningPercentage on another type that doesn’t have its own implementation, it will fall back to the default implementation:

let chicagoOptionals = BasketballRecord(wins: 10, losses: 6)

let phoenixStridables = HockeyRecord(wins: 8, losses: 7, ties: 1)

chicagoOptionals.winningPercentage // 10 / (10 + 6) == .625

phoenixStridables.winningPercentage // 8 / (8 + 7 + 1) == .500

results matching ""

    No results matching ""