Рабочая программа по дисциплине Цели и задачи освоения дисциплины Дисциплина Объектноориентированный анализ и программирование
Скачать 339.98 Kb.
|
public abstract class Parent { protected int state; public void print() { System.out.printin("This string was printed from class Parent"); System.out.flush(); public abstract boolean isAbstract(); public class Child extends Parent { public Child(int state) { this.state = state; public void print() { System.out.printin("This string was printed from class Child"); System.out.flush(); public boolean isAbstract() { System.out.printin("This class is not abstract"); System.out.printin("His state is equal " + state); System.out.flush() ; return false; В представленном примере класс Parent имеет одно поле, видимое для всех классов-потомков, и два метода. Первый метод выводит сообщение, ин(|юрмируюшее о том, что обращение выполнялось к экземпляру класса Parent. Второй метод является абстрактным и должен быть переопределен в классах-потомках. Класс Child наследует от класса Parent. Публичный конструктор Childtint state) инициализирует унаследованное поле. Потомок реализует абстрактный метод isAbstract и переопределяет необходимым образом метод print. Ромбическое наследование Одной из важных идей объектно-ориентированного программирования является множественное наследование — ситуация при которой класс-потомок одновременно является наследником нескольких родительских классов. Практически все языки программирования так или иначе реализуют эту идею: C++. Eiffel, Python, Javascript, Perl, Lisp. Delphi и так далее. Однако разработчики Java предпочли отказаться от поддержки множественного наследования. Дело в том, что при определенной иерархической структуре приложения наследование от нескольких классов чревато конфликтами. Такая ситуация получила название ромбического наследования. Вкратце она может быть описана следующим образом. Пусть имеется класс А, содержащим метод method(). Пусть класс А имеет двух наследников В и С, каждый из которых переопределяет methodt). Класс D наследует от классов В и С одновременно. Конфликт возникает в том случае, когда через экземпляр класса D происходит вызов метода methodt), так как компилятор не знает к какой конкретно реализации methodt) обращаться. Отметим также, что распространенным является мнение, согласно которому необходимость множественного наследования всегда связана с ошибками проектирования и анализа. Взамен механизма множественного наследования Java предлагает гибкий и мощный механизм интерфейсов. Напомним, что под интерфейсом в объектно-ориентированном программировании принято понимать семантическую и синтаксическую конструкцию в коде программы, используемую для специфицирования услуг, предоставляемых классом. Объявление интерфейса в Java схоже с объявлением класса. Любой интерфейс может содержать произвольное число полей и методов. При этом все поля автоматически получают модификаторы public final static, а методы — public abstract. Так как поля среди прочих имеют модификатор final, то потребуется их сразу же проинициализировать. Класс, реализующий интерфейс, получает доступ ко всем его полям и должен содержать описание всех методов. Для связывания класса и интерфейса используется ключевое слово implements. Разрешается реализовывать несколько интерфейсов. Значительная гибкость механизма интерфейсов в Java достигается за счет множественного интерфейсного наследования, то есть интерфейс может являться наследником произвольного числа других интерфейсов. Для указания родительских элементов применяется следующая форма. interface <имя интерфейса> extends <родительские интерфейсы> Здесь интерфейс является наследником родительских интерфейсов (допускается перечисление нескольких родителей через запятую). Важно помнить, что при множественном интерфейсном наследовании не должно образовываться циклических зависимостей. Кроме того, наличие среди членов родительских элементов методов с одинаковой сигнатурой, но разными возвращаемыми значениями также приведет к ошибке (в случае полного совпадения сигнатур потребуется всего лишь одна реализация такого метода). Рассмотрим пример. public abstract class People { protected int age; protected String name; public People(String name) { this .name = name; public abstract String getStatusO; public class Student extends People { private String university; public Student(String name, String university) { super(name); this.university = university; public String getStatusO { return "I'am a student of " + this.university; public interface WorkingWithDate { int getAge(); void setAge(int age); public interface PersonalCard extends WorkingWithDate, YearsOfStuding { String getUniversity(); public class StudentID implements PersonalCard { private Student owner; public StudentID(Student owner) { this.owner = owner; return owner.age; public void setAge(int age) { owner.age = age; public String getUniversity() { return owner.getStatus(); В данном примере класс StudentID представляет собой абстракцию индивидуальной электронной студенческой карты, внедряемой в некоторых университетах (упрощенный вариант студенческого билета). Класс реализует интерфейс PersonalCard, который содержит метод для получения строки с названием места обучения студента и, будучи наследником интерфейсов WorkingWithDale и YearsOfStuding, включает в себя все методы и поля родительских элементов. Одним из членов StudentID является ссылка на экземпляр класса Student — владельца студенческого билета. Класс Student является наследником абстрактного класса People и, помимо общечеловеческих свойств (таких как имя и возраст), содержит поле с информацией <ж> университете, в котором обучается студент. Данные владельца карты используются при реализации методов интер(|х:йса PersonalCard. Таким образом, представленный пример содержит две вертикальные иерархии (классов и интерфейсов), объединение которых происходит в классе StudentID. Совместное использование абстрактных классов и интерфейсов помогает в создании простых по своей внутренней структуре иерархий. Сериализация Сериализацией называется процесс перевода какой-либо структуры данных в последовательность битов. Данная операция значительно упрощает решение задачи сохранения экземпляра класса. Сериализованный объект может быть сохранен на жесткий диск или, например, передан по сети. Другими словами, сериализация позволяет работать с объектом как с потоком данных. Обратный к сериализации процесс (получение экземпляра класса из потока битов) получил название десериализации. Java позволяет сериализовывать и десериализовывать любые объекты двумя способами. Первый заключается во взаимодействии с интерфейсом Serializable (стандартная сериализация), второй — с интерфейсом Extemalizable (расширенная сериализация). Остановимся подробнее на первом способе. Для того чтобы объект мог быть превращен в поток битов необходимо, чтобы класс, от которого порожден объект, и все его подклассы реализовывали интерфейс Serializable. Данный интерфейс не содержит никаких методов и является своеобразным маркером для виртуальной машины, подсказывающим ей, что экземпляры класса могут быть сериализованы. Модификатор transient позволяет отмечать поля, сохранение которых не требуется. Также не сохраняются поля с модификатором static. Для непосредственного преобразования объект в поток данных необходимо использовать специализированные средства ввода-вывода. Поясним вышеизложенное на примере. public class TreeNode implements Serializable { //. . . public class LTLTree implements Serializable { public int rootID; public TreeSet public String formula; } public class TreeSerializator { public static void main(String . args) { String fileName = "ltltree.txt"; try { ObjectOutputStream oos new ObjectOutputStream(new FiieOutputStream(fileName)); oos.writeObject(currentTree); catch (FileNotFoundException err) { System.out.printin("Some bad thing with file"); err.printStackTrace(); catch (lOException err) { System.out.printin("Exception while writing"); err.printStackTrace(); LTLTree tempTree null; try { ObjectlnputStream ois new ObjectlnputStream(new FilelnputStream(fileName)); tempTree = (LTLTree)ois.readobject(); catch (FileNotFoundException err) { System.out.printin("Some bad thing with file"); err.printStackTrace(); catch (lOException err) { System.out.printin("Exception while writing"); err.printStackTrace(); catch (ClassNotFoundException err) { System.out.printin("Some bad with class-file LTLTree"); err.printStackTrace(); Сериализация представляет собой чрезвычайно гибкий механизм, позволяющий выполнять сохранение и последующее восстановление экземпляров сущности. Без сериализации выполнение таких рутинных операций как передача состояния объектов но сети, вызов RMI и так далее требовало бы гораздо больших затрат времени программиста. Контрольные вопросы по Теме 3 В каких случаях целесообразно использовать статический импорт? В чем необходимость использования спецификаторов видимости? В чем заключается проблема множественного наследования? Что такое абстрактный класс? Может ли абстрактный класс иметь поле с модификатором final? Объясните свой ответ. Поддерживает ли Java множественное наследование? Объясните свой ответ. Какие ошибки были допущены при проектировании абстракции студенческого билета? Какие два способа сериализации и десериализации объектов вы знаете? Как работает каждый из этих способов? Могут ли они «смешиваться» (сериализация с помощью одного способа, десериализация — с помощью другого). Возможно ли изменять значения полей с модификатором final? Объясните свой ответ. Для решения каких задач используются ключевые слова this и super? Выводы после Темы 3 Java — мощный и современный объектно-ориентированный язык программирования. Программы, написанные на этом языке программирования, обладают рядом полезных свойств, среди которых особенно стоит отметить кроссплатформенность и безопасность. Необходимо помнить, что данный язык не всегда подходит для решений, к которым предъявляется требование высокого быстродействия. Тема 4. «Шаблоны проектирования» Замечательным примером выразительной мощности объектно-ориентированной парадигмы программирования являются шаблоны проектирования. Прежде всего они помогают продемонстрировать насколько просто и элегантно могут быть решены сложные архитектурные задачи. В данной теме будут рассмотрены несколько таких решений. Шаблон как повторяющееся решение Шаблон проектирования — повторимая архитектурная конструкция, представляющая собой решение часто возникающей проблемы проектирования; это описание взаимодействия объектов и классов, адаптированных для решения задачи в конкретном контексте. Именно шаблон и представляет собой тот универсальный «рецепт» объединения классов, позволяющий добиваться от объектов сложного поведения с минимальными затратами на проектирование. Шаблоны принято подразделять на низкоуровневые и высокоуровневые. Низкоуровневые шаблоны также называются идиомами. Они учитывают специфику языка программирования и являются хорошими решениями проектирования, характерными для конкретного языка или платформы. Низкоуровневые шаблоны не являются универсальными. Высокоуровневые шаблоны также называются архитектурными шаблонами. Они охватывают архитектуру всей программной системы и являются универсальными решениями. Основные преимущества шаблонов проектирования заключаются в следующем. Во-первых, каждый шаблон проектирования описывает решение целого ряда абстрактных проблем. Во-вторых, использование шаблонов проектирования помогает унифицировать процесс разработки. Программисты могут вести диалог о системе, ссылаясь на известные шаблоны. В-третьих, шаблон проектирования — это универсальное решение, используемое множество раз. Правильно сформулированный шаблон проектирования позволяет, отыскав удачное решение, пользоваться им снова и снова. При всех вышеперечисленных достоинствах не стоит забывать и о недостатках, присущих шаблонам проектирования. В некоторых случаях их использование приводит к построению громоздкой и малоэффективной архитектуры. Активное использование шаблонов также приводит и к резкому снижению доли творческой работы, повышению доли механической работы. Бездумное или чрезмерное использование «готовых рецептов» прививает плохой стиль разработки. Часто с помощью шаблонов пытаются скрыть недостатки системы (например, отсутствующую документацию). Классическими принято считать четыре группы шаблонов: основные, порождающие, структурные и поведенческие. |