![](https://habrastorage.org/getpro/habr/upload_files/86e/222/465/86e222465c2f46ea1a08be4e7c79d798.png)
Каждый разработчик рано или поздно сталкивается с моментом, когда стандартные решения перестают справляться с возросшими требованиями проекта. Именно в этот момент стоит рассмотреть паттерн "Абстрактная фабрика" — один из мощных инструментов, который помогает строить системы, готовые к расширениям и изменениям. Это не просто шаблон проектирования, это целая философия построения многогранного, но при этом структурированного кода.
Представим ситуацию: приложение должно поддерживать несколько тем оформления, работать с разными базами данных в зависимости от клиентского окружения или отправлять различные уведомления - "Абстрактная фабрика" позволяет элегантно и прозрачно решить эту задачу, позволяя интегрировать новшества без боли и страданий
Что вообще хотим увидеть:
![](https://habrastorage.org/getpro/habr/upload_files/e91/31a/41d/e9131a41d35512828043664d1d3c55df.gif)
Для начала объявим протоколы которые определяют интерфейсы для объектов оповещения и уведомлений:
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: определяет поведение при получении уведомлений, когда приложение активно
В этом контроллере основной фокус идет на взаимодействие пользователя с интерфейсом для демонстрации различных типов уведомлений, создаваемых через абстрактную фабрику, и управления разрешениями на уведомления.
На этом на сегодня все, как сказал один класс к другому: "Я думал, мы можем быть друзьями, но ты постоянно создаешь что-то новое."