Абстрактные типы компьютеров и их основные характеристики

Обновлено: 29.06.2024

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

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

Techopedia объясняет абстракцию

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

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

Объектно-ориентированное программирование

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

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

Класс

Другим средством абстракции в объектно-ориентированном программировании являются классы. Классы объектов дополнительно стратифицируют принцип повторяющегося использования и автоматизированного распространения кода.

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

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

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

И то, и другое — потрясающие возможности, которые ООП привнесли в мир технологий.

Интерфейс прикладного программирования

Еще один современный яркий пример абстракции иллюстрируется интерфейсом прикладного программирования (API), который так распространен в кроссплатформенных системах.

API – это ключевой способ предоставления этой абстрагированной информации, чтобы сторонним третьим сторонам не нужно было так много знать о встроенной кодовой базе. В очень простом смысле API-интерфейсы — это «соединители» — стороны будут использовать API для подключения кода одного приложения к коду другого приложения, для передачи наборов данных через кросс-платформенную среду и обеспечения автоматизированного взаимодействия этих программ

Таким образом, API функционирует как ключевой инструмент абстракции, встраивая те объектно-ориентированные принципы, которые показывают "внешней программе", что делает "внутренняя программа".

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

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

Абстрактные компьютеры можно классифицировать по сложности их структуры, а также по типу информации, обрабатываемой алгоритмами.Основные классы абстрактных компьютеров перечислены ниже.

1) Абстрактные компьютеры, обрабатывающие слова в конечном алфавите. Сложность их структуры наиболее проста для того, чтобы удовлетворить особые потребности теории. Типичными представителями являются машина Тьюринга, машина Минского, нормальные алгоритмы Маркова (ср. Нормальный алгоритм) и каноническая система Поста. Абстрактные компьютеры часто используются для демонстрации неразрешимости алгоритмических задач и для оценки сложности алгоритмов (ср. Алгоритм, вычислительная сложность).

2) Абстрактные компьютеры той же сложности структуры, что и в классе 1), но которые обрабатывают матрицы, конечные графы и произвольные комплексы. Типичными представителями являются алгоритмы Колмогорова, клеточные автоматы фон Неймана и растущие автоматы. Если в алгоритмах Колмогорова информация обрабатывается локально, то в клеточных и растущих автоматах фон Неймана это происходит параллельно. Были построены универсальные абстрактные компьютеры; они имеют удовлетворительные характеристики и способны моделировать произвольные абстрактные компьютеры одного типа. Вычисления с максимальной скоростью (в реальном времени) изучались на определенных классах абстрактных компьютеров.

3) Абстрактные компьютеры, которые обычно обрабатывают слова или целые числа и имеют сложную структуру, определяемую практическими требованиями, имеют много общего с реальными компьютерами. Типичными представителями являются так называемые операторные алгоритмы [3] и машины с произвольным доступом к памяти. Такие абстрактные компьютеры используются для оценки сложности алгоритмов, используемых на практике, и для моделирования абстрактных компьютеров.

4) Абстрактные ЭВМ той же сложности структуры, что и в классе 3), но обрабатывающие произвольные графы (чаще всего бесконечные деревья). Они являются продуктом развития абстрактных компьютеров класса 3), предназначенных для адаптации к особенностям алгоритмических языков. Такие абстрактные компьютеры используются для формализации семантики алгоритмических языков и для демонстрации корректности программ. Типичными представителями являются абстрактные компьютеры, используемые в спецификации семантики Алгола-68 и PL/I.

Ссылки

