Местный
Регистрация: 26.04.2006
Адрес: Удомля, гдежещё
Сообщений: 1,986
Вы сказали Спасибо: 676
Поблагодарили 257 раз(а) в 167 сообщениях
|
Многие программисты рассматривают ООП, кроме всего прочего, как средство описания небольших модулей в рамках приложения. Любой язык программирования, имеющий даже самое неполное ОО-расширение, позволяет хорошо изолировать миниатюрные интерфейсы в общем пространстве имен программы. Такой подход помогает нам мысленно изолировать небольшую функциональную единицу и заняться ее проектированием в отрыве от всего приложения.
И тут мы временами можем сталкиваться с трудностями, связанными с семантической принадлежностью того или иного метода данному классу. Например, принадлежность метода paint() некой абстрактной визуальной компоненте почти не вызывает сомнения. Но как быть с методом, который вставляет данную компоненту в список дочерних элементов родительской компоненты? Должен ли это быть метод add_child() в родительском интерфейсе, или set_parent() в дочернем (или может и то, и другое)? Я бы предпочел первый вариант, но в GUI-интерфейсах чаще встречается как раз таки второй, потому что дочерний объект должен еще и удалить самого себя из списка предыдущего "родителя".
А что если попытаться разрешить проблему выставив этот метод за пределы класса? Представьте себе, вы имеете глобальную функцию set_affinity(parent, child), которая, возможно, вызывает защищенные виртуальные методы оповещения как в родительском классе, так и в дочернем. Не вижу в этом ничего страшного, и даже более того, мне будет легче запомнить тот факт, что set_affinity() никому не принадлежит, чем пытаться вспомнить в каком интерфейсе его следует искать: в родительском или дочернем. И все потому, что set_affinity() по сути никому не принадлежит и является "нейтральным" действием, в котором принимают участие больше одного объекта.
Другой пример: обычно базовые классы, реализующие подсчет ссылок, объявляют по крайней мере два метода: acquire() и release() внутри этого абстракного интерфейса, которые увеличивают или, соответственно, уменьшают счетчик ссылок. Но возникает вопрос: насколько метод release() принадлежит данному объекту? Ведь в результате вызова этого метода объект может быть уничтожен, следовательно он является чем-то нейтральным скорее чем является "собственностью" объекта. В PTypes оба этих метода объявлены глобальными (правда в случае acquire() - только ради симметрии с release()).
Примеров можно найти немало. Возьмите хотя бы преобразование строки в число и обратно: если в вашем языке строки и численные типы являются объектами, то каким именно классам должны принадлежать функции преобразования? На самом деле они по своей природе никому не принадлежат и являются самыми обычными глобальными функциями, а объявление их, к примеру, в классе string не дает никакого преимущества. Вы не выигрываете от этого ровным счетом ничего, или может даже проигрываете в естественности и интуитивности интерфейса.
Кстати говоря, вовсе необязательно, чтобы такие нейтральные функции были объявлены буквально глобальными: вы вполне можете найти для них место в каком-то другом классе, лишь бы это выглядело логично. Например, тот же set_affinity() может оказаться в объекте окна (ведь визуальные компоненты так или иначе "живут" в окне), а пара acquire()/release() - в каком-нибудь class factory, которые нынче очень модны.
Все это вам может и не понравиться, но лучше подумайте о том, что естественность интерфейса важнее привычек. А привычки ОО методологии настолько липучи, что, как видите, мы временами можем закрывать глаза на противоестественность в нашем коде.
***
У меня в данный момент нет из всего этого готовых конкретных выводов в стиле "следует писать программы так-то, и не следует писать так-то", поскольку любой такой вывод был бы уязвим для критики.
Но есть одна вещь, с которой вы не можете не согласиться: интерфейсы стоят того, чтобы им уделялось много времени на обдумывание. Реализация - это сосвем другая стихия, которая является сутью нашего с вами ремесла, но интерфейсы библиотек - это языки, на которых мы предлагаем думать и писать другим программистам. Я никогда не жалею своего времени, чтобы оптимизировать интерфейс, сделать его более компактным и естественным, если даже ради этого придется пойти наперекор собственным объектно-ориентированным привычкам.
Попробую сделать еще один, уже более общий и менее очевидный вывод: следует проявлять консерватизм и минимализм в таких базовых вещах, как языки программирования, generic библиотеки и ядра операционных систем. В то же время, эти свойства становятся менее уместными по мере того, как вы поднимаетесь вверх в направлении к конечному пользователю.
__________________
I never saw a wildthing sorring for itself.
A small bird will drop frozen dead without ever felt sorry for itself.
|