Изучите различные шаблоны проектирования на C++

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

Все, должно быть, слышали о GoF, которые собрались вместе, чтобы наметить различные шаблоны, с которыми они столкнулись, работая над крупномасштабными системами. Эти шаблоны могут быть применены к любому объектно-ориентированному языку. Шаблоны проектирования предназначены для решения наиболее распространенных проблем. Вам нужно понять эти шаблоны, чтобы найти наиболее подходящий для вашего случая использования. Я постараюсь объяснить каждый шаблон с точки зрения непрофессионала на примере.

Во-первых, вам нужно знать, какие типы шаблонов проектирования существуют. Я перечислю и объясню самые популярные шаблоны проектирования в этой серии статей.

Классификация шаблонов проектирования

  1. Creational — занимается инициализацией и настройкой классов и объектов.
  2. Структурный — занимается разделением интерфейса и реализацией классов и объектов.
  3. Поведенческий — имеет дело с динамическими взаимодействиями между различными классами и объектами.

Шаблоны креативного дизайна

Шаблоны создания стремятся отделить систему от того, как ее объекты создаются, составляются и представляются.

Порождающие шаблоны проектирования имеют дело либо с созданием экземпляра подкласса в соответствии с заданным контекстом, либо с делегированием создания объекта другому объекту.

Одноэлементный шаблон

Шаблон Singleton используется, когда нам нужен только один экземпляр нашего класса во всей программе. Этот термин происходит от математической концепции синглтона, которая означает наличие множества, состоящего ровно из одного элемента.

Обычно шаблон singleton используется для создания соединения с базой данных, написания логгеров, кэширования, балансировщика нагрузки и т. д.

Шаблон одиночного элемента — это шаблон проектирования программного обеспечения, который ограничивает создание экземпляра класса одним "единственным" экземпляром.

Я попытаюсь объяснить синглтон, создав класс, который мы можем использовать для регистрации некоторой информации в файле для нашей программы.

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

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

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

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

Logger &log = Logger::getInstance();

Шаблон фабричного метода

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

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

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

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

Давайте возьмем пример парсера, который мы будем использовать для анализа файлов разных типов. XMLParser и HTMLParser — это два класса, которые реализуют синтаксический анализ соответствующих типов. Это конкретный продукт, который клиент хочет использовать и создавать свои объекты. Мы также сохраним интерфейс, который можно использовать в классе Factory, чтобы обеспечить гибкость добавления других парсеров с минимальными изменениями в созданный нами класс factory.

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

Давайте используем это в клиентском коде для создания анализатора файлов другого типа.

Мы будем использовать функцию main() в качестве нашего клиента.

Вывод:

Parsing XML file
Parsing HTML file

Абстрактный заводской узор

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

Предоставьте интерфейс для создания семейств связанных или зависимых объектов без указания их конкретных классов.

Классы, которые участвуют в шаблоне Abstract Factory:

  • AbstractFactory — объявляет интерфейс для операций, создающих абстрактные продукты.
  • ConcreteFactory — реализует операции по созданию изделий из бетона.
  • AbstractProduct — объявляет интерфейс для типа объектов продукта.
  • Продукт — определяет продукт, который будет создан соответствующей ConcreteFactory; он реализует интерфейс AbstractProduct.
  • Клиент — использует интерфейсы, объявленные классами AbstractFactory и AbstractProduct.

Давайте снова сопоставим все вышеперечисленные классы в нашем примере с парсером файлов, чтобы понять шаблон абстрактной фабрики. Наш интерфейс Parser — это AbstractProduct, а XMLParser HTMLParser — это конкретный Product. Давайте сохраним Reader, который используется для чтения файла, как еще один AbstractProduct. XMLReader, HTMLReader — это конкретный продукт.

Мы создадим AbstractFactory с именем DocumentFactory и две его ConcreteFactoryдля XML и HTML с именами XMLDocumentFactory и HTMLDocumentFactory.

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

Использование класса Document в нашей программе и его вывод:

Шаблон строителя

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

Отделите построение сложного объекта от его представления. Таким образом, один и тот же процесс построения может создавать разные представления.

Классы, которые участвуют в шаблоне Builder:

  • Builder — этот класс определяет интерфейс для создания частей объекта Product.
  • ConcreteBuilder — создает и собирает части продукта, реализуя интерфейс Builder. Он определяет и отслеживает создаваемое им представление и предоставляет интерфейс для сохранения продукта.
  • Директор — этот класс создает сложный объект, используя интерфейс Builder.
  • Продукт — этот класс представляет строящийся сложный объект.

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

Мы создадим интерфейс DocumentBuilder в качестве нашего класса Builder.

Создайте разные классы для кнопок «Копировать», «Вырезать», «Вставить», «Выделить».

Теперь класс Document, который действует как наш Product.

Нам нужно построить ConcreteBuilder для нашего интерфейса DocumentBuilder. Давайте создадим PDFDocumentBuilder и WordDocumentBuilder.

Теперь нам нужно создать класс Director, который будет использоваться для создания нашего приложения Product Document.

Мы будем вызывать этот класс построителя из нашего клиента main, чтобы построить наше приложение и проверить его свойства.

Продолжение следует…