[1] Я.М. Бардзин, "Теоремы универсальности в теории растущих автоматов" Докл. акад. АН СССР , 157 : 3 (1964) pp. 542–545 (In Russian)
[2] А. Ван и др. Wyngaarden, «Пересмотренный отчет об алгоритмическом языке ALGOL 68» Acta Inform. , 5 : 1–3 (1975)
[3] AP Ершов, "Алгоритмы оператора I. Основные понятия" Проблемы Кибернета. , 3 (1960), стр. 5–48 (на русском языке)
[4] AN Колмогоров, В.А. Успенский, "Об определении алгоритма" УМН. наук , 13 : 4 (1958) с. 3–28 (на русском языке)
[5] М. Минский, "Вычисления: конечные и бесконечные машины", Прентис-Холл (1967)
[6] БА Трахтенброт, "Алгоритмы и машины для расчета", Дунод (1963) (перевод с русского)
[7] Дж. Хартманис, "Вычислительная сложность машин с хранимой программой с произвольным доступом" Math. Systems Theory , 5 (1971), стр. 232–245
[8] П. Вегнер, "Венский язык определений" Comput. Обзоры , 4 : 1 (1972), стр. 5–63

Комментарии

Понятие абстрактного компьютера, разработанное выше, включает не только машинные модели, которые используются в настоящее время, но и формализмы, более ориентированные на вычисления. Например, "агент", выполняющий сокращение членов в $\lambda$-исчислении, является абстрактным алгоритмом в смысле вышеприведенного понятия.

Существуют некоторые важные особенности классификации, которые рассматриваются сегодня и которые не упомянуты выше.

Единообразие против неравномерности: при изучении сложности формулы или сложности сети для данной функции исследуется ее ограничение на входы длины $ k $ для заданного целого числа $ k $, а затем исследуется размер или глубина формула или сеть, которая вычисляет это ограничение. Возможно, что не существует эффективной процедуры, которая для произвольной длины $k$ дает описание формулы или сети минимальной сложности для этого ограничения, и поэтому эти минимальные устройства представляют собой неоднородное устройство для исходной функции. С другой стороны, данный алгоритм машины Тьюринга представляет единообразное описание всей функции.

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

