Menu
Coddy logo textTech

Расширения протоколов

Часть раздела Объектно-ориентированное программирование путешествия по Swift на Coddy. Урок 29 из 57.

Расширение протокола предоставляет методам протокола реализацию по умолчанию. Каждый соответствующий протоколу тип получает её бесплатно, при этом тип всё ещё может предоставить собственную версию:

protocol Greeter {
    var name: String { get }
    func greet() -> String
}

extension Greeter {
    func greet() -> String { "Hello, \(name)!" }
}

struct English: Greeter { let name: String }
struct Pirate: Greeter {
    let name: String
    func greet() -> String { "Ahoy, \(name)!" }
}

print(English(name: "Ada").greet())
print(Pirate(name: "Ada").greet())

Вывод:

Hello, Ada!
Ahoy, Ada!

Расширение также может добавлять новые методы, созданные только на основе требований. Тип, соответствующий протоколу, реализует небольшую обязательную часть и получает остальное:

protocol Scored {
    var points: [Int] { get }
}

extension Scored {
    var total: Int { points.reduce(0, +) }
    var best: Int { points.max() ?? 0 }
    func summary() -> String { "total \(total), best \(best)" }
}

struct Player: Scored {
    let points: [Int]
}

print(Player(points: [3, 9, 4]).summary())

Вывод:

total 16, best 9

Это позволяет совместно использовать поведение структурами, классами и перечислениями без наследования. Стандартная библиотека работает так же: каждый Collection получает isEmpty, first и map из расширений протокола.

extension Collection {
    // isEmpty, first, map, filter, ... все определены один раз здесь
    // и каждый array, set, dictionary и string получает их
}

Ограниченное расширение добавляет методы только для некоторых соответствующих типов, записываемое с помощью where:

protocol Container {
    associatedtype Item
    var items: [Item] { get }
}

extension Container where Item == Int {
    var sum: Int { items.reduce(0, +) }
}

struct Box: Container {
    var items: [Int]
}

print(Box(items: [2, 5]).sum)

Вывод:

7
challenge icon

Задание

Легко

Объявите протокол Priced, который требует наличия var price: Int { get } (в центах) и var formatted: String { get }. В расширении протокола задайте для formatted реализацию по умолчанию, возвращающую $4.50, и добавьте метод isAffordable(budget:), который возвращает, укладывается ли цена в бюджет. Сделайте так, чтобы структуры Coffee (цена 450) и Sandwich (с хранимым свойством price) соответствовали протоколу. Coffee должен заменить реализацию formatted по умолчанию на coffee $4.50.

Предоставленный код считывает бюджет, а затем строки coffee или sandwich,650. Для каждого товара он выводит formatted и yes или no.

Ваш код должен находиться в файлах Priced.swift, Coffee.swift и Sandwich.swift. В main.swift находится предоставленный код ввода/вывода, и его нельзя изменять.

Попробуйте сами

// Предоставленный код ввода/вывода: оставьте его как есть
var input: [String] = []
while let line = readLine() { input.append(line) }
let budget = Int(input[0])!
for line in input.dropFirst() {
    let item: any Priced = line == "coffee" ? Coffee() : Sandwich(price: Int(line.split(separator: ",")[1])!)
    print("\(item.formatted) \(item.isAffordable(budget: budget) ? "yes" : "no")")
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Песочница Swift