Абстрактная фабрика: искусство создания масштабируемого кода

от автора

Каждый разработчик рано или поздно сталкивается с моментом, когда стандартные решения перестают справляться с возросшими требованиями проекта. Именно в этот момент стоит рассмотреть паттерн «Абстрактная фабрика» — один из мощных инструментов, который помогает строить системы, готовые к расширениям и изменениям. Это не просто шаблон проектирования, это целая философия построения многогранного, но при этом структурированного кода.

Представим ситуацию: приложение должно поддерживать несколько тем оформления, работать с разными базами данных в зависимости от клиентского окружения или отправлять различные уведомления — «Абстрактная фабрика» позволяет элегантно и прозрачно решить эту задачу, позволяя интегрировать новшества без боли и страданий

Что вообще хотим увидеть:

Для начала объявим протоколы которые определяют интерфейсы для объектов оповещения и уведомлений:

protocol Alert {     func show(in viewController: UIViewController) }  protocol Notification {     func send() }

Далее определяем протокол NotificationFactory, который представляет собой абстрактную фабрику для создания объектов Alert и Notification:

protocol NotificationFactory {     func createAlert(title: String, message: String) -> Alert     func createNotification(title: String, body: String) -> Notification }

Теперь создадим класс BasicNotificationFactory, который реализует протокол NotificationFactory и предоставляет конкретные реализации для создания базовых уведомлений и оповещений. Аналогично создаем класс AdvancedNotificationFactory, но предоставляющий более продвинутые версии уведомлений и оповещений:

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)     } }   class AdvancedNotificationFactory: NotificationFactory {     func createAlert(title: String, message: String) -> Alert {         return AdvancedAlert(title: title, message: message)     }      func createNotification(title: String, body: String) -> Notification {         return PushNotification(title: title, body: body)     } }

Теперь объявим протокол FactoryCreator, который определяет интерфейс для создания объектов типа NotificationFactory. Это «фабрика фабрик».

protocol FactoryCreator {     func createFactory() -> NotificationFactory }

Далее создаем класс BasicFactoryCreator, реализующий протокол FactoryCreator, предоставляет метод для создания экземпляра BasicNotificationFactory и класс AdvancedFactoryCreator, аналогичный BasicFactoryCreator, но создаёт экземпляр AdvancedNotificationFactory:

class BasicFactoryCreator: FactoryCreator {     func createFactory() -> NotificationFactory {         return BasicNotificationFactory()     } }   class AdvancedFactoryCreator: FactoryCreator {     func createFactory() -> NotificationFactory {         return AdvancedNotificationFactory()     } }

Мы определили семейство связанных или зависимых объектов без указания конкретных классов. FactoryCreator позволяет переключаться между разными «семействами» объектов (базовыми и продвинутыми версиями уведомлений и оповещений), а NotificationFactory отвечает за создание этих объектов.


Переходим к созданию сервиса NotificationService. Этот класс будет управлять всеми операциями, связанными с уведомлениями в приложении, а также представляет собой сервисный слой, который абстрагирует логику создания и управления уведомлениями, делая ее более модульной и удобной для повторного использования в разных частях приложения:

class NotificationService {          var factory: NotificationFactory = BasicNotificationFactory()      func requestNotificationPermission(completion: @escaping (Bool) -> Void) {         UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound]) { granted, error in             if let error = error {                 print("Error requesting permission to send notifications: \(error)")             }             completion(granted)         }     }      func createAndShowAlert(in viewController: UIViewController, title: String, message: String) {         let alert = factory.createAlert(title: title, message: message)         alert.show(in: viewController)     }      func sendNotification(title: String, body: String) {         let notification = factory.createNotification(title: title, body: body)         notification.send()     }      func switchFactory(to newFactory: NotificationFactory) {         factory = newFactory     } }
  • объявляем свойство factory, которое является типом NotificationFactory. По умолчанию оно инициализируется как BasicNotificationFactory. Это означает, что изначально используются базовые реализации уведомлений и оповещений.

  • объявляем метод для запроса разрешения на отправку уведомлений requestNotificationPermission()

  • внутри метода requestNotificationPermission, используем UNUserNotificationCenter для запроса разрешения на показ уведомлений с опциями .alert и .sound. В замыкании обрабатываем результат запроса

  • createAndShowAlert(): метод для создания и отображения оповещения, использует factory для создания оповещения (Alert) и затем отображает его в переданном контроллере

  • sendNotification(): метод для отправки уведомления. Аналогично предыдущему методу использует factory для создания объекта Notification и затем отправляет уведомление

  • switchFactory(): метод для изменения фабрики уведомлений. Это позволяет переключаться между различными реализациями фабрик (например, между базовой и продвинутой), изменяя способ создания уведомлений и оповещений


