Паттерны проектирования в Swift с примерами: от А до Я

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Паттерны проектирования в Swift: от основ до практики

Паттерны проектирования (Design Patterns) — это проверенные решения типовых задач разработки. В мире Swift и экосистемы Apple (iOS/macOS) их знание становится обязательным для создания поддерживаемого, тестируемого и масштабируемого кода. В этой статье мы разберем ключевые паттерны, разделив их на три основные категории: порождающие, структурные и поведенческие. Каждый паттерн будет проиллюстрирован рабочим примером на Swift.

Порождающие паттерны: Singleton, Factory Method и Builder

Порождающие паттерны отвечают за процесс создания объектов, делая его более гибким и независимым от клиентского кода.

Singleton (Одиночка)

Гарантирует, что у класса есть только один экземпляр, и предоставляет глобальную точку доступа к нему. В iOS часто используется для управления сетевыми сессиями, базами данных (Core Data) или настройками приложения.

class NetworkManager {
    static let shared = NetworkManager()
    private init() {}
    
    func fetchData(from url: URL) {
        // Логика запроса
        print("Fetching data from \(url)")
    }
}

// Использование
NetworkManager.shared.fetchData(from: URL(string: "https://example.com")!)

Factory Method (Фабричный метод)

Определяет интерфейс для создания объекта, но позволяет подклассам изменять тип создаваемого объекта. Отлично подходит для создания UI-компонентов в зависимости от iOS-версии или конфигурации.

protocol Button {
    func render()
}

class iOSButton: Button {
    func render() { print("Rendering iOS button") }
}

class macOSButton: Button {
    func render() { print("Rendering macOS button") }
}

enum Platform { case iOS, macOS }

class ButtonFactory {
    static func createButton(for platform: Platform) -> Button {
        switch platform {
        case .iOS: return iOSButton()
        case .macOS: return macOSButton()
        }
    }
}

// Использование
let button = ButtonFactory.createButton(for: .iOS)
button.render()

Builder (Строитель)

Позволяет пошагово создавать сложные объекты. Широко используется для конфигурации запросов (URLRequest) или сложных UI-настроек.

class Pizza {
    var dough: String = ""
    var cheese: String = ""
    var toppings: [String] = []
}

class PizzaBuilder {
    private var pizza = Pizza()
    
    func setDough(_ dough: String) -> PizzaBuilder {
        pizza.dough = dough
        return self
    }
    
    func setCheese(_ cheese: String) -> PizzaBuilder {
        pizza.cheese = cheese
        return self
    }
    
    func addTopping(_ topping: String) -> PizzaBuilder {
        pizza.toppings.append(topping)
        return self
    }
    
    func build() -> Pizza {
        return pizza
    }
}

// Использование
let margherita = PizzaBuilder()
    .setDough("Thin")
    .setCheese("Mozzarella")
    .addTopping("Basil")
    .build()

Структурные паттерны: Adapter, Decorator и Facade

Структурные паттерны упрощают проектирование, определяя способы объединения объектов и классов в более крупные структуры.

Adapter (Адаптер)

Позволяет объектам с несовместимыми интерфейсами работать вместе. Классический пример — интеграция сторонних библиотек или работа с устаревшим кодом.

// Старый протокол
protocol OldPaymentSystem {
    func pay(amount: Double)
}

// Новый протокол
protocol NewPaymentSystem {
    func processPayment(amount: Double, currency: String)
}

// Адаптер
class PaymentAdapter: NewPaymentSystem {
    private let oldSystem: OldPaymentSystem
    
    init(oldSystem: OldPaymentSystem) {
        self.oldSystem = oldSystem
    }
    
    func processPayment(amount: Double, currency: String) {
        // Конвертация валюты
        let convertedAmount = amount * 1.1
        oldSystem.pay(amount: convertedAmount)
    }
}

// Использование
let oldPayment = OldPaymentSystemImpl()
let adapter = PaymentAdapter(oldSystem: oldPayment)
adapter.processPayment(amount: 100, currency: "USD")

Decorator (Декоратор)

Динамически добавляет объекту новые обязанности. В iOS часто применяется для расширения функциональности UI-элементов или обработки данных.

protocol Coffee {
    func cost() -> Double
    func description() -> String
}

class SimpleCoffee: Coffee {
    func cost() -> Double { return 2.0 }
    func description() -> String { return "Simple coffee" }
}

class MilkDecorator: Coffee {
    private let wrappedCoffee: Coffee
    
    init(_ coffee: Coffee) {
        self.wrappedCoffee = coffee
    }
    
    func cost() -> Double {
        return wrappedCoffee.cost() + 0.5
    }
    
    func description() -> String {
        return wrappedCoffee.description() + " + milk"
    }
}

// Использование
var coffee: Coffee = SimpleCoffee()
coffee = MilkDecorator(coffee)
print("Cost: \(coffee.cost()), Description: \(coffee.description())")

Facade (Фасад)

Предоставляет простой интерфейс к сложной системе подсистем. Идеально подходит для инкапсуляции сложной логики работы с библиотеками (например, Core Location или AVFoundation).

class VideoConverterFacade {
    private let codec = Codec()
    private let compressor = Compressor()
    private let fileManager = FileManager()
    
    func convertAndSaveVideo(from source: URL, to destination: URL) {
        let decodedData = codec.decode(source)
        let compressedData = compressor.compress(decodedData)
        fileManager.save(compressedData, to: destination)
        print("Video converted and saved successfully")
    }
}

// Использование
let converter = VideoConverterFacade()
converter.convertAndSaveVideo(from: sourceURL, to: destinationURL)

Поведенческие паттерны: Observer, Delegate и Strategy

Поведенческие паттерны определяют способы взаимодействия между объектами, распределяя обязанности и улучшая коммуникацию.

Observer (Наблюдатель)

Создает механизм подписки, позволяющий одним объектам следить за изменениями других. В Swift реализуется через Combine или NotificationCenter.

// Использование NotificationCenter (встроенный Observer)
extension Notification.Name {
    static let userLoggedIn = Notification.Name("userLoggedIn")
}

class LoginService {
    func loginUser() {
        // Логика входа
        NotificationCenter.default.post(name: .userLoggedIn, object: nil)
    }
}

class ProfileViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(updateUI),
            name: .userLoggedIn,
            object: nil
        )
    }
    
    @objc func updateUI() {
        print("UI updated after login")
    }
    
    deinit {
        NotificationCenter.default.removeObserver(self)
    }
}

Delegate (Делегат)

Блоги

Рекомендаций по книгам