Прежде чем приступить к изучению объектно-ориентированного программирования (в дальнейшем для краткости - ООП ) имеет смысл разобраться, что же это такое и каковы исторические причины его возникновения.
Для этого проведём краткий экскурс истории развития языков программирования.
В первой половине прошлого века возможности компьютеров первого поколения (Z4, Манчестерский Марк I , UNIVAC I, МЭСМ) были весьма невелики, и программирование преимущественно велось непосредственно в машинных кодах. Программы создавались интуитивно, и говорить о каких-либо технологиях не приходилось. Программирование было прерогативой научных учреждений и программы использовались самими программистами, а понятие «пользователя» отсутствовало за неимением последнего.
Рисунок 1. Компьютеры первого поколения: а) Z4 (Германия, 1950); б) Манчестерский Марк I (Великобритания, 1949); в) UNIVAC I (США, 1951); г) МЭСМ (СССР, 1950)
Уже первые вычислительные машины (ВМ) были способны выполнять множество команд и для создания программ нужно помнить их коды. По мере увеличения количества команд трудоёмкость создания программ становилась непозволительно высокой. Поэтому уже в то время у конструкторов вычислительных машин возникла идея создания искусственного языка, который позволил бы сделать процесс создания программ более производительным. Именно в этот период для преодоления трудностей программирования в машинном коде родилась фундаментальная для технологии программирования концепция модульного программирования, а затем и ассемблер (англ. assembler - сборщик), представляющий собой символическое представление машинного языка. Поскольку система команд у ВМ различались (и продолжают различаться до сих пор), то и язык ассемблера для каждой машины был (и остаётся) свой. Написание программ на ассемблере по прежнему требовало (и требует) от программиста такого же хорошего знания внутреннего устройства ВМ, как и программирования непосредственно в кодах. Именно поэтому Ассемблер является языком программирования низкого уровня.
На первых ВМ решались исключительно вычислительные математические задачи, методы решения которых традиционно излагаются в виде формул. Именно поэтому первые языки высокого уровня были направлены на перевод формул в форму, понятную ВМ. При этом от программиста уже не требовалось в деталях представлять каким именно образом данные и программа размещены в памяти и можно сосредоточиться на алгоритме вычислений. Первым языком высокого уровня принято считать описанный немецким инженером Конрадом Цузе язык Планкалкюль (планировщик вычислений), пригодный для написания программ для ВМ Z4 и ей подобных. Цузе написал на этом языке около 50 программ решения математических задач и анализа шахматных позиций. Однако практически реализовать язык и свои программы Цузе при жизни так и не удалось (компилятор Планкалькуль был создан лишь в 2000 году, через пять лет после смерти Цузе).
Первым практически реализованным языком высокого уровня является ФОРТРАН. Он был создан в период с 1954 по 1957 год группой программистов под руководством Джона Бэкуса в корпорации IBM., Название Fortran является аббревиатурой от FORmula TRANslator, то есть, переводчик формул. Фортран широко используется в первую очередь для научных и инженерных вычислений. Одно из преимуществ современного Фортрана — большое количество написанных на нём программ и библиотек подпрограмм. Среди учёных, например, ходит такая присказка, что любая математическая задача уже имеет решение на Фортране, и, действительно, можно найти среди тысяч фортрановских пакетов и пакет для перемножения матриц, и пакет для решения сложных интегральных уравнений, и многие, многие другие. Ряд таких пакетов создавались на протяжении десятилетий и популярны (главным образом в научной среде) по сей день. Большинство таких библиотек является фактически достоянием человечества: они доступны в исходных кодах, хорошо документированы, отлажены и весьма эффективны. Поэтому изменять, а тем более переписывать их на других языках программирования накладно, несмотря на то, что регулярно производятся попытки автоматического конвертирования FORTRAN-кода на современные языки программирования.
Следует отметить, что Фортран - единственный язык, пробившийся для использования в следующие десятилетия.
60-е годы ознаменованы бурным развитием языков программирования высокого уровня:
В конце 60-х появляется разработанный сотрудниками Норвежского Вычислительного Центра (Осло) Кристеном Нюгором и Уле-Йоханом Далем для моделирования сложных систем первый в мире объектно-ориентированный язык программирования Simula-67.
Этот язык в значительной степени опередил своё время, программисты 60-х годов оказались не готовы воспринять все ценности нового подхода к разработке программ, и он не выдержал конкуренции с другими языками программирования (прежде всего, с языком Fortran). Прохладному отношению к языку Simula 67 способствовало и то обстоятельство, что его реализация была весьма неэффективна, не в последнюю очередь из-за использования сборки мусора. Тем не менее, этот язык активно использовался в образовательном процессе в высших учебных заведениях. В современной терминологии Simula 67 можно охарактеризовать как объектное расширение Algol 60.
Постепенно стало понято, что важно не столько то, на каком языке программировать, сколько, как это делать. Это было уже началом серьёзных размышлений над методологией и технологией программирования.
Pascal (Никлаус Вирт, 1970; ETH, Швейцария),
C (Кен Томпсон иДеннис Ритчи, 1971; AT&T Bell Labs, США)
Smalltalk (произносится [смо'лток]) (Алан Кей, 1972; Xerox PARC, США).
Эта великая тройка дала путёвку в жизнь трём важнейшим направлениям: структурному (Pascal), системному (C) и объектно-ориентированному программированию (Smalltalk). Она определила и разные языковые ветви с непохожим синтаксисом и существенно отличающейся языковой культурой.
Зародилась и стала интенсивно развиваться технология программирования: обоснование и широкое внедрение нисходящей разработки (так называемого подхода «сверху-вниз») и структурного программирования, развитие типов данных и модульного программирования, исследование проблем обеспечения надёжности и мобильности программ, создание методики управления их коллективной разработки, появление средств поддержки технологии программирования.
Язык Smalltalk был создан группой исследователей возглавляемой Аланом Кэйем в исследовательском центре Xerox PARC. Первая реализация, известная как Smalltalk-71, была создана за несколько месяцев как результат спора о том что язык программирования, основанный на идее посылки сообщений, подсказанной Симулой, должен реализовываться на «странице кода». Более поздняя версия, действительно использованная для исследовательской работы, известна сейчас как Smalltalk-72. Его синтаксис и модель исполнения сильно отличались от современного Smalltalk, настолько, что его надо рассматривать как другой язык. После существенных переработок, которые зафиксировали несколько сторон семантики выполнения для увеличения эффективности, была создана версия известная как Smalltalk-76. В этой версии добавились наследование, синтаксис более близкий к Smalltalk-80, и среда разработки включающую большинство инструментов знакомых сейчас Smalltalk-ерам. В Smalltalk-80 были добавлены метаклассы, что делало фразу «всё — объекты» истинной путём связывания с индивидуальными классами свойств и поведения (например, поддержки различных способов создания экземпляров). Smalltalk-80 был первой версией, доступной за пределами PARC — сначала как Smalltalk-80 Version 1, розданный небольшому количеству компаний и университетов для «экспертной оценки». Позже, в 1983 году, были выпущены общедоступная реализация, известная как Smalltalk-80 Version 2 — в виде образа (независимый от платформы файл, содержащий объекты) и спецификации виртуальной машины. Сейчас существует две реализации Smalltalk, являющиеся прямыми потомками Smalltalk-80 — Squeak и VisualWorks.
В результате бурного развития языков программирования в трёх направлениях возникло хрупкое равновесие между сложностью решаемых задач и технологиями программирования.
Языки программирования 80-х:
Basic - продуктивный, но неэффективный;
Objective-C (ObjC, Obj-C) - компилируемый объектно-ориентированный язык;
Ада (Ada) — в высшей степени объектно-ориентированный язык программирования;
Си++ добавляет к С объектно-ориентированные возможности;
Object Pascal - объектное расширение Pascal.
Ситуация казалось бы стабилизировалась, но в 80-х годах появляются персональные компьютеры, которые стали стремительно внедряться во все сферы человеческой деятельности, порождая тем самым обширный и разнообразный контингент пользователей программ. Это потребовало развития пользовательских интерфейсов и создания чёткой концепции качества программ.
С появлением DOS программисты встали перед нелёгким выбором между продуктивным, но неэффективным Basic и эффективным, но крайне непродуктивным ассемблером. Начался бум микрокомпьютеров, и трансляторы для распространённых языков (Паскаля, Си, Бейсика) были оперативно оптимизированы под маломощные системы. Появление компиляторов Pascal и C во многом решило их проблемы, однако тем временем сложность решаемых задач все росла и возможности по их алгоритмической декомпозиции были практически исчерпаны.
Уже в начале 80-х появляется созданный Брэдом Коксом Objective-C, известный также как Objective C, ObjC или Obj-C — компилируемый объектно-ориентированный язык программирования корпорации Apple, построенный на основе языка Си и парадигм Smalltalk.
В 1979—1980 годах в результате проекта, предпринятого Министерством обороны США с целью разработать единый язык программирования для встраиваемых систем (то есть систем управления автоматизированными комплексами, работающими в реальном времени) был создан Ада (Ada) — в высшей степени объектно-ориентированный язык программирования. Имелись в виду, прежде всего, бортовые системы управления военными объектами (кораблями, самолётами, танками, ракетами, снарядами и т. п.).
Язык назван в честь Ады Лавлэйс.
Примерно в то же время сотрудник фирмы Bell Laboratories Бьёрн Страуструп придумал ряд усовершенствований к языку Си под собственные нужды, который он сначала назвал "C with classes" ("Си с классами"). С ростом популярности язык сменил название на "С++".
В названии "С++" по замыслу Струструпа два плюса обозначают операцию автоинкремента (увеличение значения на следующее) и его следует понимать как "следующий после C" или "новое поколение С". В английской транскрипции название читается "си-плас-плас", но в русскоязычном сообществе программистов устойчиво укрепилось "русифицированное" произношение "си-плюс-плюс".
Си++ добавляет к Си объектно-ориентированные возможности. Он вводит классы, которые обеспечивают три самых важных свойства ООП: инкапсуляцию, наследование и полиморфизм Раздел 1.2.
В 1986 году фирма Apple разработала объектное расширение языка Паскаль, получив в результате Object Pascal. В 1989 году аналогичные объектные средства были добавлены фирмой Borland в систему Turbo Pascal (языковые различия между объектным Turbo Pascal 5.5 и Object Pascal от Apple крайне незначительны). Последующие версии Turbo Pascal, который стал затем называться Borland Pascal, дополняли объектное расширение новыми средствами, этот процесс продолжился и в ходе развития языка системы Delphi.
С появлением операционной системы Windows кризис повторился на новом уровне - приходилось выбирать между мощным, но требующим специальных знаний C++ и простым (притягательно визуальным), но крайне ограниченным Visual Basic.
В 90-е годы начался продолжающийся по сей день решающий этап полной информатизации и компьютеризации общества. Растущая сложность задач информатизации поставила ребром вопрос о скорости и надёжности разрабатываемых программ. Компьютерная индустрия ответила на это развитием объектно-ориентированных методов программирования:
Мощные среды разработки
1995 год - Sun Microsystems выпускается объектно-ориентированный язык программированияJava (предназначен для кроссплатформенной разработки приложений);
В 1991 компания Microsoft выпускает первую версию Visual Basic;
Фортран 95 становится объектно-ориентированным;
Фирма Borland в 1995 году выпускает революционное по тем меркам объектно-ориентированное средство разработки Delphi
1997 год - PHP стал поддерживать объектно-ориентированный синтаксис;
В 2000-х годах сетевые технологии и сеть Интернет прочно вошла в обиход даже весьма отдаленных от компьютерной тематики областей человеческой деятельности.
С начала 21-го века развитие информационных технологий и их проникновение в обычную жизнь приобрело лавинообразный характер. Такое проникновение можно условно разделить на два направления: "микро" и "макро".
В направлении "микро" это проявилось в широком использовании устройств, которые по сути дела являются компьютерами, но сохранили за собой свои традиционные названия: телефон, плеер, органайзер, диктофон, фотоаппарат, видеокамера и т.д. Причём зачастую одно устройство выполняет множество функций. Так например, современный мобильный телефон представляет собой эдакий "комбайн" из всех вышеперечисленных устройств, по прежнему скромно называясь "телефоном".
Интересно, что телекоммуникационные устройства с предустановленными операционными системами семейства Windows стали называться "коммуникаторами", а все остальные - по прежнему "телефонами", хотя весьма незначительные различия между ними продолжают стираться.
Тенденция объединять различную функциональность в одном устройстве получившая название "конвергенция". Программируемой стала даже бытовая техника: телевизоры; стиральные, швейные и посудомоечные машины; микроволновые и электрические печи; кофемолки и холодильники. Так что пользователями ЭВМ стали даже домохозяйки, которые зачастую даже не догадываются об этом. Производство встраиваемых систем потребовало развития языков программирования и для программирования встраиваемых систем наряду с применением кроссплатформенных компиляторов разработки стал применяться интерпретируемый язык Java.
В макронаправлении сеть Интернет за несколько лет проникла буквально во все сферы деятельности современного человека. Рядовой пользователь сети из пассивного читателя статических сайтов превратился в автора в многочисленных форумах, блогах и энциклопедиях. Столь бурное развитие информационных технологий сопровождается весьма быстрым, но гораздо менее заметным для обычного пользователя развитием языков программирования. Так, для обеспечения возможности управления элементами веб-страниц с целью придания им некоторой динамичности и интерактивности (возможности общения с пользователем) в 1995 году появился язык JavaScript, полностью занявший нишу браузерных языков программирования. JavaScript - разноплановый (или как сейчас говорят - "мультипарадигменный") язык. Он несёт в себе черты как объектно-ориентированного так и обобщённого, функционального, императивного, аспектно-ориентированного, событийно-ориентированного и прототипного программирования. По мере развития интернет-технологий стало ясно, что интерактивных веб-страниц с элементами JavaScript недостаточно для решения множества задач. Для программирования на стороне сервера с целью создания динамических веб-страниц стали применяться уже существующие к тому времени языки Perl и Python. Для обеспечения перенаправления результатов работы программы веб-серверу стал применяться стандарт интерфейса CGI (от англ. Common Gateway Interface — «общий интерфейс шлюза»). Очень скоро стало ясно, возможностей универсальных языков программирования для создания динамических страниц недостаточно и стали появляться специализированные языки серверного программирования: Язык PHP (PHP - рекурсивный акроним словосочетания «PHP: Hypertext Preprocessor»), появившись всего лишь в конце 90-х прошлого века, проделал такой путь развития, что стал стандартом де-факто для написания серверных скриптов (программ, выполняющихся на стороне сервера и показывающих результат своей работы пользователю в привычном для него виде - веб-страницы). Как результат PHP из набора скриптов стал полноценным объектно-ориентированным языком программирования, позволяющим создавать мощные веб-приложения. Фирма Microsoft для программирования на сервере стала продвигать язык ASP (от англ. -Active Server Pages - активные серверные страницы), а позже ее вариант для платформы .NET - ASP.NET.
Однако по мере проникновения веб-технологий в жизни обычных людей становилось все более очевидным различие между мощным красивым графическим пользовательским интерфейсом и неповоротливым текстовым интерфейсом веб-страниц. Ни один язык оказался не способен разрешить это противоречие в одиночку. Как результат конвергенции HTML, Javascript и XML в 2005 году появился "сплав" языков и технологий, получивший акроним AJAX (от англ. Asynchronous Javascript and XML — «асинхронный JavaScript и XML»). Суть нового подхода к построению интерактивных пользовательских интерфейсов веб-приложений заключается в «фоновом» обмене данными браузера с веб-сервером. В результате при обновлении данных веб-страница не перезагружается полностью, и веб-приложения становятся более быстрыми и удобными.
Конечно, в столь коротком экскурсе невозможно не то чтобы дать характеристику каждому языку программирования, но даже упомянуть наиболее достойные. Подводя итог изложенной истории можно сказать, что последовательная эволюция правил и практических приёмов разработки сложных программных систем привела к возникновению и последующему развитию качественно нового подхода к их созданию, в соответствии с которым компьютерная программа проектируется как система взаимодействующих объектов. Совокупность способов реализации такого подхода получила название методологии объектно-ориентированного программирования.
ООП – весьма прогрессивный и естественный подход к построению программных систем, заключающийся в том, что программа проектируется как совокупность взаимодействующих друг с другом объектов, являющихся экземплярами определённых иерархически упорядоченных классов.
Подробнее принципы объектно-ориентированной разработки программ изложены в Раздел 1.2.
Способ управления сложными системами был известен ещё в древности: divide et impera (разделяй и властвуй). Применительно к проектированию программных систем он заключается в том, что необходимо составлять ее из небольших относительно самостоятельных подсистем, разрабатываемых и отлаживаемых независимо от других. Другими словами основной целью декомпозиции является разделение сложной системы на более простые составляющие.
Известны два подхода к декомпозиции сложной системы: алгоритмическая и объектная.
При объектной декомпозиции проектируемая программа представляется в виде совокупности объектов, обменивающихся сообщениями.
Попробуем разобраться с основным понятием ООП - объектом.
Вообще, объект это - предмет или явление (сущность), обладающая следующими атрибутами:
определёнными свойствами (характеристиками);
способностью совершать действия;
способностью реагировать на происходящие события.
В ООП для представления объектов используется специальная структура, обеспечивающая хранение совокупности:
данных, описывающих характеристики объекта (свойства - properties),
алгоритмов его действий (методы - methods)
событий, на которые он может реагировать (события - events).
Рассмотрим в качестве примера объекта мобильный телефон.
Во-первых, он обладает рядом свойств - размеры, вес, разрешающая способность дисплея, объем телефонной книги и т.д. Можете продолжить список в соответствии со своими предпочтениями.
Во-вторых, он может совершать некоторые действия - набирать номер, звонить, посылать SMS и т.д. Для этого он использует реализуемые внутри него методы.
В-третьих, он реагирует на некоторые события - нажали цифровую клавишу - включилась подсветка дисплея и цифра появилась на экране, поступил вызов - звучит мелодия и т.д.
В обобщённом виде схема организации объекта приведена на Рисунок 2
В современной учебной литературе ??? принято выделять только три основных принципа ООП: иерархия, инкапсуляция и полиморфизм. В то же время не для практической работы не менее важно понимания и основополагающих принципов абстрагирования и модульности ???.
Рассмотрим их в том порядке, в котором они возникают при проектировании.
Итак, в результате объектной декомпозиции были выделены объекты. При этом они могут оказаться весьма сложными по своей структуре (как например тот же сотовый телефон) и воспроизвести все его свойства, методы и события не представляется возможным. А этого делать и нужно. Следует выделить наиболее существенные характеристики объекта, отличающие его от всех других видов объектов. Несущественные характеристики объектов принимать во внимание не следует, от них нужно абстрагироваться .
В результате такого подхода мы получаем не точную копию объекта реального мира, а его упрощённую модель - абстракцию.
Абстракция выделяет существенные характеристики некоторого объекта, отличающие его от всех других видов объектов и, таким образом, чётко определяет его концептуальные границы с точки зрения наблюдателя.
Следует заметить, что направление и уровень абстракции существенно зависит от того, для чего разрабатывается программа (вышеупомянутой "точки зрения наблюдателя"). Так, например, для мультимедийного каталога телефонов в качестве существенных характеристик выступают его внешний вид, массогабаритные и пользовательские характеристики, а от материала, из которого изготовлена печатная плата, и химического состава припоя лучше абстрагироваться. В то же время, если вы создаёте технологическую линию по производству печатных плат для телефонов, то ситуация изменяется на прямо противоположную.
Рисунок 3. Абстракция фокусируется на существенных с точки зрения наблюдателя характеристиках объекта
Если в процессе объектной декомпозиции выяснилось, что некоторые объекты характеризуются одним набором характеристик, то говорят, что они принадлежат к одному классу.
Для представления абстракций объектов используется специальный определяемый программистом тип данных - класс.
Класс - это структурный тип данных, который включает описание полей данных, а также методов (процедур и функций), работающих с этими полями.
Процесс объединения данных с действиями над этими данными получил название инкапсуляции.
Суть инкапсуляции заключается в скрытии внутренней реализации объекта.
Практически это означает наличие двух частей в классе: интерфейса и реализации.
Интерфейс - это совокупность доступных извне характеристик состояния и поведения объекта. Реализация- это совокупность недоступных извне алгоритмов поведения объекта.
Принцип разделения интерфейса и реализации соответствует сути вещей: в интерфейсной части собрано все, что касается взаимодействия данного объекта с любыми другими объектами; реализация скрывает от других объектов все детали, не имеющие отношения к процессу взаимодействия объектов.
Таким образом, инкапсуляция - это процесс отделения друг от друга элементов объекта, определяющих его устройство и поведение.
Значительное упрощение в понимании сложных задач достигается за счёт образования из абстракций иерархической структуры.
Иерархия - это расположение частей или элементов целого от высшего к низшему, это упорядочивание абстракций, расположение их по уровням.
В ООП иерархия подразумевает два вида отношений:
отношения наследования
отношения включения (использования)(Рисунок 6)
Важнейшим видом иерархии является наследование.
Программист для решения определённого класса задач может строить иерархию классов, в которой (и это самой главное) каждый следующий производный класс имеет доступ к данным и действиям своих предков (суперклассов). Потомок получает в своё распоряжение все, что принадлежало его предку. Потомок может добавить новые методы, свойства и поля и изменить реализацию любого метода, но не может их уничтожить.
При отношениях включения (использования) в состав некоторого класса может в качестве поля (свойства) включаться другой класс.
Различают несколько терминов, связанных с конкретными механизмами реализации полиморфизма для различных случаев:
1. Чистый полиморфизм - используется для обозначения того, что один код функции может по-разному интерпретироваться в зависимости от типа аргументов;
2. Перегрузка (полиморфные имена функций) - используется, когда определяется несколько функций с одним именем - одно и то же имя функции может многократно использоваться в разных местах программы;
3. Переопределение (перекрытие) используется в ООП при необходимости задания различных реализаций одноимённых методов в иерархии классов; различают следующие виды полиморфизма:
1. Чистый полиморфизм - используется для обозначения того, что один код функции может по-разному интерпретироваться в зависимости от типа аргументов;
Чистый полиморфизм используется в языках высокого уровня абстракции, например, в языке Lisp или Smalltalk;
2. Перегрузка (полиморфные имена функций) - используется, когда определяется несколько функций с одним именем - одно и то же имя функции может многократно использоваться в разных местах программы;
выбор нужной функции может определяться типами аргументов, областью видимости (внутри модуля, файла, класса и т. д.); если выбор определяется типом аргументов, то перегрузка называется параметрической; например, язык C++ позволяет разработчику выполнять параметрическую перегрузку функций как вне классов, так и внутри;
3. Переопределение (перекрытие) используется в ООП при необходимости задания различных реализаций одноимённых методов в иерархии классов; различают следующие виды полиморфизма:
а) простой полиморфизм - конкретный метод определяется типом объекта при компиляции программы (раннее связывание); одноимённые методы при использовании простого полиморфизма называют статическими полиморфными;
б) сложный полиморфизм (полиморфные объекты) - конкретный метод определяется типом объекта при выполнении программы (позднее связывание); одноимённые методы при использовании сложного полиморфизма называют виртуальными полиморфными;
Связи между модулями - это их представление друг о друге.
Правильное разделение программы на модули является почти такой же сложной задачей, как выбор правильного набора абстракций. Модули играют роль физических контейнеров, в которые помещаются определения классов и объекты при логическом построении системы. Для описания небольших систем допустимо описание всех классов помещать в один модуль. Однако для большинства программ лучшим решением будет сгруппировать в отдельный модуль логически связанные классы и объекты, оставив открытыми те элементы, которые совершенно необходимо видеть другим модулям.
Таким образом, модульность - это свойство системы, которая была разложена на внутренне связные, но слабо связанные между собой модули.
Мы рассмотрели теоретические основы объектно-ориентированного подхода к разработке программ.
Следует особо подчеркнуть, что ООП является методологией проектирования и общей концепцией программирования, а не особенностью какого-либо языка. Существует множество объектно-ориентированных языков программирования (Lisp, Smalltalk, C++, Java, PHP, Ada, Delphi (ранее - Object Pascal)), каждый из которых реализует концепцию ООП по-своему, со своим синтаксисом, типизацией и другими, присущему конкретному языку особенностями.
Какие подходы могут применяться для декомпозиции системы?
Какими атрибутами может обладать объект?
Какие существуют принципы объектно-ориентированного программирования?
В чем заключается принцип абстрагирования?
Что такое класс?
В чем заключается принцип инкапсуляции?
На какие части делиться класс в соответствии с принципом инкапсуляции?
В чем заключается принцип модульности?
В чем заключается принцип иерархии?
Какие виды иерархии существуют?