Наконец переходим в контроллер:

import UIKit  class ViewController: UIViewController {          private let notificationService = NotificationService()      lazy var alertButton: UIButton = {         let button = UIButton()         button.setTitle("Show Notification", for: .normal)         button.setTitleColor(.systemBlue, for: .normal)         button.addTarget(self, action: #selector(showAlertButtonTapped), for: .touchUpInside)         return button     }()      lazy var toggleFactoryButton: UIButton = {         let button = UIButton()         button.setTitle("Switch to Advanced Factory", for: .normal)         button.setTitleColor(.systemBlue, for: .normal)         button.addTarget(self, action: #selector(toggleFactory), for: .touchUpInside)         return button     }()      override func viewDidLoad() {         super.viewDidLoad()         setupViews()         setupConstraints()         notificationService.requestNotificationPermission { granted in             if granted {                 print("Уведомления разрешены.")                 // Здесь можно запланировать какие-то уведомления или выполнить другие действия, требующие разрешения на уведомления.             } else {                 print("Уведомления не разрешены.")                 // Здесь можно обработать ситуацию, когда пользователь отказал в разрешении.             }         }         UNUserNotificationCenter.current().delegate = self     }      private func setupViews() {         view.backgroundColor = .white         view.addSubview(alertButton)         view.addSubview(toggleFactoryButton)     }      private func setupConstraints() {         alertButton.translatesAutoresizingMaskIntoConstraints = false         alertButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true         alertButton.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true          toggleFactoryButton.translatesAutoresizingMaskIntoConstraints = false         toggleFactoryButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true         toggleFactoryButton.topAnchor.constraint(equalTo: alertButton.bottomAnchor, constant: 20).isActive = true     }      @objc func showAlertButtonTapped() {         notificationService.createAndShowAlert(in: self, title: "Attention", message: "This is an example of an abstract factory.")         notificationService.sendNotification(title: "Hello", body: "This is a local notification.")     }      @objc func toggleFactory() {         if let _ = notificationService.factory as? BasicNotificationFactory {             notificationService.switchFactory(to: AdvancedNotificationFactory())             toggleFactoryButton.setTitle("Switch to Basic Factory", for: .normal)         } else {             notificationService.switchFactory(to: BasicNotificationFactory())             toggleFactoryButton.setTitle("Switch to Advanced Factory", for: .normal)         }     } }  extension ViewController: UNUserNotificationCenterDelegate {     func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {                  completionHandler([.banner, .list, .sound])     } }
  • private let notificationService = NotificationService(): создаем экземпляр нашего сервиса, который будет использоваться для управления уведомлениями

  • накидываем UI и раскидываем якоря

  • showAlertButtonTapped(): создает и показывает оповещение, а также отправляет уведомление

  • toggleFactory(): переключает между базовой и продвинутой фабриками уведомлений и изменяет текст кнопки

  • extension ViewController: UNUserNotificationCenterDelegate: определяет поведение при получении уведомлений, когда приложение активно

В этом контроллере основной фокус идет на взаимодействие пользователя с интерфейсом для демонстрации различных типов уведомлений, создаваемых через абстрактную фабрику, и управления разрешениями на уведомления.

На этом на сегодня все, как сказал один класс к другому: «Я думал, мы можем быть друзьями, но ты постоянно создаешь что-то новое.»


ссылка на оригинал статьи https://habr.com/ru/articles/773388/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *