Объектно-ориентированные шаблоны JavaScript: шаблон factory

Своевременная и разумная организация кода избавит от головной боли любого разработчика. Благодаря объектно-ориентированному программированию мы можем использовать определенные шаблоны проектирования для улучшения читабельности, уменьшения избыточности кода и создания абстракций. Один из таких шаблонов называется factory.

Шаблон factory относится к объектно-ориентированным и следует методологии DRY. Как следует из названия шаблона, factory создает экземпляры объектов, на основе которых собирается объект, необходимый разработчику.

Давайте рассмотрим один очень простой пример использования шаблона factory для сборки объекта alligator. Чтобы сделать это, сначала нужно создать экземпляры factory, которые произведут части (или детали) объекта alligator:

class TailFactory {

  constructor(props) {

    this.tailLength = props.tailLength;

  }

};

class TorsoFactory {

  constructor(props) {

    this.color = props.color;

  }

};

class HeadFactory {

  constructor(props) {

    this.snoutLenth = props.snoutLenth;

  }

};

Теперь нужно создать класс, который выступит как посредник между фактическими классами factory и пользователем. Назовем этот класс ReptilePartFactory:

class ReptilePartFactory {

  constructor(type, props) {

    if(type === "tail")

      return new TailFactory(props);

    if(type === "torso")

      return new TorsoFactory(props);

    if(type === "head")

      return new HeadFactory(props);

  }

};

Сейчас мы попробуем собрать наш объект alligator из частей, для этого мы воспользуемся классом ReptilePartFactory, который предоставит все необходимые детали:

let alligator = {};

let alligatorProps = {

  tailLength : 2.5,

  color: "green",

  snoutLenth: 1

};

//gets a tail from the tail factory

alligator.tail  = new ReptilePartFactory("tail", alligatorProps);

//gets a torso from the torso factory

alligator.torso = new ReptilePartFactory("torso", alligatorProps);

//gets a head from the head factory

alligator.head  = new ReptilePartFactory("head", alligatorProps);

Если посмотреть на приведенный выше шаблон, может показаться, что мы могли бы использовать тот же класс ReptilePartFactory для создания частей alligator-подобных объектов. При этом другие экземпляры factory никогда не узнают о природе конечного объекта.

Таким образом, шаблон factory дает нам определенные преимущества, среди которых:

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

Теперь, когда у вас есть некоторое представление о шаблоне factory, давайте посмотрим, как можно улучшить код данного шаблона.

Чтобы в приведенном выше примере определить, какой экземпляр factory вызвать, на основе введенных пользователем данных используется лестница if. Это простая и интуитивно понятная реализация, в которую не следует вносить много изменений. Если нам нужно добавить новые части – новые объекты, – то нам придется нарушить работу ReptilePartFactory. Это повлечет за собой нарушение принципов SOLID, которые гласят, что «программные сущности (классы, модули, функции и т.п.) должны быть открыты для расширения, но закрыты для модификации».

А что, если сохранить классы factory в объекте и вызвать только необходимый экземпляр factory, используя искомую часть в качестве ключа? Давайте попробуем сделать это. Для начала нам нужно зарегистрировать классы factory, это очень просто:

let registeredPartFactories = {};

registeredPartFactories['tail'] = class TailFactory{

  ...

};

registeredPartFactories['torso'] = class TorsoFactory {

  ...

};

registeredPartFactories['head'] = class HeadFactory {

  ...

};

А теперь уровень абстракции может вызвать классы factory:

class ReptilePartFactory {

  constructor(type, props) {

    return new registeredPartFactories[type](props);

  }

};

Это гораздо более чистый подход, который позволяет расширять классы factory, не нарушая код в ReptilePartFactory.

Заключение

В JavaScript есть несколько других объектно-ориентированных шаблонов, которые также повышают удобочитаемость и качество кода, делают его масштабируемым и поддерживаемым.

Прежде чем широко использовать шаблон factory в своем коде, убедитесь, что он действительно нужен. Если вы собираетесь многократно создавать похожие типы объектов и вам нужен уровень для создания новых экземпляров на основе этих объектов (при этом оставляя некоторый уровень абстракции), тогда шаблон factory – хороший вариант.

Tags:

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