В номере МК №15 (238) была опубликована моя статья «ООП-ля!», где я в популярной форме постарался изложить основные концепции объектно-ориентированного программирования (ООП). Даже самые сложные вещи можно объяснить доступно и понятно, когда говоришь простыми словами и при этом знаешь, что говоришь.
Признаться, я был удивлен, прочитав в МК №41 (264) статью Сергея Рогаткина «Снова ООП-ля!!!», суть которой сводилась к критике моего материала и изложению тех же основ ООП, но в несколько иной форме. Критика Рогаткина, на мой взгляд, совершенно не имеет оснований с технической точки зрения.
Так, Рогаткин пишет: «На сцену вышло структурное программирование (Петр Семилетов в своей статье назвал его «процедурным», что терминологически неверно)». Предлагаю обратиться к классике Бьярн Страустрап (создатель C++) со своей капитальной книгой «Язык программирования С++». В книге есть раздел «Парадигмы программирования», где противопоставляются парадигма ООП и процедурная парадигма. О том же я и написал в своей статье. Выходит, моя терминология совпадает с Бьярном Страустрапом. Но по мнению Сергея Рогаткина, это терминологически неверно. Тысячи людей оперируют этими двумя терминами именно в этом контексте. Но РОГАТКИН выводит в противовес ООП иной предмет «структурное программирование», и замечает, что ООП является частным случаем процедурного программирования. Я не буду комментировать это заявление. Я придерживаюсь общепринятой трактовки терминов, то есть заодно со Страустрапом.
Далее РОГАТКИН обращается к более конкретной цели разбору приведенного мною примера. Напомню, что для демонстрации читателям идеи полиморфизма и наследования я рассказал о движке моей работающей программы интернет-звонилки. Суть сводилась к тому, что у нас был абстрактный класс CAStats и его потомки CWin9xStats и CW2KStats, каждый из которых наследовал свойства CAStats, но по-разному реализовывал метод GetStats, служащий для сбора и обработки данных о статистике соединения с провайдером (в разных версиях Windows этот механизм различается).
Я объявлял переменную stats как переменную типа CAStats, затем проверял версию системы и в зависимости от нее создавал stats как экземпляр класса CWin9xStats (для Windows 9x) либо CW2KStats (для Windows 2000/XP). Но Рогаткин говорит: «Третье замечание состоит в том, что в его примере переменная stats НЕ может становиться экземпляром классов CWin9xStats или CW2KStats, как он об этом пишет».
Давайте напишем простой пример на Delphi. Сначала объявим три класса общего предка А и его потомков B и C:
Объявим теперь экземпляр класса A:
теперь проверим, какой тип будет иметь instance, если его создать как экземпляр класса-потомка B:
В instance.ClassName содержится имя типа класса красноречивый ответ: B. Более того, при проверке вида instance is B мы получим TRUE, то бишь истину. Итак, можем ли мы сказать, что instance СТАЛ экземпляром класса B? Вполне. Аналогично и с переменной stats.
Но Рогаткин отмечает следующее: «Наоборот, переменная stats позволяет получить доступ к классу CAStats (исключительно к членам, определенным в CAStats), являющемуся частью классов потомков CWin9xStats и CW2KStats.».
То есть, автор имеет в виду, что если переменная объявлена как переменная базового типа (родителя), то возможен доступ только к тем членам, которые объявлены в родителе. На самом деле это не так.
Немного усложним наш пример.
Итак, в классе B мы реализовали новую функцию GetHello, которая возвращает нам строку Hello. Очевидно, что в родительском для B классе A этой функции нет. Мы создаем instance, который имеет тип A, как экземпляр класса B:
Затем мы проверяем, является ли instance переменной класса B, и в случае успешной проверки (а иначе и быть не может) выводим в заголовок окна результат, возвращаемый функцией GetHello:
По Рогаткину, это невозможно. Тем не менее, работает :-).
Мы всего лишь воспользовались стандартной операцией «приведения типа» (instance as B) и получили доступ к методу класса B, т.к. класс A знать не знал о функции GetHello. Факт говорит сам за себя в заголовок формы выведено «Hello».
Надеюсь, эти объяснения помогли вам разобраться в истине. Мне же хочется сделать еще несколько замечаний по материалу Сергея Рогаткина, не касаясь более критики. В его статье есть фраза: «В абстрактном классе определен только интерфейс, а методы такого класса не содержат программного кода, т.е. не имеют реализации, почему часто называются абстрактными методами. [...] Абстрактный класс это не шаблон объекта (во время выполнения программы не может быть создан экземпляр такого объекта)».
На практике абстрактный класс часто содержит в себе элементы реализации например, функции вычисления каких-нибудь величин или базовый набор неких инструкций в конструкторе и деструкторе. Чисто абстрактные классы хороши в теоретических выкладках, а в реальной жизни проще создать довольно-таки функционального общего предка, а потом «уточнять» его возможности потомками.
Экземпляр абстрактного класса МОЖЕТ быть создан, хотя это довольно бесполезная, а в некоторых случаях рискованная (в плане стабильности выполнения программы) затея. Некоторые компиляторы вас об этом предупредят некими тревожными сообщениями.
Остается подвести итоги. Все, что я пишу о программировании результат исследования опробованных НА ПРАКТИКЕ технологий, будь то XML, ООП или скрипты для NSIS. Все это реально работает в моих реальных проектах (см. сайт) и было бы удивительно, пиши я об этом без знания дела и используя ошибочную терминологию. Я не Линус Торвальдс, но уровень моих разработок существенно отличается от алгоритма вычисления факториала. Занимаясь созданием софта, активно использующего возможности ООП, я могу свободно и технически корректно рассуждать об этой замечательной парадигме программирования.