Menu
Coddy logo textTech

Фабричные конструкторы

Часть раздела Объектно-ориентированное программирование путешествия по Dart на Coddy — урок 14 из 110.

Фабричный конструктор — это специальный конструктор, который не всегда создает новый экземпляр. В отличие от обычных конструкторов, фабричный конструктор может возвращать существующий экземпляр, подтип или даже null (если это разрешено). Вы объявляете его с помощью ключевого слова factory.

class Logger {
  static final Logger _instance = Logger._internal();
  
  // Приватный именованный конструктор
  Logger._internal();
  
  // Фабричный конструктор
  factory Logger() {
    return _instance;
  }
}

Logger log1 = Logger();
Logger log2 = Logger();
print(identical(log1, log2)); // true

В этом примере фабричный конструктор всегда возвращает один и тот же экземпляр. Независимо от того, сколько раз вы вызываете Logger(), вы получаете идентичный объект. Этот паттерн обычно используется для реализации синглтонов.

Фабричные конструкторы отличаются от обычных конструкторов ключевым образом: они не имеют доступа к this, поскольку они могут вообще не создавать новый экземпляр. Вместо этого они должны явно возвращать объект:

class Shape {
  factory Shape(String type) {
    if (type == 'circle') return Circle();
    if (type == 'square') return Square();
    return Rectangle();
  }
}

class Circle extends Shape { Circle() : super._(); }
class Square extends Shape { Square() : super._(); }
class Rectangle extends Shape { Rectangle() : super._(); }

Фабричные конструкторы полезны, когда вам нужен контроль над созданием экземпляров, например, для возврата кэшированных объектов, реализации пулов объектов или выбора подтипа для создания экземпляра на основе входных параметров.

challenge icon

Задание

Легко

Давайте создадим систему уведомлений, которая использует фабричные конструкторы для создания различных типов уведомлений на основе простого строкового ввода.

Вы создадите два файла для организации вашего кода:

  • notification.dart: Определите класс Notification, который служит базой для всех типов уведомлений. Ваш класс должен иметь:
    • Поле String message
    • Приватный именованный конструктор Notification._internal, который инициализирует сообщение
    • Фабричный конструктор Notification(String type, String message), который возвращает различные подтипы уведомлений в зависимости от параметра type:
      • Если тип равен 'email', верните EmailNotification
      • Если тип равен 'sms', верните SMSNotification
      • В противном случае верните PushNotification
    • Метод send(), который выводит в консоль Sending: [message]

В том же файле создайте три подкласса, которые расширяют Notification:

  • EmailNotification: Переопределите send() для вывода Email: [message]
  • SMSNotification: Переопределите send() для вывода SMS: [message]
  • PushNotification: Переопределите send() для вывода Push: [message]

Каждому подклассу нужен конструктор, который принимает message и передает его родителю с помощью super._internal(message).

  • main.dart: Импортируйте ваш класс уведомлений и создайте три уведомления, используя фабричный конструктор:
    • Email-уведомление с сообщением 'Welcome!'
    • SMS-уведомление с сообщением 'Your code is 1234'
    • Push-уведомление с сообщением 'New update available'
    Вызовите send() для каждого уведомления в порядке, указанном выше.

Ожидаемый результат:

Email: Welcome!
SMS: Your code is 1234
Push: New update available

Шпаргалка

Фабричный конструктор — это специальный конструктор, объявленный с помощью ключевого слова factory, который не всегда создает новый экземпляр. В отличие от обычных конструкторов, он может возвращать существующий экземпляр, подтип или null.

Фабричные конструкторы не имеют доступа к this и должны явно возвращать объект.

Пример паттерна Singleton:

class Logger {
  static final Logger _instance = Logger._internal();
  
  Logger._internal(); // Приватный именованный конструктор
  
  factory Logger() {
    return _instance; // Всегда возвращает один и тот же экземпляр
  }
}

Logger log1 = Logger();
Logger log2 = Logger();
print(identical(log1, log2)); // true

Возврат различных подтипов:

class Shape {
  factory Shape(String type) {
    if (type == 'circle') return Circle();
    if (type == 'square') return Square();
    return Rectangle();
  }
}

class Circle extends Shape { Circle() : super._(); }
class Square extends Shape { Square() : super._(); }
class Rectangle extends Shape { Rectangle() : super._(); }

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

Попробуйте сами

import 'notification.dart';

void main() {
  // TODO: Создайте email-уведомление с сообщением 'Welcome!'
  
  // TODO: Создайте SMS-уведомление с сообщением 'Your code is 1234'
  
  // TODO: Создайте push-уведомление с сообщением 'New update available'
  
  // TODO: Вызовите send() для каждого уведомления по порядку
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование