Введение
Техника управления сложностью компьютерных систем
В разработке программного обеспечения и информатике, абстракция – это процесс обобщения конкретных деталей, таких как атрибуты, от изучения объектов и систем, чтобы сосредоточить внимание на более важных аспектах. Абстракция является фундаментальным понятием в информатике и разработке программного обеспечения, особенно в парадигме объектно-ориентированного программирования. Примеры включают:
использование абстрактных типов данных для отделения интерфейса от реализации данных в программах;
концепция функций или подпрограмм, представляющих собой конкретный способ реализации потока управления;
процесс выделения общего поведения из групп конкретных классов в абстрактные классы с использованием наследования и подклассов, как это встречается в объектно-ориентированных языках программирования.
the concept of functions or subroutines which represent a specific way of implementing control flow;
the process of reorganizing common behavior from groups of non abstract classes into abstract classes using inheritance and sub classes, as seen in object oriented programming languages.
Обоснование
Компьютерные технологии в основном функционируют независимо от реального мира. Аппаратное обеспечение реализует модель вычислений, взаимозаменяемую с другими. Программное обеспечение структурировано в архитектуры, позволяющие людям создавать огромные системы, концентрируясь на нескольких задачах одновременно. Эти архитектуры основаны на конкретных выборах абстракций. Десятое правило Гринспана – это афоризм о том, как такая архитектура одновременно неизбежна и сложна. Языковая абстракция является центральной формой абстракции в вычислительной технике: разрабатываются новые искусственные языки для выражения конкретных аспектов системы. Языки моделирования помогают в планировании. Компьютерные языки могут обрабатываться компьютером. Примером этого процесса абстракции является последовательное развитие языка программирования от машинного языка к языку ассемблера и языку высокого уровня. Каждый этап может служить ступенькой к следующему. Языковая абстракция продолжается, например, в скриптовых языках и языках программирования, ориентированных на конкретную область. В рамках языка программирования некоторые возможности позволяют программисту создавать новые абстракции, такие как подпрограммы, модули, полиморфизм и программные компоненты. Некоторые другие абстракции, такие как шаблоны проектирования программного обеспечения и архитектурные стили, остаются невидимыми для разработчика и применяются только при проектировании системы. Некоторые абстракции стремятся ограничить круг понятий, которые должен учитывать программист, полностью скрывая лежащие в их основе абстракции. Инженер-программист и писатель Джоэл Спольски критиковал эти попытки, утверждая, что все абстракции несовершенны – они никогда не могут полностью скрыть детали реализации; однако это не умаляет полезности абстракции. Некоторые абстракции предназначены для взаимодействия с другими абстракциями – например, язык программирования может содержать интерфейс для вызова функций, написанных на языке более низкого уровня.
Языки программирования
Различные языки программирования обеспечивают различные типы абстракции в зависимости от предполагаемого применения языка. Например:
В объектно-ориентированных языках программирования, таких как C++, Object Pascal или Java, само понятие абстракции стало декларативным утверждением – с использованием синтаксиса function(parameters) = 0; (в C++) или ключевых слов abstract и interface (в Java). После такой декларации ответственность за реализацию класса для создания экземпляра объекта этой декларации ложится на программиста. Функциональные языки программирования часто демонстрируют абстракции, связанные с функциями, такие как лямбда-абстракции (преобразование выражения в функцию некоторой переменной) и функции высшего порядка (параметрами которых являются функции). Современные представители семейства языков программирования Lisp, такие как Clojure, Scheme и Common Lisp, поддерживают макросистемы, позволяющие осуществлять синтаксическую абстракцию. Другие языки программирования, такие как Scala, также имеют макросы или очень похожие возможности метапрограммирования (например, у Haskell есть Template Haskell, а у OCaml – MetaOCaml). Это может позволить программисту исключить шаблонный код, абстрагироваться от утомительных последовательностей вызовов функций, реализовать новые структуры управления потоком и реализовать предметно-ориентированные языки (DSL), позволяющие выражать концепции предметной области кратко и элегантно. Все эти методы, при правильном использовании, повышают эффективность работы программиста и ясность кода, делая предполагаемую цель более явной. Следствием синтаксической абстракции также является то, что любой диалект Lisp и, фактически, почти любой язык программирования может, в принципе, быть реализован на любом современном Lisp со значительно меньшими (но все же нетривиальными в большинстве случаев) усилиями по сравнению с "более традиционными" языками программирования, такими как Python, C или Java.
Языки спецификации
Языки спецификаций обычно используют те или иные абстракции, поскольку спецификации, как правило, определяются на более ранних этапах проекта (и на более высоком уровне абстракции), чем последующая реализация. Например, язык спецификаций UML позволяет определять абстрактные классы, которые в проекте, реализованном каскадной моделью, остаются абстрактными на фазе архитектуры и спецификации.
Структурированное программирование
Структурное программирование подразумевает разделение сложных программных задач на более мелкие части с четким управлением потоком и интерфейсами между компонентами, что снижает вероятность возникновения побочных эффектов и упрощает отладку. В простой программе это может означать обеспечение наличия у циклов единственных или очевидных точек выхода и (по возможности) единственных точек выхода из функций и процедур. В более крупной системе это может включать в себя разбивку сложных задач на множество различных модулей. Рассмотрим систему для расчета заработной платы на судах и в береговых офисах: верхний уровень может представлять собой меню типичных операций для конечного пользователя. Внутри него могут располагаться автономные исполняемые файлы или библиотеки для таких задач, как регистрация прибытия и убытия сотрудников или печать платежных ведомостей. Каждый из этих автономных компонентов может состоять из множества исходных файлов, каждый из которых содержит программный код для решения определенной части задачи, при этом другим частям программы доступны лишь выбранные интерфейсы. Программа регистрации прибытия может содержать исходные файлы для каждого экрана ввода данных и интерфейса базы данных (который сам по себе может быть автономной библиотекой стороннего разработчика или статически связанным набором библиотечных подпрограмм). Инициацию процесса обмена данными между судном и берегом должна осуществлять либо база данных, либо приложение для расчета заработной платы, и эта задача передачи данных часто включает в себя множество других компонентов. Эти уровни создают эффект изоляции деталей реализации одного компонента и его внутренних методов от других компонентов. Объектно-ориентированное программирование развивает и расширяет эту концепцию.
The uppermost level may feature a menu of typical end user operations. Within that could be standalone executables or libraries for tasks such as signing on and off employees or printing checks. Within each of those standalone components there could be many different source files, each containing the program code to handle a part of the problem, with only selected interfaces available to other parts of the program. A sign on program could have source files for each data entry screen and the database interface (which may itself be a standalone third party library or a statically linked set of library routines). Either the database or the payroll application also has to initiate the process of exchanging data with between ship and shore, and that data transfer task will often contain many other components. These layers produce the effect of isolating the implementation details of one component and its assorted internal methods from the others. Object oriented programming embraces and extends this concept.
Абстракция данных
Абстракция данных обеспечивает четкое разделение между абстрактными свойствами типа данных и конкретными деталями его реализации. Абстрактные свойства – это те, которые видны клиентскому коду, использующему этот тип данных, – интерфейс типа данных, – в то время как конкретная реализация остается полностью скрытой и, более того, может изменяться, например, для повышения эффективности со временем. Предполагается, что такие изменения не должны влиять на клиентский код, поскольку они не влекут за собой изменений в абстрактном поведении. Например, можно определить абстрактный тип данных под названием «таблица поиска», который однозначно сопоставляет ключи со значениями, и в котором значения можно получить, указав соответствующие ключи. Реализовать такую таблицу поиска можно разными способами: как хеш-таблицу, двоичное дерево поиска или даже простой линейный список пар (ключ: значение). Для клиентского кода абстрактные свойства типа остаются одинаковыми в любом из этих случаев. Разумеется, все это зависит от правильной проработки деталей интерфейса, поскольку любые изменения в нем могут существенно повлиять на клиентский код. Интерфейс можно рассматривать как соглашение об оговоренном поведении между типом данных и клиентским кодом; все, что не прописано в этом соглашении, может быть изменено без предварительного уведомления.
Ручная абстракция данных
Хотя большая часть абстракции данных осуществляется с помощью компьютерных наук и автоматизации, бывают случаи, когда этот процесс выполняется вручную, без использования программирования. Один из способов понять это – рассмотреть абстракцию данных в процессе проведения систематического обзора литературы. В этой методологии данные извлекаются одним или несколькими исследователями при проведении метаанализа, а количество ошибок снижается благодаря двойной абстракции данных с последующей независимой проверкой, известной как арбитраж.
Объектно-ориентированный дизайн
Решения о том, что абстрагировать, а что оставить под контролем программиста, становятся ключевой задачей объектно-ориентированного проектирования и анализа предметной области. По сути, определение релевантных взаимосвязей в реальном мире является задачей объектно-ориентированного анализа или анализа существующей системы. В общем случае, для определения подходящей абстракции необходимо принять множество небольших решений относительно области действия (анализ предметной области), определить, с какими другими системами необходимо взаимодействовать (анализ существующей системы), а затем провести детальный объектно-ориентированный анализ, который, в рамках ограничений времени и бюджета проекта, преобразуется в объектно-ориентированный дизайн. В нашем простом примере, предметная область – это двор, живые свиньи и коровы, а их пищевые привычки – ограничения существующей системы. Детальный анализ показывает, что программисты должны иметь возможность кормить животных тем, что доступно, и, следовательно, нет необходимости кодировать тип корма непосредственно в класс. Дизайн представляет собой один простой класс Animal, экземплярами которого являются свиньи и коровы, обладающие одинаковыми функциями. Решение о создании отдельного класса DairyAnimal изменило бы детальный анализ, но анализ предметной области и анализ существующей системы остались бы неизменными – таким образом, это полностью находится под контролем программиста и называется абстракцией в объектно-ориентированном программировании, в отличие от абстракции в анализе предметной области или анализе существующей системы.
Рассмотрение
При обсуждении формальной семантики языков программирования, формальных методов или абстрактной интерпретации, под абстракцией понимается рассмотрение менее детализированного, но безопасного определения наблюдаемого поведения программы. Например, можно рассматривать только конечный результат выполнения программы, не учитывая все промежуточные этапы. Абстракция определяется относительно конкретной (более точной) модели исполнения. Абстракция может быть точной или соответствовать свойству, если на вопрос об этом свойстве можно ответить одинаково хорошо как на конкретной, так и на абстрактной модели. Например, если необходимо узнать, чему равен результат вычисления математического выражения, содержащего только целые числа и операции +, -, ×, по модулю n, достаточно выполнять все операции по модулю n (известная форма этой абстракции – приведение по модулю 9). Однако абстракции, хотя и не обязательно точные, должны быть корректными. То есть, они должны давать надёжные ответы, даже если абстракция может привести к результату неразрешимости. Например, студентов в классе можно представить минимальным и максимальным возрастом; если спрашивают, принадлежит ли человек этому классу, достаточно сравнить его возраст с минимальным и максимальным возрастом; если возраст выходит за пределы диапазона, можно с уверенностью сказать, что человек не принадлежит классу; в противном случае можно ответить только «Я не знаю». Уровень абстракции, поддерживаемый языком программирования, может влиять на его общую удобство использования. В рамках когнитивных измерений существует понятие градиента абстракции. Этот фреймворк позволяет разработчику языка программирования изучать компромиссы между абстракцией и другими характеристиками дизайна, а также влияние изменений в абстракции на удобство использования языка. Абстракции оказываются полезными при работе с компьютерными программами, поскольку нетривиальные свойства компьютерных программ по сути неразрешимы (см. теорему Райса). Следовательно, автоматические методы получения информации о поведении компьютерных программ должны либо отказываться от гарантий завершения (в некоторых случаях они могут завершиться неудачно, аварийно завершиться или никогда не выдать результат), либо жертвовать корректностью (они могут предоставлять ложную информацию), либо точностью (они могут отвечать «Я не знаю» на некоторые вопросы). Абстракция является ключевым понятием абстрактной интерпретации. Проверка моделей обычно выполняется на абстрактных версиях исследуемых систем.
Уровни абстракции
В информатике обычно рассматриваются уровни (или, реже, слои) абстракции, где каждый уровень представляет собой различную модель одних и тех же данных и процессов, но с разной степенью детализации. Каждый уровень использует систему представления, включающую уникальный набор объектов и их комбинаций, применимых только к определенной области. Каждый относительно абстрактный, "верхний" уровень строится на основе относительно конкретного, "нижнего" уровня, который, как правило, обеспечивает всё более детальное представление. Например, логические элементы основаны на электронных схемах, двоичная система – на логических элементах, машинный код – на двоичной системе, язык программирования – на машинном коде, приложения и операционные системы – на языках программирования. Каждый уровень реализуется, но не предопределяется лежащим в его основе уровнем, что делает его относительно самодостаточным языком описания.