УДК 519.682.2, 004.045
ОШИБКИ НАЧИНАЮЩИХ ПРОГРАММИСТОВ ПРИ ИСПОЛЬЗОВАНИИ ОБЪЕКТНО-ОРИЕНТИРОВАННОГО ПОДХОДА
Штанюк Антон Александрович, кандидат технических наук, доцент Нижегородского государственного университета им. Н.И. Лобачевского [email protected]
Введение
В настоящее время объектно-ориентированный подход (ООП) широко распространен на практике и его изучение является необходимой содержательной компонентой дисциплин, связанных с программированием, как для ВУЗов, так и для курсов повышения квалификации [1,2]. Существует проблема применения теоретических знаний при создании программного обеспечения, вызванная недостаточным пониманием основ ООП и тонкостями его реализации в конкретных языках. В рамках данной работы рассматриваются типичные ошибки начинающих программистов, что может быть полезным при составлении методических указаний к выполнению практических работ по программированию.
Особенности использования термина «ошибка»
Люди, которые пишут код, совершают ошибки. Существуют многочисленные подходы к описанию самого термина «ошибка», поэтому необходимо определить его сущность в рамках данной работы. Мы рассматриваем ошибки двух основных видов. К первому виду можно отнести дефекты, вызванные неправильным использованием синтаксических конструкций и правил построения выражений конкретного языка. Такие ошибки могут быть обнаружены на стадии трансляции или на стадии тестирования рабочей программы (модуля).
Ко второй группе относим к ошибкам все дефекты программного кода, не связанные с логикой работы программы и не влияющие на конечный результат, а следовательно, не определяемые на стадии трансляции и выполнения. Такие ошибки также прямо не влияют на стабильность работы программы, поэтому не выявляются на этапе тестирования методом черного ящика и на этапе эксплуатации. Рассматриваемые ошибки могут выявляться на этапах тестирования методом белого ящика при инспекциях исходного кода, при тестировании архитектуры всей системы или отдельных модулей. Существенно, что такие ошибки непосредственно влияют на возможности сопровождения, поиска других дефектов и на развитие проекта в последующие годы.
Совершенно очевидно, что программные проекты с использованием ООП часто относятся к классу больших систем, развитие которых продолжается много лет, поэтому ошибки на стадиях проектирования могут иметь катастрофические последствия в дальнейшем. Поэтому борьба с подобными ошибками должна иметь принципиальное значение.
При изучении программирования вопросам правильной арихитектуры не всегда уделяется достаточно внимания. Часто упор делается на синтаксические средства, на быстрое достижение результата, так сказать «любой ценой». Многие студенты считают, что критерием правильной программы является именно правильный результат и ничего более. Другим фактором, стимулирующим появление ошибок ООП, является невозможность иллюстрации многих его особенностей в рамках тех небольших учебных проектов, на которых проводится обучение. В результате небольшие и часто изолированные примеры не могут научить правильному использованию компонент ООП и привить «иммунитет» от подобных ошибок [4,5].
Для разработки больших программ необходимы приемы, которые описываются как паттерны программирования. Изучение этих паттернов, составляющих, как правило,
отдельный учебный курс, приступают уже тогда, когда изучен язык программирования и сформировались привычки неправильного применения ООП. В определенном смысле, можно объяснить большинство ошибок при неправильном использовании концепции ООП или объектно-ориентированных языков программирования тем, что разработчики либо не знают о паттернах вообще, либо имеют смутное представление об их использовании.
Несколько простейших языковых ошибок
Рассмотрим несколько типичных ошибок, определяемых на ранних стадиях.
- Неправильное использование конструкций, вызванное особенностями реализации в разных языках.
Типичный пример: когда конструкции try..catch() в языке С++ ошибочно приписывают блок finally, используемый в Java.
Другой пример: для создания абстрактного класса языке С++ необходимо объявить в таком классе абстракный (чисто-виртуальный) метод, в то время как в языке Java можно воспользоваться спецификатором abstract для этой цели.
- Ограниченное использование спецификатора const в С+ +
Использование const для создания ссылок на неизменяемые объекты и определения константых методов в классе предотвращает ошибочные изменения данных в объектах, что способствует повышению надежности всей программы. Начинающие программисты часто не уделяют этому факту должного внимания и не используют данный спецификатор. В языке Java подобным образом может игнорироваться спецификатор final.
Ошибки именования
Ошибки, связанные с неправильными именами классами, методами и объектами.
- Неправильные имена классов
В языке Java имя файла должно совпадать с именем публичного класса, определенного в этом файле. Несоответствие имен приводит к ошибкам компиляции.
Кроме того, в программах с большим количеством классов, неправильные имена могут приводить к путанице при наследовании.
- Неправильные имена методов
Помимо путаницы в использовании, неправильные имена в иерархиях (как и ошибки в сигнатурах методов) часто приводят к ошибкам замещения (override) или перегрузок (overload) методов. Например, если разработчик базового класса при выборе метода допустил синтаксическую ошибку, то разработчик производного класса может этого не заметить и выбрать корректное имя, что и приведет к ошибке.
Частое использование методов, которые не возвращают результаты своей работы, может свидетельствовать о дефектах в проектировании классов (сильной зависимости от состояния объекта) и может потребовать дополнительного комментирования в тексте или документирования.
- Слишком длинные или короткие имена
Начинающие разработчики часто предпочитают давать объектам программы короткие имена, которые в дальнейшем быстро "заканчиваются", при этом создавая известную путаницу в силу своей схематичности. Использование слишком длинных имен, особенно при активной разработке «оберток» (wrappers) к существующим объектам, может приводить к появлению сверхдлинных цепочек имен, затрудняющих работу с кодом.
Пример длинного выражения:
container.add((new JLabel("label text")).setMaximumSize(new Dimension(100,200)));
В языке Java принято собирать классы в пакеты, которые, в свою очередь вкладываются друг в друга. В результате глубоких вложений или при использовании очень длинных имен могут образовываться очень длинные цепочки.
Ошибки архитектуры
- Неоправданное использование ООП там, где оно не требуется.
Объектно-ориентированный подход, требующий рассматривать решение в виде совокупности взаимодействующих объектов, может не соответствовать задаче, для которой больше подходит структурный подход. В качестве примера можно привести программы, состоящие из линейной последовательности элементарных действий, или программы, для которой высшим уровнем абстракции является процедура (функция). Искусственное добавление классов в такую программу приводит к увеличению размера кода и неоправданному усложнению процесса отладки.
- Усложнение модели предметной области, увеличение количества классов и связей
Принцип декомпозиции предметной области, позволяющий выделить базовые
сущности, описать классы и отношения, может применяться так «успешно», что даже в простых задачах схема классов оказывается чрезвычайно сложной и запутанной (рис.1) [3].
Рис. 1 - Усложнение модели предметной области
- Недостаточная декомпозиция, малое количество сущностей
Здесь наблюдается обратная картина: вместо хорошей декомпозиции, разработчик работает с малым количеством сущностей. Классы становятся громоздкими, нарушается принцип повторного использования, поскольку один и тот же функционал начинает дублироваться в разных классах. Возникает такой антипаттерн ООП, как «божественный объект» [5], при котором одному объекту программы соответствует слишком большой функционал.
Нарушение принципов ООП
- Использование большого количества открытых членов класса
Сокрытие данных и кода придумали для их защиты от прямого доступа (в целях безопасности) и для облегчения тестирования и отладки. С другой стороны, этот принцип может стать помехой при написании кода, и начинающие программисты охотно заменяют закрытые и защищенные элементы классов на открытые. Вместо закрытых классов в С++ применяют открытые по умолчанию структуры.
- Злоупотребление «дружбой» классов в С+ +
Ключевое слово friend, принятое в С++ используется для предоставления исключительного доступа одного класса к другому. Если этой возможностью начать злоупотреблять, то принцип сокрытия сойдет на нет и в программе начнется бесконтрольный прямой доступ между классами.
- Нарушение принципа единственной ответственности (SRP)
Если проектировать слишком большие классы с богатым набором функционала, то, скорее всего, будет нарушаться SRP. Основная проблема возникнет при дальнейшей работе над классом, при внесении в него изменений. Кроме того, такой класс будет иметь множество связей и при изменениях требований потребуется вносить много изменений в связанные участки программы.
Неправильное использование наследования
Наиболее сложной стороной ООП для новичков выступают вопросы, связанные с использованием наследования и полиморфизма. Одна из наиболее часто встречающихся ошибок:
- Подмена агрегации (композиции) наследованием
(С) Student
cal сС РА О
( С) Group
с ale С РА () |
Рис. 2 - Подмена агрегации наследованием
Если в классе студент имеется расчет среднего балла и такая же цель определяется для группы, то начинающие связывают эти классы наследованием, полагая, что в таком случае внутри группы удобно вызвать метод Student::calcGPA().
- Игнорирование виртуальных деструкторов
При отсутствии виртуальных деструкторов проблемы будут возникать при освобождении ресурсов, занимаемыми различными объектами в рамках одной иерархии.
- Наследование вместо делегирования
Довольно популярная ошибка, связанная с неправильным подходом к наследованию и имеющая специальное название «антипаттерн BaseBean» [5].
- Использование копий объектов при полиморфизме вместо адресов и ссылок
Очень удобно, например, хранить объекты разных связанных между собой классов в контейнере, который работает с указателями на базовый класс. Благодаря виртуальным функциям выполняется именно тот код, который нужен. Но если помещать в контейнеры копии объектов, то полиморфизм уже работать не будет и в контейнере будут храниться только экземпляры базового класса.
Разное
- Дублирование кода в конструкторах
Если несколько конструкторов должны выполнить идентичный код, то нет смысла дублировать его. В языке С++ можно вынести общий код в приватный метод, а затем в каждом конструкторе поместить вызов этого метода. В языке Java можно воспользоваться вызовом из одного конструктора другого:
public class Server {
private String address; public Server(String uri) { this.address = uri;
}
public Server(URI uri) { this(uri.toString());
}
}
Такой подход помогает решить еще одну проблему: модификацию внутренних (объектных) переменных в теле только одного конструктора.
Мы рассмотрели только несколько самых популярных ошибок начинающих при изучении программирования с использованием ООП. Одной из важнейших задач преподавателя курсов программирования будет осуществление контроля за появлением подобных ошибок и детальное разъяснение последствий их возникновения.
Литература
1. Штанюк А.А. Изучение объектных технологий в рамках курсов повышения квалификации // Международное научное издание Современные фундаментальные и прикладные исследования. 2014 №4 (15), с.20-23.
2. Штанюк А.А. Объектно-ориентированный подход и объектно-ориентированные языки как предмет изучения // Объектные системы. 2010. №2 (2). С. 68-70.
3. Common Pitfalls Developers Make with Object Oriented Programming [Электронный ресурс.] -Режим доступа: http://seguetech.com/common-pitfalls-developers-make-with-object-oriented-programming/ (дата обращения 29.12.2016).
4. S. Hubalovsky, J. Sedivy. Mistakes in object oriented programming // 2nd International Conference ob Information Technology (ICIT), 2010. 28-30 June 2010, Gdansk, Poland. pp 113-116.
5. Laplante, Phillip A.; Neill, Colin J. Antipatterns: Identification, Refactoring and Management. Auerbach Publications, 2005.
УДК 681.3
ПРИНЦИПЫ ПРОЕКТИРОВАНИЯ ANDROID-ПРИЛОЖЕНИЯ ДЛЯ АВТОМАТИЧЕСКОГО ОПРЕДЕЛЕНИЯ ОБЪЕМОВ ПАРТИИ БРЕВЕН
Малков Георгий Валерьевич, Уральский федеральный университет имени первого Президента России Б. Н. Ельцина, Нижнетагильский технологический институт (фил.), Факультет экономики и менеджмента, кафедра информационных технологий, студент, Россия, г. Нижний Тагил,
[email protected] Грегер Сергей Эдуардович, Уральский федеральный университет имени первого Президента России Б. Н. Ельцина, Нижнетагильский технологический институт (фил.), Факультет экономики и менеджмента, кафедра информационных технологий, доцент, Россия, г. Нижний Тагил,
segreger@gmail. com
Мухутдинов Руслан Маисович, Уральский федеральный университет имени первого Президента России Б. Н. Ельцина, Нижнетагильский технологический институт (фил.), Факультет экономики и менеджмента, кафедра информационных технологий, ассистент кафедры ИТ, Россия, г.Нижний Тагил,