
Каждый разработчик рано или поздно сталкивается с моментом, когда стандартные решения перестают справляться с возросшими требованиями проекта. Именно в этот момент стоит рассмотреть паттерн "Абстрактная фабрика" — один из мощных инструментов, который помогает строить системы, готовые к расширениям и изменениям. Это не просто шаблон проектирования, это целая философия построения многогранного, но при этом структурированного кода.
Представим ситуацию: приложение должно поддерживать несколько тем оформления, работать с разными базами данных в зависимости от клиентского окружения или отправлять различные уведомления - "Абстрактная фабрика" позволяет элегантно и прозрачно решить эту задачу, позволяя интегрировать новшества без боли и страданий.
Чтобы показать работу паттерна давайте сделаем простую генерацию различных видов уведомлений в зависимости от выбранной фабрики (для отправки локальных уведомлений не забываем добавить разрешение в файл Info.plist)
Что вообще хотим увидеть:

Прежде всего начнем с протоколов для определения интерфейсов уведомлений и фабрики для создания уведомлений:
protocol Alert {
    func show(in viewController: UIViewController)
}
protocol Notification {
    func send()
}
protocol NotificationFactory {
    func createAlert(title: String, message: String) -> Alert
    func createNotification(title: String, body: String) -> Notification
}Далее создаем реализацию уведомлений:  реализуем два класса BasicAlert и LocalNotification, которые соответствуют протоколам Alert и Notification соответственно.
- func send(): Метод для отправки уведомления, создает содержимое уведомления- UNMutableNotificationContentс установленными заголовком и текстом, а также звуком по умолчанию. Затем создаем триггер- UNTimeIntervalNotificationTrigger, который определяет, через сколько времени уведомление должно быть отправлено (в данном случае через 5 секунд без повторения) и- UNNotificationRequestдля запроса отправки уведомления. Наконец, добавляем этот запрос в- UNUserNotificationCenter.
import UIKit
import UserNotifications
class BasicAlert: Alert {
    private var title: String
    private var message: String
    init(title: String, message: String) {
        self.title = title
        self.message = message
    }
    func show(in viewController: UIViewController) {
        let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
        alertController.addAction(UIAlertAction(title: "OK", style: .default))
        viewController.present(alertController, animated: true)
    }
}
class LocalNotification: Notification {
    private var title: String
    private var body: String
    init(title: String, body: String) {
        self.title = title
        self.body = body
    }
    func send() {
        let content = UNMutableNotificationContent()
        content.title = title
        content.body = body
        content.sound = .default
        let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5, repeats: false)
        let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)
        UNUserNotificationCenter.current().add(request) { error in
            if let error = error {
                print("Ошибка при добавлении локального уведомления: \(error)")
            }
        }
    }
}Теперь создадим централизованное место для создания объектов уведомлений в приложении, которое позволяет легко заменять и модифицировать способ создания уведомлений, класс BasicNotificationFactory является реализацией протокола NotificationFactory. Он функционирует как конкретная фабрика, создающая объекты, которые соответствуют протоколам Alert и Notification
class BasicNotificationFactory: NotificationFactory {
    func createAlert(title: String, message: String) -> Alert {
        return BasicAlert(title: title, message: message)
    }
    func createNotification(title: String, body: String) -> Notification {
        return LocalNotification(title: title, body: body)
    }
}Переходим в наш контроллер, создаем кнопку, раскидываем якоря.
- requestNotificationPermission(): Запрашивает разрешение пользователя на отправку уведомлений с опциями для баннера и звука. В случае успеха или ошибки выводит соответствующее сообщение в консоль.
- showAlertButtonTapped(): Метод, который вызывается при нажатии на кнопку. Он создает и показывает предупреждение используя метод- createAlert()фабрики- factoryи планирует локальное уведомление с помощью метода- createNotification()той же фабрики.
Расширение (extension) ViewController: UNUserNotificationCenterDelegate реализует метод делегата userNotificationCenter(_:willPresent:withCompletionHandler:), который позволяет приложению отображать уведомления даже если оно находится на переднем плане. В нём вызывается completionHandler с опциями .banner, .list и .sound, которые определяют, как уведомление будет показано пользователю.
import UIKit
class ViewController: UIViewController {
    var factory: NotificationFactory = BasicNotificationFactory()
    
    lazy var alertButton: UIButton = {
        let button = UIButton()
        button.setTitle("Показать уведомление", for: .normal)
        button.setTitleColor(.systemBlue, for: .normal)
        button.addTarget(self, action: #selector(showAlertButtonTapped), for: .touchUpInside)
        return button
    }()
    override func viewDidLoad() {
        super.viewDidLoad()
        setupViews()
        setupConstraints()
        requestNotificationPermission()
        UNUserNotificationCenter.current().delegate = self
    }
    
    private func setupViews() {
        view.backgroundColor = .white
        view.addSubview(alertButton)
    }
    
    private func setupConstraints() {
        alertButton.translatesAutoresizingMaskIntoConstraints = false
        alertButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        alertButton.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
    }
  //выполняем запрос разрешений на отправку локальных уведомлений пользователю
    private func requestNotificationPermission() {
        UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound]) { granted, error in
            if granted {
                print("Разрешение на отправку уведомлений получено.")
            } else if let error = error {
                print("Ошибка при запросе разрешения на отправку уведомлений: \(error)")
            }
        }
    }
    @objc func showAlertButtonTapped() {
        let alert = factory.createAlert(title: "Внимание", message: "Это пример абстрактной фабрики.")
        alert.show(in: self)
        
        let notification = factory.createNotification(title: "Привет", body: "Это локальное уведомление.")
        notification.send()
    }
}
extension ViewController: UNUserNotificationCenterDelegate {
    func userNotificationCenter(_ center: UNUserNotificationCenter,
                                willPresent notification: UNNotification,
                                withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        //показываем уведомление даже когда приложение на переднем плане
        completionHandler([.banner, .list, .sound])
    }
}
На начальных этапах изучения складывается впечатление, что "Фабричный метод" и "Абстрактная фабрика" — это синонимы, однако это далеко не так. Различия между этими паттернами весьма значительны:
- Абстрактная фабрика обычно используется для создания семейств взаимозависимых объектов без спецификации их конкретных классов. В этом случае - NotificationFactoryявляется абстрактной фабрикой, которая создаёт семейство объектов связанных с уведомлениями —- Alertи- Notification.
- Фабричный метод фокусируется на одном продукте и обычно включает один метод для создания объекта, а наследование используется для изменения типа создаваемого продукта. Если бы в вашем примере был один протокол с одним методом - create, который возвращал бы разные типы объектов на основе какого-то параметра, и этот метод переопределялся в подклассах, то это был бы "Фабричный метод".
В нашем примере BasicNotificationFactory предоставляет методы для создания двух разных типов продуктов: Alert и Notification. Каждый из этих продуктов может иметь множество вариаций (например, разные виды алертов и уведомлений), и фабрика может быть расширена другими фабриками для создания различных вариаций этих продуктов.
На этом на сегодня все, как сказал один класс к другому: "Я думал, мы можем быть друзьями, но ты постоянно создаешь что-то новое."
 
          