Абстракции на основе протоколов
Зависите от протоколов, а не от конкретных типов.
«Абстракции на основе протоколов» — бесплатный урок Swift Academy на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Swift Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Swift Academy содержит 4 уроков всего.
Абстракции на основе протоколов
Внедрение протокола вместо конкретного типа позволяет свободно заменять реализации. Потребитель зависит только от поведения, а не от класса.
Определение протокола
Опишите возможность с помощью протокола. В качестве зависимости можно передать любой соответствующий ему тип.
protocol Logging {
func log(_ message: String)
}
print("Protocol describes behavior, not a concrete class.")Соответствие конкретного типа
Настоящая реализация соответствует протоколу. Именно она используется как зависимость в рабочем приложении.
protocol Logging { func log(_ m: String) }
struct ConsoleLogger: Logging {
func log(_ m: String) { print("LOG: \(m)") }
}
ConsoleLogger().log("ready")Зависимость от протокола
Тип свойства потребителя — это протокол, поэтому оно принимает любое соответствующее ему значение.
protocol Logging { func log(_ m: String) }
struct ConsoleLogger: Logging { func log(_ m: String) { print(m) } }
struct Service {
let logger: Logging // protocol, not ConsoleLogger
func run() { logger.log("running") }
}
Service(logger: ConsoleLogger()).run()Замена реализаций
Поскольку типом является протокол, можно передать другую соответствующую ему реализацию, не изменяя потребитель.
protocol Logging { func log(_ m: String) }
struct Loud: Logging { func log(_ m: String) { print(m.uppercased()) } }
struct Quiet: Logging { func log(_ m: String) {} }
struct Service { let logger: Logging; func run() { logger.log("hi") } }
Service(logger: Loud()).run()
Service(logger: Quiet()).run()Программирование на основе интерфейсов
Это принцип «зависите от абстракций, а не от конкретных реализаций». Код высокого уровня защищён от низкоуровневых деталей.
protocol Storage { func save(_ v: Int) }
struct MemoryStore: Storage { func save(_ v: Int) { print("kept \(v)") } }
struct Manager { let store: Storage; func add() { store.save(1) } }
Manager(store: MemoryStore()).add()Несколько соответствующих типов
Один и тот же протокол могут реализовывать несколько типов, каждый из которых подходит для своего контекста: сети, диска или памяти.
protocol Source { func value() -> Int }
struct Fixed: Source { func value() -> Int { 10 } }
struct Doubler: Source { func value() -> Int { 20 } }
func use(_ s: Source) { print(s.value()) }
use(Fixed())
use(Doubler())Протоколы со связанным поведением
Протоколы могут объявлять несколько методов, формируя ясный контракт для зависимости.
protocol Cache {
func get(_ key: String) -> Int?
func set(_ key: String, _ v: Int)
}
struct Dict: Cache {
var store: [String: Int] = [:]
func get(_ k: String) -> Int? { store[k] }
mutating func set(_ k: String, _ v: Int) { store[k] = v }
}
var c = Dict(); c.set("a", 1)
print(c.get("a") ?? -1)Ограничения обобщений
Зависимость также можно внедрить через обобщённый параметр с ограничением на протокол, избежав накладных расходов на работу с экзистенциальным типом.
protocol Greeter { func greet() -> String }
struct English: Greeter { func greet() -> String { "Hello" } }
struct Welcome<G: Greeter> {
let greeter: G
func show() { print(greeter.greet()) }
}
Welcome(greeter: English()).show()Стабильные контракты
Пока протокол остаётся стабильным, реализации могут меняться внутри, не нарушая работу потребителей.
protocol Formatter { func format(_ n: Int) -> String }
struct Plain: Formatter { func format(_ n: Int) -> String { "\(n)" } }
struct Padded: Formatter { func format(_ n: Int) -> String { String(format: "%03d", n) } }
func render(_ f: Formatter) { print(f.format(7)) }
render(Plain())
render(Padded())Подготовка к созданию имитаций
Особенно важно, что протоколы позволяют тестам подставлять имитацию вместо настоящей реализации. Именно этому посвящён следующий урок.
protocol Network { func fetch() -> String }
struct Real: Network { func fetch() -> String { "server data" } }
struct VM { let net: Network; func load() { print(net.fetch()) } }
VM(net: Real()).load()Быстрая проверка
Зачем внедрять тип протокола вместо конкретного класса?
Итоги
Внедрение протокола скрывает конкретный тип, поэтому вместо него можно подставить любую соответствующую реализацию. Это соответствует принципу «зависите от абстракций» и позволяет использовать альтернативные реализации и, что особенно важно, тестовые двойники. Далее: имитация зависимостей в тестах.
Часто задаваемые вопросы
Урок «Абстракции на основе протоколов» бесплатный?
Да — полный текст урока «Абстракции на основе протоколов» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Swift Academy, подпишись на CoddyKit PRO. Курс Swift Academy содержит 4 уроков всего.
Чему я научусь в уроке «Абстракции на основе протоколов»?
Зависите от протоколов, а не от конкретных типов. Ты практикуешь Swift Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Swift Academy?
Предыдущий опыт не требуется. Swift Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.
Сколько времени занимает урок «Абстракции на основе протоколов»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Swift Academy?
Да. Каждый урок Swift Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Зачем нужна инъекция зависимостей
- Инъекция через конструктор
- Абстракции на основе протоколов
- Имитация зависимостей в тестах