- •Оглавление
- •Предисловие
- •Введение
- •Глава 1. Средства разработки приложений
- •1.1. Системные требования
- •1.2. Введение в NetBeans
- •1.3. Установка NetBeans
- •1.4. Первый запуск NetBeans
- •1.5. Интерфейс пользователя
- •1.6. Настройки среды
- •1.7. Создание проекта
- •Глава 2. Язык программирования Java
- •2.1. Первое приложение
- •2.2. Метод main
- •2.3. Основные понятия языка Java
- •2.3.1. Переменные и константы
- •2.4. Примитивные типы данных
- •2.4.1. Целые и символьные типы(byte, short, char, int, long)
- •2.4.2. Логический тип Boolean
- •2.4.3. Вещественные типы (float и double)
- •2.5. Ссылочные типы
- •2.6. Выражения и операторы
- •2.6.1. Логические операторы
- •2.6.2. Целочисленные битовые операторы
- •2.6.3. Арифметические операторы
- •2.6.4. Приоритеты выполнения операторов
- •2.6.5. Преобразование типов
- •2.6.6. Оболочечные классы
- •2.7. Класс Math
- •2.8. Сложные типы данных и строки
- •2.8.1. Массивы
- •2.8.2. Строки
- •2.9. Управляющие конструкции
- •2.9.1. Составной оператор
- •2.9.2. Условный оператор if
- •2.9.3. Оператор выбора
- •2.9.4. Операторы цикла
- •2.9.5. Операторы перехода
- •Глава 3. Введение в ООП
- •3.1. Основные принципы ООП
- •3.2. Состав структура и основные свойства классов
- •3.2.1. Описание полей класса
- •3.2.2. Оператор new
- •3.2.3. Описание методов
- •3.2.4. Модификаторы
- •3.2.5. Конструкторы
- •Совмещение методов
- •Глава 4. Рефакторинг
- •Глава 5. Ввод и вывод в Java
- •Глава 6. Графический интерфейс пользователя GUI
- •6.1. Компоненты и контейнеры
- •6.2. Встроенные пакеты GUI классов на основе библиотеки awt
- •6.2.1. Метка Label
- •6.2.2. Кнопка Button
- •6.2.3. Кнопка выбора CheckBox
- •6.2.4. Радио-кнопка Radiobutton
- •6.2.5. Поле ввода TextField
- •6.2.6. Поле ввода TextArea
- •6.3. Использование визуального редактора для проектирования ГИП
- •6.4. Графика в Java
- •6.5. Обработка событий
Глава 3. Введение в ООП
Обучение принципам ООП, объектно-ориентированное проектирование, объектная декомпозиция, создание повторно используемого кода.
3.1. Основные принципы ООП
Наиболее ранние упоминания об объектном подходе можно встретить еще у древнегреческих философов Аристотеля (Категории), Платона (Политик), которые заложили филосовские основы современныго представления об объектном подходе. В средние века произошли значительное осмысление и анализ различными авторами (Рене Декарт, Фома Аквинский и др) различных аспектов объектного подхода к реальной действительности. Так Р.Декарт в своей работе «Расуждение о методе» указывает, что человек имеет «объектноориенитрованный взгляд на мир». В XX веке интерес к объектному подходу значительно возрос в связи с стремительным развитием научно-технического процесса. Философ А.Рэнд разработал концепцию объетивистской эпистимологии, А. Минский предложил модель человеческого мыщления, как общность различно мыслящих агентов.
Вкомпьютерных науках интерес к оъектно-ориентированному программирования возрос тогда, когда методы структурного программирования и проектирования были не в состоянии решать проблемы связанные с возростающей сложностью процесса разработки программного обеспечения. В то время, как одним из главных достоинств объектно-ориенировнного подхода возможность решать с помощью него сложные задачи.
Алан Кей впервые ввел термин объектно-ориенированный. Известный специалист в областии ООП Гради Буч предложил следующее определение «объектно-ориентированное программирование — это методология программирования, основанная на представлении программмы в виде совокупности объектов, каждый из которых является экземпляром определенного класса, а классы образуют иерархию наследования»
В1967 году в Норвежском вычислительном центре (г. Осло, Норвегия) был представлен объектно-ориентированный язык Simula 67, созданный Кристеном Нюгором и Уле-Йоханом Далем. Наибольшее распространиеие язык получил в академической среде, и широко практического использования не получил. В дальнейшем оказал большое воздействие на последующие разработки обектноориентированых языков программирования. Следующим языком
оказавшим значительное влияние на разработу объектно-
ориентированного подхода является Smalltalk, разработанный в 70-хх годах XX века в компании Xerox А.Кеем. В язые Smalltalk все является объектом, а основная конструкция языка - передача сообщения между объектами. В основу языка положены следующие принципы объектноориентированного программирования:
1)объект — базовая единица объектно ориентированной системы;
2)объекты могут обладать состоянием;
3)посылка сообщений — единственный способ передачи информации между объектами.
Особый интерес к ООП стал развиваться в 80-х годах, XX века, когда стали появляться разработки объектно-ориентированных методов проектирования.
Преимущества ООП.
1)Построение сложных систем.
2)Повышение надежности программного обеспечения.
3)Улучшение сопровождения программного обеспечения. Возможность внесения изменений отдельных компонент измения остальных частей.
4)Расширение и масштабировнаие программного кода.
5)Создание аовторно используемого программного кода. Рассмотрим основные понятия оьъектно-ориентированного подхода: обьект, класс, основные принципы ООП, типы отношений между классами.
Обьекты
Под обьектом будем понимать понятие, абстракцию или любой предмет с четко очерченными границами, имеющий смысл в аспекте рассматриваемой проблемы. Введение объектов имеет следующие цели:
1)добиться большего понимания задачи;
2)введение основы для реализации на ЭВМ.
Примеры объектов: автомобиль, университет, журнал, студент и др. Каждый обьект в реальнм мире имеет определенное время жизни. В программирование объекты так же могут создаваться (новые обекты) и уничтожаться.
Гради Буч дает следующее определение объекта:
Объект — это мыслимая или реальная сущность, обладающая характерным поведением и отличительными характеристиками и являющаяся важной в предметной области.
Каждый объект имеет состояние, обладает четко определенным поведением и уникальной идентичностью.
Состояние
Рассмотрим в качестве объекта — человека. Любой человек в различные моменты времени может находится в различных состояниях (положениях): стоять, сидеть, лежать, веселый, грустный, и одновременно совершать какиелибо действия.
Например, человек может идти, если он стоит, и не может выполнять этого действия если лежит. Сначало, для выполнеия ему необходимо встать. Если у человека есть ручка, то он може писать, если ее нет то он не может выполнять такие действия. Таким образом набор действий которые может осуществлять в данный момент времени, ограничен и зависит от связей с другими объектами.
Набор действий, которые может совершать человек, зависят от параметровобъекта его моделирующего. В нашем случае набор параметров (характеристик), или говорят атрибутов объекта, котрые определяют его состояние можно считать следующие:
1)текущее положение человека (стоит, сидит, лежит)
2)наличие ручки (имеется, нет).
Вконкретных заданиях могут появится другие свойства и параметры характеризующие физическое и моральное состояние и др.
Состояние — совокупный результат поведения объекта:одно из стабильных условий, в которых объект может существовать, охарактеризованных количественно; в любой момент времени состояние объекта включает перечень свойств и их текущее значение, которые собственно определяют поведение обьекта.
Поведение
Для каждого объекта существует определенный набор действий, который с ним можно произвести. Например, возможные действия с некоторым файлом:
•создать;
•открыть;
•читать из файла;
•писать в файл;
•закрыть;
•удалить.
Результат выполнения действий зависит от состояния объекта на
момент совершения действия, т.е. нельзя, например, удалить файл, если он открыт кем-либо (заблокирован). В то же время действия могут менять внутреннее состояние объекта - при открытии или закрытии файла свойство "открыт" принимает значения "да" или "нет", соответственно.
В терминологии объектно-ориентированного подхода понятия "действие", "сообщение" и "метод" являются синонимами. Т.е. выражения "выполнить действие над объектом", "вызвать метод
объекта" и "послать сообщение объекту для выполнения какого-либо действия" эквивалентны. Последняя фраза появилась из следующей модели. Программу, построенную по технологии ООП, можно представить себе как виртуальное пространство, заполненное объектами, которые условно "живут" некоторой жизнью. Их активность проявляется в том, что они вызывают друг у друга методы, или посылают друг другу сообщения. Внешний интерфейс объекта, или набор его методов,- это описание того, какие сообщения он может принимать.
Программа, написанная с использованием ООП, обычно состоит из множества объектов, и все эти объекты взаимодействуют между собой. Обычно говорят, что взаимодействие между объектами в программе происходит посредством передачи сообщений между ними.
Поведение (behavior) - действия и реакции объекта, выраженные в терминах передачи сообщений и изменения состояния; видимая извне и воспроизводимая активность объекта
Уникальность
Уникальность - это то, что отличает объект от других объектов. Например, у вас может быть несколько одинаковых монет. Даже если абсолютно все их свойства (атрибуты) одинаковы (год выпуска, номинал и т.д.) и при этом вы можете использовать их независимо друг от друга, они по-прежнему остаются разными монетами.
В машинном представлении под параметром уникальности объекта чаще всего понимается адрес размещения объекта в памяти.
Наиболее распространенной ошибкой является понимание уникальности как имени ссылки на объект. Это неверно, т.к. на один объект может указывать несколько ссылок, и ссылки могут менять свои значения (ссылаться на другие объекты).
Identity (уникальность) объекта состоит в том, что всегда можно определить, указывают две ссылки на один и тот же объект или на разные объекты. При этом два объекта могут во всем быть похожими, их образ в памяти может представляться одинаковыми последовательностями байтов, но, тем не менее, их Identity может быть различна.
Итак, уникальность (identity) - свойство объекта; то, что отличает его от других объектов.
Классы
Все монеты из предыдущего примера принадлежат одному и тому же классу объектов (именно с этим связана их одинаковость). Номинальная стоимость монеты, металл, из которого она изготовлена, форма - это атрибуты класса. Совокупность атрибутов и их значений характеризует объект. Наряду с термином "атрибут" часто используют
термины "свойство" и "поле", которые в объектно-ориентированном программировании являются синонимами.
Все объекты одного и того же класса описываются одинаковыми наборами атрибутов. Однако объединение объектов в классы определяется не наборами атрибутов, а семантикой. Так, например, объекты "конюшня" и "лошадь" могут иметь одинаковые атрибуты: цена и возраст. При этом они могут относиться к одному классу, если рассматриваются в задаче просто как товар, либо к разным классам, если в рамках поставленной задачи будут использоваться по-разному, т.е. над ними будут совершаться различные действия.
Объединение объектов в классы позволяет рассмотреть задачу в более общей постановке. Класс имеет имя (например, "лошадь"), которое относится ко всем объектам этого класса. Кроме того, в классе вводятся имена атрибутов, которые определены для объектов. В этом смысле описание класса аналогично описанию типа структуры или записи (record), широко применяющихся в процедурном программировании; при этом каждый объект имеет тот же смысл, что и экземпляр структуры (переменная или константа соответствующего типа).
Формально класс - это шаблон поведения объектов определенного типа с заданными параметрами, определяющими состояние. Все экземпляры одного класса (объекты, порожденные от одного класса) имеют один и тот же набор свойств и общее поведение, то есть одинаково реагируют на одинаковые сообщения.
В соответствии с UML (Unified Modelling Language - унифицированный язык моделирования), класс имеет следующее графическое представление
Класс изображается в виде прямоугольника, состоящего из трех частей. В верхней части помещается название класса, в средней - свойства объектов класса, в нижней - действия, которые можно выполнять с объектами данного класса (методы).
Объектно-ориентированное программирование (ООП) - это методология программирования, опирающаяся на три базовых принципа:
●инкапсуляцию;
●наследование;
●полиморфизм.
Можно легко убедиться, что язык Java является полностью объектно-ориентированным языком программирования в котором реализованы вышеприведенные основные принципы.
Построение программ, основанных на ООП, принципиально отличается от более ранней методики процедурного программирования, в которой основой построения программы служили
подпрограммы.
Программа – это набор инструкций процессору и данных, объединённых в единую функционально законченную последовательность, позволяющую выполнять какую-нибудь конкретную деятельность.
Подпрограмма – это набор инструкций и данных, объединённых в относительно самостоятельную последовательность, позволяющую выполнять какую-нибудь конкретную деятельность внутри программы. При этом подпрограмма не может работать самостоятельно - она запускается из программы, и может получать из неё данные или передавать их в программу.
Программы, написанные в соответствии с принципами процедурного программирования, состоят из набора подпрограмм, причём для решения конкретной задачи программист явно указывает на каждом шагу, что делать и как делать. Эти программы практически полностью состоят из решения конкретных задач.
Программы, написанные в соответствии с принципами ООП, построены несколько иначе. В них основное время занимает продумывание и описание того, как устроены классы. Как правило прототипом, для большинства объектов, реализуемых в программах на ООП выступают реально существующие объекты, которые окружают человека. Но для решения задач обычно требуется реализация только лишь некоторых свойств и типов поведения объекта. Используя метод абстрагирования необходимо выделить (и далее определить) атрибуты и методы создаваемого объекта, которые необходимы для решения задачи. Под абастрагированием будем понимать процесс поиска и определения абстракций. Абстракция выделяет существенные свойства объекта отличающего его от всех других видов объектов, и таким образом четко определяет его концептуальные границы с точки зрения наблюдателя. В ООП процесс выделения абстракций является крайне трудоемким и очень важным. Выбор правильного набора абстракций для заданной предметной области представляет собой главную задачу объектно-ориентированного программирования. В процессе поиска абстракций необходимо стремиться к минимальному уровню избыточности программного кода. Для решения этой задачи
рекомендуется реализовть повторное использование программного кода на основе принципа наследования. Опытные программисты советуют тщательно изучать библиотеки программного кода с описаниями классов Java, которые являются хорошими примерами и образцами для начинающих программистов. Именно благодаря повторному эффективному использованию прогроаммного кода объектное программирование приобрело огромную популярность. При необходимости решения сходных задач можно использовать уже готовый код, модифицировав только ту часть программы, которая относится к решению конкретной задачи.
Инкапсуляция
Инкапсуляция это процесс отделения друг от друга элементов объекта, определяющих его внутреннее устройство и поведение от внешнего (интерфейса); говорят инкапсуляция скрывает реализациию класса. Поэтому понятия инкапсуляции и сокрытия информации можно считать синонимами при рассмотрении в Java. С целью реализации принципа инкапсуляции, каждый класс должен иметь все необходимые методы для обработки имеющихся данных. Инкапсуляция связывает в класс аттрибуты с методами которые имеют отношения к их обработке. Можно привести наглядное сравнение по которому класс сравнивается с яйцом. Тогда скарлупа — это общедоступные операции, белок — операции класса, желток — атрибуты класса.
Инкапсуляцию можно проилюстрировать на примере взаимодействия двух обьектов на рис. 1.
Рис. 1. Взаимодействие обьектов и инкапсуляция.
В соответствии с принципом инкапсуляции объект не должен получать прямой доступ к атрибутам другого объекта. Чтобы получить доступ к атрибутам класса необходимо обратиться к общедоступным public методам этого класса. А уже те могут получить доступ, по необходимости к другим методам этого класса или напрямую к атрибутам этого класса. При использовании объектно-
ориентированного подхода не принято применять прямой доступ к свойствам какого-либо класса из методов других классов. Для доступа к свойствам класса принято задействовать специальные методы этого класса для получения и изменения его свойств.
Внутри объекта данные и методы могут обладать различной степенью открытости (или доступности). Степени доступности, принятые в языке Java, будут расмотрены в следующем параграфе. Они позволяют более точно управлять поведением обьекта с использованием принципа инкапсуляции.
Открытые члены класса составляют внешний интерфейс объекта. Это та функциональность, которая доступна другим классам. Закрытыми обычно объявляются все свойства класса, а также вспомогательные методы, которые являются деталями реализации и от которых не должны зависеть другие части системы.
Благодаря сокрытию реализации за внешним интерфейсом класса можно менять внутреннюю логику отдельного класса, не меняя код остальных компонентов системы. Это свойство называется модульностью.
Таким образом инкапсуляция приводит к следующему:
1)исключение возможности искажения данных или случайного несанкционированного их изменения;
2)упрощение процесса изменения структуры данных.
Вобъектно-ориентированном программировании “инкапсуляция” означает использование классов, которые в сущности являются некоторыми типами, где кроме данных описаны подпрограммы (действия), позволяющие работать с этими данными, а также выполнять другие действия. Такие подпрограммы, инкапсулированные в класс, называются методами. Поля данных и методы, заданные в классе, называют членами класса (class members).
Все объекты, являющиеся экземплярами некоторого класса, имеют одинаковые наборы полей данных (атрибуты объекта), значения которых могут отличаться для разных объектов класса. Поля данных это переменные, заданные на уровне описания класса, а не при описании метода. В процессе жизни объекта эти значения могут изменяться. Текущие значения полей данных объекта задают его состояние. А методы задают поведение объекта. Причём в общем случае на это поведение влияет состояние объекта – методы пользуются значениями его полей данных.
Наследование
Прогресс в современной разработке программного обеспечения в значительной степени опирается на повторном использовании программного кода. При повторном использовании программного кода ключевую роль играет принцип наследования в ООП. Наследование
позволяет особым образом организовать программный код на основе повторного использования, что позволяет сравнительно быстро разрабатывать сложные программные системы на основе уже созданных классов.
Наследование (inheritance) - это механизм создания нового класса на основе уже существующего используя структуру и поведение (одиночное наследование), или других (множественное наследование) классов. Наследование вводит иерархию "общее-частное", в которой подкласс наследует от одного или нескольких более общих суперклассов. Подклассы обычно дополняют или переопределяют унаследованную структуру и поведение.
Исходный класс называется — базовым или суперклассом (класс предок), а класс потомок — подклассом. От потомков, в свою очередь, можно наследовать, получая очередных потомков. И так далее. Набор классов, связанных отношением наследования, называется иерархией классов. А класс, стоящий во главе иерархии, от которого унаследованы все остальные (прямо или опосредованно), называется базовым классом иерархии. В Java все классы являются потомками класса Object.
Рассмотрим пример фрагмента классификации и иерархии классов на рис. 2.
Рис. 2. Пример фрагмента классификации животных Каждый элемент на рис.2. находится в прямоугольных рамках и
является некоторым классом, который входит в более общий класс животные. Отношение обобщения обозначается сплошной линией с стрелкой на конце. Стрелка указывает на более общий класс — суперкласс. На рисунке изображена иерархия классов, которая подчиняется одиночному насследованию (только один предок).
Множественное наследование изображается точно так же, как одиночное, за исключением того, что линии наследования соединяют класс-потомок сразу с несколькими суперклассами. Не все объектноориентированные языки программирования содержат языковые конструкции для описания множественного наследования. В языке Java
множественное наследование имеет ограниченную поддержку через интерфейсы, которые будут рассмотрены в конце пособия.
Создание иерархии классов подразумевает согласованность уровней при перемещении «сверху-вниз» или наоборот. На верхних уровнях иерархии находятся более абстрактные объекты, на нижних более частные. Если в иерархии появится новый объект то он, как правило, получает все атрибуты и операции своих родителей. Такой подход позволяет работать с большим числом сложных объектов и совершать минимальное количество ошибок.
На практике, часто сразу построить самый общий класс бывает весьма трудно, в силу того что процесс абстрагирования является крайне трудоемким. Поэтому реализуют наследование «снизу-вверх». После некоторого времени использования классов и накопления опыта выделяют наиболее абстрактны класс и при обучении уже, используют наследование «сверху-вниз».
Необходимо помнить, что внесение изменений в базовые классы ведут к соответствующим изменениям в подклассах, что может привести на практике к нежелательному или некоректному поведению подклассов. Поэтому рекомендуется тщательно подготавливать базовые классы, состовлять грамотно документацию и вносить своевременно изменения, изучать базовые классы Java.
Впроцессе определениея классов необходимо учитывать, что существуют не только объекты но и абстракции. Например на рис.2 животное объектом реального мира не является, а воробей — является объектом. Поэтому для того, чтобы избежать создание экземпляров класса, который является абстракцией определяют его как абстрактный класс.
Абстрактный класс — это класс, методы которого не имеют реализации. Абстрактный класс включает то общее, что характерно целому набору классов. Применение абстрактных классов позволяет лучше понять взаимосвязи между существующими классами.
Вкачестве примера того, как строится иерархия (рис. 20), рассмотрим простой пример иерархии фигур, выводимых на экране ЭВМ. Базовым классом в нашем случае является Shape, от которого
наследуются классы: Dot – “точка”, Triangle – “треугольник” и Square – “квадрат”. От класса Dot наследуется класс Circle – “окружность”, а от Circle - Ellipse (эллипс). И, наконец, от Square будем наследовать Rectangle (прямоугольник).
Рис. 20. Иерархия классов фигур
Чем ближе к основанию иерархии лежит класс, тем более общим и универсальным он является. И одновременно – более простым. В нашем случае в качестве базового класса используется класс Shape (фигура). Как правило, базовый класс имеет имя, которое характеризует все объекты экземпляры классов-наследников, и которое выражает наиболее общую абстракцию, применимую к таким объектам.
Каждая фигура в качестве данных содержит x и y – координаты фигуры на экране. Так как класс Dot является наследником Shape, поэтому он будет иметь поля данных x и y, наследуемые от Shape. Тогда в классе Dot декларировать эти поля не надо, нужно их только инициализировать. В классе Dot появляется новое поле Color, характеризующее цвет точки. От Dot мы наследуем класс Circle (окружность), поэтому там также имеются поля x и y, наследуемые от Shape и поле Color, от класса Dot. Отметим, что дополнительно в классе Circle появляется поле, соответствующее величине радиуса (r). Кроме того, для окружности возможна операция изменения радиуса, поэтому необходимо ввести новый метод, обеспечивающий это действие – назовём его setSize() (установить размер). Класс Ellipse имеет те же поля данных и обеспечивает то же поведение, что и Circle, но в этом классе появляется дополнительное поле данных r2 – длина второй полуоси эллипса, и возможность регулировать значение этого поля.
Более специальный класс (потомок) класс, вообще говоря, должен быть устроен более сложно по сравнению с родительским. У него должны иметься дополнительные поля данных и/или дополнительные методы. Иногда встречаются ситуации, когда потомок отличается от класса предка только своим поведением. У него не добавляется новых полей или методов, а только переопределяется часть методов (возможно, только один). Отметим, что поля или методы, имеющиеся в предке, не могут отсутствовать в наследнике – они наследуются от предка. Даже если доступ к ним в родительском классе закрыт (так бывает в случае, когда поле или метод объявлены с модификатором видимости private – приватный (закрытый). В случае, когда класс
предок имеет один или несколько закрытых полей, и класс потомок их наследует, доступ к этим полям напрямую будет невозможен ни в каких наследниках. Если в течении работы программы доступ к таким полям должен быть организован, необходимо предварительно описать set и get методы, о которых будет рассказано немного позже.
Достаточно часто требуется совмещать в объекте поведение, характерное для двух или более независимых иерархий. Одиночное (унарное наследование), при котором у класса может быть только один непосредственный предок, не обеспечивает такой возможности. При унарном наследовании нельзя ввести переменную, которая бы могла ссылаться на экземпляры из разных иерархий, так как она должна иметь тип, совместимый с базовыми классами этих иерархий.
В C++ для решения подобных проблем используется множественное наследование, которое означает, что у класса может быть не только один непосредственный предок, а два или более. В этом случае проблема совместимости с классами из разных иерархий решается путём создания класса, наследующего от необходимого числа классов-предков.
При множественном наследовании классов возникает ряд трудно разрешимых проблем, поэтому в Java оно не поддерживается. Основные причины, по которым произошёл отказ от использования множественного наследования классов — это наследование ненужных полей и методов, конфликты совпадающих имён из разных ветвей наследования. Для решения подобных проблем в Java реализуется механизм интерфейсов.
Интерфейсы являются специальной разновидностью полностью абстрактных классов. То есть таких классов, в которых вообще нет реализованных методов - все методы абстрактные. Полей данных в них также нет, но можно задавать константы. Класс в Java должен быть наследником одного класса-родителя, и может быть наследником произвольного числа интерфейсов. Сами интерфейсы также могут наследоваться от интерфейсов, причём также с разрешением на множественное наследование.
Таким образом любой класс может стать наследником сразу нескольких интерфейсов и наследовать методы и константы из нескольких различных иерархий.
С практическим примером наследования с использованием интерфейсов мы познакомимся позже, в разделе «Обработка событий», где классы, которые должны будут обрабатывать события компонентов графического интерфейса пользователя.
Полиморфизм
Полиморфизм является фундаментальным понятием которым обозначают механизм использования одного и того же имени
(переменной или метода) в классах, которые по разному реализуются (имеют различные действия). Следовательно, любой объект, обозначаемый полиморфным именем, будет по-своему реагировать в зависимости от конткста. На практике встречаются случаи когда обьект должен иметь одно и тоже имя метода но их реализация отличается. Например «метод включить в сеть» используется для различных бытовых приборов при включении в сеть, но реализация его для каждого прибора различна.
Слово “полиморфизм” в переводе с греческого означает “имеющий несколько форм”. При написании полиморфного кода заранее неизвестно, для объектов именно какого типа он будет выполняться. Один и тот же метод будет исполняться по-разному в зависимости от типа объекта для которого он вызывается.
Пусть, например, у нас имеется класс Shape, и в нём заданы методы show() – показать Shape на экране, и и hide() - скрыть её. Тогда для переменной rectangle типа Shape вызовы rectangle.show() и rectangle.hide() будут показывать или скрывать объект, на который ссылается эта переменная. Причём сам объект “знает”, как себя показывать или скрывать, а код пишется в общем расчете на поддержку выполнения указанных действий.
Пример реализации полиморфизма в Java. Имеется абстрактный класс учеников Pupils
abstract class Pupils { abstract void homework();
}
В этом классе описан один абстрактный метод homework() (домашнее задание), переопределим его в подклассах и используем в классе Output (вывод)
class TenthGrade extends Pupils { void homework() {
time=2,5;
kolur=6;
ekz=1;
sec=0;
System.out.println(“Домашнее задание для десятого класса”);
}
}
class NinthGrade extends Pupils { void homework() {
time=2;
kolur=5;
ekz=0;
sec=1;
System.out.println(“Домашнее задание для девятого класса”);
}
}
class EighthGrade extends Pupils { void homework() {
time=1,5;
kolur=4;
ekz=0;
sec=1;
System.out.println(“Домашнее задание для восьмого класса”);
}
}
public class Out {
public static void main (String[] args) { Pupils[] grade = new Pupils[3]; grade[0] = new TenthGrade();
grade[1] = new NinthGrade(); grade[2] = new EighthGrade();
for (int i = 0; i<grade.length; i++) grade[i].homework();
}
}
Таким образом все зависит от определения массива объектов grade[], хотя этот массив ссылок и имеет один тип Pupils, каждый из 3- х его элементов ссылается на объект своего типа TenthGrade(),
NinthGrade() и EighthGrade(). При этом вызывается метод homework()
конкретного объекта, а не класса, которым обределялось имя ссылки. Таким способом в Java реализуется полиморфизм - в данном примере многоформенность «учеников», точнее то, что класс Pupils может
принять форму TenthGrade(), NinthGrade() или EighthGrade().
Необходимо отметить, что в процедурном программировании тоже существует понятие полиморфизма, которое отличается от рассмотренного механизма. Процедурный полиморфизм предполагает возможность создания нескольких процедур или функций с одним и тем же именем, но разным количеством или различными типами передаваемых параметров. Такие функции имеющие одинаковые имена называются перегруженными, а само явление - перегрузкой (overloading). Перегрузка функций имеется и в ООП и называется
перегрузкой методов.
Модульность
Согласно синтаксису языка Java каждый класс должен составлять отдельный модуль. В некоторых учебных пособиях говорят о принципе модульности.
В Java инкапсуляция т.е. скрытие информации о внутренней структуре класса от внешнего воздействия осуществляется при помощи модификатора private, используемого при описании какого-либо члена класса (метода или поля). В этом случае к данным полям и методам доступ иметь будут только экземпляры этого класса, например:
class Pet{
private boolean mouseCatching; private boolean hunting;
}
Таким образом, создавая экземпляр класса Pet, тем самым задаем некоторое домашнее животное, в случае кошки, необходимо дополнительно переопределить поля mouseCatching (ловля мышей) и hunting (охота), присваивая первому true (истина), а второму false (ложь). Тогда экземпляры других классов не смогут изменить эти параметры, например, не заставят собаку ловить мышей, а кошку охотиться.
Каждый класс также может иметь специальные методы, которые автоматически вызываются при создании и уничтожении объектов этого класса:
•конструктор (constructor) - выполняется при создании объектов;
•деструктор (destructor) - выполняется при уничтожении объектов. Обычно конструктор и деструктор имеют специальный синтаксис, который может отличаться от синтаксиса, используемого для написания обычных методов класса.
Типы отношений между классами
Как правило, любая программа, написанная на объектноориентированном языке, представляет собой некоторый набор связанных между собой классов. Можно провести аналогию между написанием программы и строительством дома. Подобно тому, как стена складывается из кирпичей, компьютерная программа с использованием ООП строится из классов. Причем эти классы должны иметь представление друг о друге, для того чтобы сообща выполнять поставленную задачу.
Возможны следующие связи между классами в рамках объектной модели (приводятся лишь наиболее простые и часто используемые виды связей, подробное их рассмотрение выходит за рамки этой ознакомительной лекции):
•агрегация (Aggregation);
•ассоциация (Association);
•наследование (Inheritance);
•метаклассы (Metaclass).
Агрегация
Отношение между классами типа "содержит" (contain) или "состоит из" называется агрегацией, или включением. Например, если аквариум наполнен водой и в нем плавают рыбки, то можно сказать, что аквариум агрегирует в себе воду и рыбок.
Такое отношение включения, или агрегации (aggregation), изображается линией с ромбиком на стороне того класса, который выступает в качестве владельца, или контейнера. Необязательное название отношения записывается посередине линии.
В нашем примере отношение contain является двунаправленным. Объект класса Aquarium содержит несколько объектов Fish. В то же время каждая рыбка "знает", в каком именно аквариуме она живет. Каждый класс имеет свою роль в агрегации, которая указывает, какое место занимает класс в данном отношении. Имя роли не является обязательным элементом обозначений и может отсутствовать на диаграмме. В примере можно видеть роль home класса Aquarium (аквариум является домом для рыбок), а также роль inhabitants класса Fish (рыбки являются обитателями аквариума). Название роли обычно совпадает с названием соответствующего поля в классе. Изображение такого поля на диаграмме излишне, если уже указано имя роли. Т.е. в данном случае класс Aquarium будет иметь свойство (поле) inhabitants, а класс Fish - свойство home.
Число объектов, участвующих в отношении, записывается рядом с именем роли. Запись "0..n" означает "от нуля до бесконечности". Приняты также обозначения:
•"1..n" - от единицы до бесконечности;
•"0" - ноль;
•"1" - один;
•"n" - фиксированное количество;
•"0..1" - ноль или один
Код, описывающий рассмотренную модель и явление агрегации, может выглядеть, например, следующим образом:
// определение класса Fish public class Fish {
//определения поля home
//(ссылка на объект Aquarium) private Aquarium home;
public Fish() {
}
}
// определение класса Aquarium public class Aquarium {
// определения поля inhabitants
// (массив ссылок на объекты Fish) private Fish inhabitants[];
public Aquarium() {
}
}
Ассоциация
Если объекты одного класса ссылаются на один или более объектов другого класса, но ни в ту, ни в другую сторону отношение между объектами не носит характера "владения", или контейнеризации, такое отношение называют ассоциацией (association). Отношение ассоциации изображается так же, как и отношение агрегации, но линия, связывающая классы,- простая, без ромбика.
Вкачестве примера можно рассмотреть программиста и его компьютер. Между этими двумя объектами нет агрегации, но существует четкая взаимосвязь. Так, всегда можно установить, за какими компьютерами работает какой-либо программист, а также какие люди пользуются отдельно взятым компьютером. В рассмотренном примере имеет место ассоциация "многие-ко-многим".
Вданном случае между экземплярами классов Programmer и Computer в обе стороны используется отношение "0..n", т.к. программист, в принципе, может не работать с компьютером (если он теоретик или на пенсии). В свою очередь, компьютер может никем не использоваться (если он новый и еще не установлен).
Код, соответствующий рассмотренному примеру, будет, например, следующим:
public class Programmer { private Computer computers[]; public Programmer() {
}
}
public class Computer {
private Programmer programmers[]; public Computer() {
}
}
Достоинства ООП
Чем больше и сложнее программная система, тем важнее учащемуся правильно разбить ее на небольшие, по возможности независимые части. Чтобы справиться со сложностью, необходимо абстрагироваться от деталей. В этом смысле классы представляют собой весьма удобный инструмент.
Классы позволяют проводить конструирование из полезных компонентов, обладающих простыми инструментами, что позволяет абстрагироваться от деталей реализации.
Данные и операции над ними образуют определенную сущность, и они не копируются по всей программе, как нередко бывает в случае процедурного программирования, а описываются в одном месте. Локализация кода и данных улучшает наглядность и удобство сопровождения программного обеспечения.
Инкапсуляция позволяет привнести свойство модульности, что облегчает распараллеливание выполнения задачи между несколькими исполнителями и обновление версий отдельных компонентов.
ООП дает возможность создавать расширяемые системы. Это одно из основных достоинств ООП, и именно оно отличает данный подход от традиционных методов программирования. Расширяемость означает, что существующую систему можно заставить работать с новыми компонентами, причем без внесения в нее каких-либо изменений. Компоненты могут быть добавлены на этапе исполнения программы.
Полиморфизм оказывается полезным преимущественно в следующих ситуациях.
Обработка разнородных структур данных. Программы могут работать, не различая вида объектов, что существенно упрощает код. Новые виды могут быть добавлены в любой момент.
Изменение поведения во время исполнения. На этапе исполнения один объект может быть заменен другим, что позволяет легко, без изменения кода, адаптировать алгоритм в зависимости от того, какой используется объект.
Реализация работы с наследниками. Алгоритмы можно обобщить настолько, что они уже смогут работать более чем с одним видом объектов.
Создание "каркаса" (framework). Независимые от приложения части предметной области могут быть реализованы в виде набора универсальных классов, или каркаса (framework), и в дальнейшем расширены за счет добавления частей, специфичных для конкретного приложения.
Недостатки ООП
Документирование классов - задача более трудная, чем это было в случае процедур и модулей. Поскольку любой метод может быть
переопределен, в документации должно говориться не только о том, что делает данный метод, но и о том, в каком контексте он вызывается. Ведь переопределенные методы обычно вызываются не клиентом, а самим каркасом. Таким образом, программист должен знать, какие условия выполняются, когда вызывается данный метод. Для абстрактных методов, которые пусты, в документации должно говориться о том, для каких целей предполагается использовать переопределяемый метод.
Всложных иерархиях классов поля и методы обычно наследуются
сразных уровней. И не всегда легко определить, какие поля и методы фактически относятся к данному классу. Для получения такой информации нужны специальные инструменты, вроде навигаторов классов. Если конкретный класс расширяется, то каждый метод обычно сокращают перед передачей сообщения базовому классу. Реализация операции, таким образом, рассредотачивается по нескольким классам, и чтобы понять, как она работает, нам приходится внимательно просматривать весь код.
Методы, как правило, короче процедур, поскольку они осуществляют только одну операцию над данными, зато их намного больше. В коротких методах легче разобраться, но они неудобны тем, что код для обработки сообщения иногда "размазан" по многим маленьким методам.
Инкапсуляцией данных не следует злоупотреблять. Чем больше логики и данных скрыто в недрах класса, тем сложнее его расширять. Отправной точкой здесь должно быть не то, что клиентам не разрешается знать о тех или иных данных, а то, что клиентам для работы с классом этих данных знать не требуется.
