Каковы основные различия между протоколом и абстрактным классом в Swift?

«Каковы основные различия между протоколом и абстрактным классом в Swift?» — вопрос из категории ООП, который задают на 22% собеседований IOS Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

В Swift протокол определяет контракт (интерфейс), а абстрактный класс — это шаблон для наследования, который сам по себе языком не поддерживается напрямую, но эмулируется. Вот ключевые различия:

Сравнительная таблица

Критерий Протокол (Protocol) Абстрактный класс (Эмуляция)
Наследование Поддерживает множественное наследование (тип может соответствовать многим протоколам). Поддерживает только одиночное наследование.
Кто может использовать Классы, структуры (struct), перечисления (enum). Только классы.
Реализация по умолчанию Есть через расширения (extension). Может содержать реализованные и абстрактные (нереализованные) методы в одном классе.
Хранимые свойства Не может иметь хранимые свойства, только требования. Может иметь хранимые свойства.
Инициализаторы Может требовать инициализатор (init), но не может его реализовать. Может иметь назначенные и convenience инициализаторы.
Тип-значение / ссылка Агностичен (работает и со value, и с reference типами). Работает только с reference типами (классами).

Примеры

// 1. Протокол с расширением для реализации по умолчанию
protocol Vehicle {
    var fuelLevel: Double { get set }
    func startEngine() // Требование
}

extension Vehicle {
    func startEngine() { // Реализация по умолчанию
        print("Engine started for a generic vehicle.")
    }
}

struct Car: Vehicle {
    var fuelLevel: Double
    // startEngine() можно не реализовывать, используется дефолтная версия
}

// 2. Эмуляция абстрактного класса
class AbstractShape {
    // "Абстрактный" метод - требует переопределения
    func area() -> Double {
        fatalError("Subclass must override area()")
    }
    // Конкретный метод
    func description() -> String {
        return "Shape with area: (area())"
    }
}

class Square: AbstractShape {
    let side: Double
    init(side: Double) { self.side = side }
    override func area() -> Double { // Обязательное переопределение
        return side * side
    }
}

// let shape = AbstractShape() // Вызовет fatalError
let square = Square(side: 5)
print(square.description()) // "Shape with area: 25.0"

Вывод: Протоколы в Swift более гибкие и являются предпочтительным способом определения интерфейсов и поведения (протокол-ориентированное программирование). Абстрактные классы используются реже, в основном когда нужна общая базовая реализация с хранимым состоянием для иерархии классов.