Но это еще не конец истории: представитель все еще разоблачен! Рассмотрим этот совершенно разумный клиентский код, использующий `Tweet`: ```java /** @return твит, который ретвитит t, через час*/ public static Tweet retweetLater(Tweet t) < Date d = t.getTimestamp(); d.setHours(d.getHours()+1); вернуть новый твит("rbmllr", t.getText(), d); >``` `retweetLater` принимает твит и должен вернуть другой твит с тем же сообщением (называемым *ретвитом*), но отправленным через час. Метод retweetLater может быть частью системы, которая автоматически повторяет смешные вещи, которые говорят знаменитости Twitter. В чем проблема? Вызов `getTimestamp` возвращает ссылку на тот же объект даты, на который ссылается твит `t`. t.timestamp и d являются псевдонимами одного и того же изменяемого объекта. Таким образом, когда этот объект даты изменяется с помощью `d.setHours()`, это также влияет на дату в `t`, как показано на диаграмме моментального снимка. Инвариант неизменности `Tweet` был нарушен. Проблема в том, что в `Tweet` просочилась ссылка на изменяемый объект, от которого зависела его неизменяемость. Мы выставили представителя таким образом, что «Tweet» больше не может гарантировать, что его объекты неизменны. Совершенно разумный клиентский код создал незаметную ошибку. Мы можем исправить такой вид репутации, используя защитное копирование: создание копии изменяемого объекта, чтобы избежать утечки ссылок на репутацию. Вот код: java public Date getTimestamp() < return new Date(Date.getTime()); >``` Изменяемые типы часто имеют конструктор копирования, который позволяет создавать новый экземпляр, дублирующий значение существующего экземпляра. В этом случае конструктор копирования `Date` использует значение метки времени, измеренное в секундах с 1 января 1970 года. В качестве другого примера, конструктор копирования `StringBuilder` принимает `String`. Еще один способ скопировать изменяемый объект — `clone()`, который поддерживается некоторыми типами, но не всеми. Есть неприятные проблемы с тем, как работает `clone()` в Java. Подробнее см. Джош Блох, *Effective Java*, пункт 10.

Итак, мы сделали защитное копирование в возвращаемом значении `getTimestamp`. Но мы еще не закончили! Есть еще репутация. Рассмотрим этот (опять же вполне разумный) клиентский код: ```java /** @return список из 24 вдохновляющих твитов, по одному сегодня в час */ public static List tweetEveryHourToday () < List list = new ArrayList (); Дата Дата = новая Дата(); for (int i=0; i © сотрудники курса MIT EECS

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

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

Управление доступом в Java

Вы уже должны были прочитать: Управление доступом к членам класса в учебных пособиях по Java.

Что означает абстракция

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

  • Абстракция. Пропуск или сокрытие низкоуровневых деталей с помощью более простой идеи более высокого уровня.
  • Модульность.Разделение системы на компоненты или модули, каждый из которых можно спроектировать, внедрить, протестировать, обосновать и повторно использовать отдельно от остальной системы.
  • Инкапсуляция. Возведение стен вокруг модуля (жесткой оболочки или капсулы), чтобы модуль отвечал за свое внутреннее поведение, а ошибки в других частях системы не могли нарушить его целостность.
  • Скрытие информации. Сокрытие сведений о реализации модуля от остальной системы, чтобы эти сведения можно было изменить позже без изменения остальной системы.
  • Разделение проблем. Передача функции (или «проблемы») одному модулю, а не распределению ее по нескольким модулям.

Как инженер-программист, вы должны знать эти термины, потому что вы будете часто с ними сталкиваться. Фундаментальная цель всех этих идей — помочь достичь трех важных свойств, которые важны для нас в 6.005: защита от ошибок, простота понимания и готовность к изменениям.

Определяемые пользователем типы

На заре вычислительной техники языки программирования поставлялись со встроенными типами (такими как целые числа, логические значения, строки и т. д.) и встроенными процедурами, например для ввода и вывода. Пользователи могли определять свои собственные процедуры: так создавались большие программы.

Важным достижением в разработке программного обеспечения стала идея абстрактных типов: можно было разработать язык программирования, позволяющий использовать и определяемые пользователем типы. Эта идея возникла в результате работы многих исследователей, в частности Даля (изобретателя языка Simula), Хоара (разработавшего многие методы, которые мы сейчас используем для рассуждений об абстрактных типах), Парнаса (который ввел термин «сокрытие информации» и «первый сформулировали идею организации программных модулей вокруг секретов, которые они инкапсулировали), а здесь, в Массачусетском технологическом институте, Барбара Лисков и Джон Гуттаг, которые проделали основополагающую работу по спецификации абстрактных типов и поддержке языков программирования для них — и разработали исходный 6.170, предшественник 6.005. Барбара Лисков получила премию Тьюринга, эквивалент Нобелевской премии в компьютерных науках, за работу над абстрактными типами.

Ключевая идея абстракции данных заключается в том, что тип характеризуется операциями, которые вы можете с ним выполнять. Число — это то, что вы можете складывать и умножать; строка - это то, что вы можете объединить и взять подстроки; логическое значение — это то, что вы можете отрицать, и так далее. В некотором смысле пользователи уже могли определять свои собственные типы в ранних языках программирования: вы могли создать тип записи даты, например, с целочисленными полями для дня, месяца и года. Но то, что сделало абстрактные типы новыми и отличными, было сосредоточено на операциях: пользователю типа не нужно было бы беспокоиться о том, как на самом деле хранятся его значения, точно так же, как программист может игнорировать то, как компилятор фактически хранит целые числа. Все, что имеет значение, — это операции.

В Java, как и во многих современных языках программирования, различие между встроенными и определяемыми пользователем типами несколько размыто. Классы в java.lang, такие как Integer и Boolean, являются встроенными; менее ясно, считаете ли вы все коллекции java.util встроенными (и в любом случае не очень важными). Java усложняет проблему наличием примитивных типов, которые не являются объектами. Набор этих типов, таких как int и boolean, не может быть расширен пользователем.

Классификация типов и операций

Типы, встроенные или определяемые пользователем, могут быть классифицированы как изменяемые и неизменяемые. Объекты изменяемого типа могут быть изменены: то есть они предоставляют операции, выполнение которых приводит к тому, что результаты других операций над тем же объектом дают другие результаты. Таким образом, Date является изменяемым, потому что вы можете вызвать setMonth и наблюдать за изменением с помощью операции getMonth. Но String неизменяем, потому что его операции создают новые объекты String, а не изменяют существующие. Иногда тип предоставляется в двух формах: изменяемой и неизменяемой. StringBuilder , например, является изменяемой версией String (хотя они, безусловно, не являются одним и тем же типом Java и не являются взаимозаменяемыми).

Операции абстрактного типа классифицируются следующим образом:

  • Создатели создают новые объекты этого типа. Создатель может принимать объект в качестве аргумента, но не объект создаваемого типа.
  • Производители создают новые объекты из старых объектов этого типа. Например, метод concat для String является производителем: он принимает две строки и создает новую, представляющую их конкатенацию.
  • Наблюдатели принимают объекты абстрактного типа и возвращают объекты другого типа. Например, метод size в List возвращает значение int .
  • Мутаторы меняют объекты. Например, метод add объекта List изменяет список, добавляя элемент в конец.

Мы можем схематично обобщить эти различия следующим образом (объяснение следует):

  • создатель: t* → T
  • производитель: T+, t* → T
  • наблюдатель: T+, t* → t
  • мутатор : T+, t* → void | т | Т

Они неформально показывают форму сигнатур операций в различных классах. Каждый T сам по себе является абстрактным типом; каждый t имеет другой тип. Маркер + указывает, что тип может встречаться один или несколько раз в этой части подписи, а маркер * указывает, что он встречается ноль или более раз. | указывает или. Например, производитель может принимать два значения абстрактного типа T , как это делает String.concat():

Некоторые наблюдатели принимают нулевые аргументы других типов t , например:

… и другие берут несколько:

  • regionMatches : String × boolean × int × String × int × int → boolean

Операция создателя часто реализуется как конструктор , например new ArrayList() . Но создатель может быть просто статическим методом, например, Arrays.asList() . Создатель, реализованный как статический метод, часто называют фабричным методом. Различные методы String.valueOf в Java являются другими примерами создателей, реализованных как фабричные методы.

Мутаторы часто сигнализируются возвращаемым типом void. Метод, возвращающий void, должен вызываться из-за какого-то побочного эффекта, иначе он ничего не возвращает. Но не все мутаторы возвращают пустоту. Например, Set.add() возвращает логическое значение, указывающее, действительно ли набор был изменен. В наборе инструментов графического пользовательского интерфейса Java Component.add() возвращает сам объект, так что несколько вызовов add() могут быть объединены в цепочку.

Примеры абстрактных типов данных

Вот несколько примеров абстрактных типов данных, а также некоторые их операции, сгруппированные по типу.

int — примитивный целочисленный тип Java. int неизменяем, поэтому у него нет мутаторов.

  • создатели: числовые литералы 0 , 1 , 2 , …
  • производители: арифметические операторы + , - , * , /
  • наблюдатели: операторы сравнения == , != , , >
  • мутаторы: нет (это неизменяемо)

Список — это тип списка в Java. Список изменяем. Список также является интерфейсом, что означает, что другие классы обеспечивают реальную реализацию типа данных. Эти классы включают ArrayList и LinkedList .

  • создатели: конструкторы ArrayList и LinkedList, Collections.singletonList
  • производители: Collections.unmodifiableList
  • наблюдатели: размер , получение
  • мутаторы: add , remove , addAll , Collections.sort

String — это строковый тип Java. Строка является неизменной.

  • создатели: конструкторы строк
  • производители: concat , substring , toUpperCase
  • наблюдатели: длина , charAt
  • мутаторы: нет (это неизменяемо)

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

Разработка абстрактного типа

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

Лучше иметь несколько простых операций, которые можно эффективно комбинировать, чем множество сложных операций.

Каждая операция должна иметь четко определенную цель и последовательное поведение, а не множество особых случаев. Например, нам, вероятно, не следует добавлять операцию суммирования в List. Это может помочь клиентам, работающим со списками целых чисел, но как насчет списков строк? Или вложенные списки? Все эти особые случаи делают операцию sum сложной для понимания и использования.

Набор операций должен быть адекватным в том смысле, что их должно быть достаточно для выполнения тех вычислений, которые, вероятно, захотят выполнять клиенты. Хорошим тестом является проверка того, что каждое свойство объекта данного типа может быть извлечено. Например, если бы не было операции получения, мы не смогли бы узнать, что такое элементы списка. Базовую информацию не должно быть чрезмерно трудно получить. Например, метод size не является строго обязательным для List, потому что мы могли бы применять get при увеличении индекса до тех пор, пока не получим ошибку, но это неэффективно и неудобно.

Тип может быть общим: например, список, набор или график. Или это может быть предметная область: карта улиц, база данных сотрудников, телефонная книга и т. д. Но она не должна смешивать общие и специфичные для предметной области функции. Тип Deck, предназначенный для представления последовательности игральных карт, не должен иметь общий метод добавления, который принимает произвольные объекты, такие как целые числа или строки. И наоборот, не имеет смысла помещать специфичный для предметной области метод, такой как DealCards, в общий тип List .

Независимость представления

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

Вы вообще не сможете изменить представление АТД, если его операции не будут полностью определены предусловиями и постусловиями, чтобы клиенты знали, от чего зависеть, а вы знали, что можно безопасно изменить.

Пример: различные представления строк

Давайте рассмотрим простой абстрактный тип данных, чтобы понять, что означает независимость представления и почему она полезна. В приведенном ниже типе MyString гораздо меньше операций, чем в настоящей Java String, и их спецификации немного отличаются, но это все равно иллюстративно. Вот спецификации для ADT:

Эти общедоступные операции и их спецификации являются единственной информацией, которую может знать клиент этого типа данных. Фактически, следуя парадигме программирования «сначала тесты», первый клиент, который мы должны создать, — это набор тестов, который выполняет эти операции в соответствии с их спецификациями. Однако на данный момент написание тестов, использующих assertEquals непосредственно для объектов MyString, не сработает, потому что у нас нет операции равенства, определенной для MyString. Мы поговорим о том, как тщательно реализовать равенство, в следующем чтении. На данный момент единственные операции, которые мы можем выполнять с MyString, — это те, которые мы определили выше: valueOf, length, charAt и substring. Наши тесты должны ограничиваться этими операциями. Например, вот один тест для операции valueOf:

Мы вернемся к вопросу тестирования АТД в конце этого чтения.

А пока давайте рассмотрим простое представление для MyString : просто массив символов, точная длина строки, без лишнего места в конце. Вот как это внутреннее представление будет объявлено как переменная экземпляра внутри класса:

При таком выборе представления операции будут реализованы простым способом:

Вопрос для размышления: почему charAt и substring не должны проверять, находятся ли их параметры в допустимом диапазоне? Как вы думаете, что произойдет, если клиент вызовет эти реализации с недопустимыми входными данными?

Одна из проблем этой реализации заключается в том, что она упускает возможность повышения производительности. Поскольку этот тип данных является неизменяемым, операция подстроки на самом деле не должна копировать символы в новый массив. Он может просто указывать на массив символов исходного объекта MyString и отслеживать начало и конец, которые представляет новый объект подстроки. Реализация String в некоторых версиях Java делает это.

Чтобы реализовать эту оптимизацию, мы могли бы изменить внутреннее представление этого класса на:

С этим новым представлением операции теперь реализованы следующим образом:

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

Реализация концепций ADT в Java

Давайте подытожим некоторые общие идеи, которые мы обсуждали в этом чтении, применимые в целом к ​​программированию на любом языке, и их конкретную реализацию с использованием возможностей языка Java. Дело в том, что есть несколько способов сделать это, и важно понимать как общую идею, например, операцию создателя, так и различные способы реализации этой идеи на практике.

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

Читайте также: