Пришло время снова переосмыслить всё то, что касается программирования и моего взгляда на него.
Эта заметка призвана закрепить то немногое, что я повторно присмотрел и переварил об объектно-ориентированном программировании (ООП).
Переосмысление возникло не на пустом месте, а было вызвано недовольством ООП. Меня постоянно что-то подначивало, заставляло сомневаться в ООП, в его месте в моей практике. Собственно, это было тягучее чувство, что ООП как-то совсем не моё и не может являться базой для получение определённого удовлетворения от разработки программ.
Короче, на определённом этапе недовольство ООП достигло критической точки.
Что же это я всё ООП да ООП. У него есть вполне конкретный образ - Си++, если ещё конкретнее, то Qt.
С чем я для себя сравниваю этот образ ООП - с Си, с одной стороны, с Scheme, с другой. Первый - процедурный язык, второй - функциональный. То есть ни первый, ни второй не является объектно-ориентированным языком.
Что объединяет упомянутые Си и схему? Концептуально? Данные и функции в них разделены. Они существуют по отдельности: есть данные и есть функции.
Что в случае ООП? Функции (в его терминах, методы) определены (существуют) для конкретных данных (экземпляра класса/структуры). Методы и данные неотделимы. Да, они могут существовать как-бы отдельно, но тогда это не ООП в том смысле, что за бортом оказываются свойства, которые дают ООП интересные (мягко говоря) возможности.
Что за свойства? 1) Полиморфизм. 2) Инкапсуляция. 3) Наследование. Первое и второе свойства не так интересны как третье, которое является, на мой взгляд, самым главным.
Наследование. Если к наследованию “прибавить” такое качество как неотделимость методов от данных, то получается, что наследование даёт безграничные возможности по расширению свойств некоторой (базовой) сущности (в смысле данных) и определению дополнительных и ПЕРЕОПРЕДЕЛЕНИЮ существующих методов исходной (базовой) сущности.
Переопределение методов. Почему это важно? Думаю вот почему. При разработке чего-то большого на Си (или функциональных языках) появляется необходимость изменения структуры программы таким образом, чтобы некоторые функции принимали в качестве аргумента другую функцию. Такая возможность, вообще-то, существенно помогает, а часто незаменима. В Си это указатель на функцию с известной сигнатурой, а в схеме всё есть функция, и проблем нет по определению.
Что в Си++? Можно изгаляться и пробрасывать внутрь одного класса указатель на функцию (внимание) экземпляра другого класса. Но на каждый чих делать это не захочется.
В Си++ подход другой. Использует этот другой подход механизм наследования: а) определяется базовый класс с некоторым виртуальным методом (сигнатурой); б) определяется другой класс, который определяется как производный от базового класса, внутри которого кодируется реализация виртуального метода; в) в третьем классе создаётся метод, среди аргументов которого будет указатель на экземпляр класса п.а) для того, чтобы вызвать описанный классом п.а) метод.
Какой вывод? В Си++ (вернее в ООП) сложнее. Конкретно на этом примере.
В Qt с этим проще, потому что существует система сигналов/слотов. Как-бы внешний механизм, который регистрирует и обслуживает вызовы определённых методов, закреплённых за конкретными экземплярами объектов. Более того, этот механизм позволяет осуществлять передачу управления другому потоку (вернее, инициировать вызов метода тем потоком, которому принадлежит экземпляр-владелец метода).
То есть в Qt существует очень эффективный способ “передачи указателя на функцию” для связи двух (в простейшем случае) экземпляров разных объектов (классов). И это хорошо.
Теперь нужно ещё раз подумать и сформулировать в каких случаях применять наследование?
Для определения базовых абстрактных классов (сигнатур, прототипов методов), которые будут являться основой для определения производных классов, отличающихся друг от друга поведением (реализациями методов).
Для себя я уяснил следующее: языку свойственны ограничения; их необходимо принять и одновременно забыть об особенностях и ограничениях других языков; способы решения задач средствами языка А отличаются от способов решения тех же задач средствами языка Б.
Эта заметка призвана закрепить то немногое, что я повторно присмотрел и переварил об объектно-ориентированном программировании (ООП).
Переосмысление возникло не на пустом месте, а было вызвано недовольством ООП. Меня постоянно что-то подначивало, заставляло сомневаться в ООП, в его месте в моей практике. Собственно, это было тягучее чувство, что ООП как-то совсем не моё и не может являться базой для получение определённого удовлетворения от разработки программ.
Короче, на определённом этапе недовольство ООП достигло критической точки.
Что же это я всё ООП да ООП. У него есть вполне конкретный образ - Си++, если ещё конкретнее, то Qt.
С чем я для себя сравниваю этот образ ООП - с Си, с одной стороны, с Scheme, с другой. Первый - процедурный язык, второй - функциональный. То есть ни первый, ни второй не является объектно-ориентированным языком.
Что объединяет упомянутые Си и схему? Концептуально? Данные и функции в них разделены. Они существуют по отдельности: есть данные и есть функции.
Что в случае ООП? Функции (в его терминах, методы) определены (существуют) для конкретных данных (экземпляра класса/структуры). Методы и данные неотделимы. Да, они могут существовать как-бы отдельно, но тогда это не ООП в том смысле, что за бортом оказываются свойства, которые дают ООП интересные (мягко говоря) возможности.
Что за свойства? 1) Полиморфизм. 2) Инкапсуляция. 3) Наследование. Первое и второе свойства не так интересны как третье, которое является, на мой взгляд, самым главным.
Наследование. Если к наследованию “прибавить” такое качество как неотделимость методов от данных, то получается, что наследование даёт безграничные возможности по расширению свойств некоторой (базовой) сущности (в смысле данных) и определению дополнительных и ПЕРЕОПРЕДЕЛЕНИЮ существующих методов исходной (базовой) сущности.
Переопределение методов. Почему это важно? Думаю вот почему. При разработке чего-то большого на Си (или функциональных языках) появляется необходимость изменения структуры программы таким образом, чтобы некоторые функции принимали в качестве аргумента другую функцию. Такая возможность, вообще-то, существенно помогает, а часто незаменима. В Си это указатель на функцию с известной сигнатурой, а в схеме всё есть функция, и проблем нет по определению.
Что в Си++? Можно изгаляться и пробрасывать внутрь одного класса указатель на функцию (внимание) экземпляра другого класса. Но на каждый чих делать это не захочется.
В Си++ подход другой. Использует этот другой подход механизм наследования: а) определяется базовый класс с некоторым виртуальным методом (сигнатурой); б) определяется другой класс, который определяется как производный от базового класса, внутри которого кодируется реализация виртуального метода; в) в третьем классе создаётся метод, среди аргументов которого будет указатель на экземпляр класса п.а) для того, чтобы вызвать описанный классом п.а) метод.
Какой вывод? В Си++ (вернее в ООП) сложнее. Конкретно на этом примере.
В Qt с этим проще, потому что существует система сигналов/слотов. Как-бы внешний механизм, который регистрирует и обслуживает вызовы определённых методов, закреплённых за конкретными экземплярами объектов. Более того, этот механизм позволяет осуществлять передачу управления другому потоку (вернее, инициировать вызов метода тем потоком, которому принадлежит экземпляр-владелец метода).
То есть в Qt существует очень эффективный способ “передачи указателя на функцию” для связи двух (в простейшем случае) экземпляров разных объектов (классов). И это хорошо.
Теперь нужно ещё раз подумать и сформулировать в каких случаях применять наследование?
Для определения базовых абстрактных классов (сигнатур, прототипов методов), которые будут являться основой для определения производных классов, отличающихся друг от друга поведением (реализациями методов).
Для себя я уяснил следующее: языку свойственны ограничения; их необходимо принять и одновременно забыть об особенностях и ограничениях других языков; способы решения задач средствами языка А отличаются от способов решения тех же задач средствами языка Б.
Комментариев нет:
Отправить комментарий