Про объекты
Sep. 23rd, 2009 12:54 pm![[personal profile]](https://www.dreamwidth.org/img/silk/identity/user.png)
![[livejournal.com profile]](https://www.dreamwidth.org/img/external/lj-userinfo.gif)
Ответы я выношу в отдельный пост, поскольку внутри 200-комментной дискуссии их вряд ли кто-нибудь кроме него прочитает.
если каждый продвинутый тиклер писал свою объектную систему, то не следует ли из этого, что объектный подход таки естетсвенен и удобен?
Объектный подход, очевидно, имеет свою нишу. В которой он таки да, естественнен и удобен. Но вот GUI этой нишей не является.
Далее, существует несколько видов объектного подхода. Даже в компилируемых языках на базе C есть Objective-C и C++ с существенно разными объектными подходами. А если мы рассмотрим, скажем, SmallTalk, Python и Ruby, разница будет еще больше. Или можно сравнить несколько объектных систем Tcl, входящих в tcllib. Они существенно разные.
Разбирать, чем именно разные, и в каких конкретно случаях удобна та или иная разновидность, мне сейчас лень.
Когда задачи, для которых удобен один из видов объектного подхода, пытаются силой прогнуть под другой его вид, только потому что его поддерживает естественный язык, то получается хреново.
чем тебе не объектное API fopen/fread/fclose?
Тем что FILE * нельзя унаследовать. Вернее, может быть оно и объектное API, но использование объектного API и объектно-ориентированное программирование - разные вещи.
Объектно-ориентированное программирование это когда используется наследование с переопределением методов, а не когда у тебя есть предоставленные языком или библиотекой сложные типы данных с операциями над ними. А так-то, конечно, любой кусочек данных немножечко
Плохой объектно-ориентированный подход, это когда объект НЕОБХОДИМО наследовать чтобы получить уникальный экземпляр. Например, интерфейсы Turbo Vision были устроены так, что для каждого приложения было необходимо порождать наследника от TApplication, а для каждого диалогового окошка - наследника от TDialog.
Еще один пример плохого OO-дизайна - объект SocketServer в стандартной библиотеке Python.
Ему при инициализации передается имя класса-наследника RequestHandler, и он сам порождает экземпляр этого класса. Если бы ему передавался инициализированный экземпляр, было бы гораздо удобнее. Можно было бы написать один RequestHandler, который работал немножко по-разному. в зависимости от переданных при инициализации данных.
Хороший объектно-ориентированный подход это когда в большинстве случаев ты можешь рассматривать объекты как данность, как такие встроенные типы. А наследовать их - только когда задача ДЕЙСТВИТЕЛЬНО нетривиально.
Даже если у тебя вся остальная программа объектная, и есть какая-то своя иерархия классов, наследовать стандартные классы из библиотек тебе ни к чему. Ими и так можно пользоваться, производят необходимую кастомизацию с помощью параметров и делегирования.
Примером хорошего объектно-ориентированного подхода являются перловые модули CGI и DBI.
Плохим объектно-ориентированным языком является такой, где нельзя унаследовать int (или другой встроенный базовый тип). Вы уж или штаны наденьте, или крестик снимите. Или у вас объектный язык, тогда от любого используемого типа данных можно породить наследника, переопределив часть его свойств, либо не заикайтесь об ООП.
no subject
Date: 2009-09-23 09:29 am (UTC)Золотые слова!
Плохим объектно-ориентированным языком является такой, где нельзя унаследовать int (или другой встроенный базовый тип). Вы уж или штаны наденьте, или крестик снимите.
Ну это уже аргумент не практический, а сугубо методологический. Сугубо методологические аргументы в решении практических вопросов опасны. Ой, у меня тоже методологический аргумент вышел. Даже метаметодологический ;)
no subject
Date: 2009-09-23 09:32 am (UTC)Вполне практический. Я тут, знаете ли, криптографией занимаюсь. И мне удобно когда у меня bigint является наследником от int.
no subject
Date: 2009-09-23 09:36 am (UTC)но тем не менее беру свои слова назад в таком случае, ок.
no subject
Date: 2009-09-23 09:38 am (UTC)Не говоря уж о том, что другой предрассудок - что декларативное наследование - единственный возможный ОО-механизм. Например есть альтернатива (в O'Caml), где отношение subtyping'a определяется не наследованием, а соотвествием типов - если A реализвует все что должно быть в B - значит A подтип B.
А наследование - просто удобный механизм для создания подтипов (но никто не ме мешает обойтись без него просто реализовав тот же интерфес руками)
(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:no subject
Date: 2009-09-23 12:19 pm (UTC)сильно разные алгоритмы работы на низком уровне.
(no subject)
From:no subject
Date: 2009-09-23 09:31 am (UTC)no subject
Date: 2009-09-23 09:36 am (UTC)OOП в GUI начинается тогда, когда для данного контрола нужно поменять ПОВЕДЕНИЕ, чуть-чуть в сторону от стандартного. Повесить обработчик на какое-то событие.
Это гораздо дешевле делается как в Tk, когда мы просто берем и присваиваем куда следует функцию-обработчик.
На самом деле одна из засад современного GUI-программирования заключается в том, что мы de-facto отошли от архитектуры фон Неймана (где код это такие данные). Современные компилируемые языки старательно разносят код и данные по разным сегментам с разными правами на чтение, чтобы упаси господи, никто не попытался runtime код генерировать.
(no subject)
From:(no subject)
From:no subject
Date: 2009-09-23 11:10 am (UTC)Собственно, лучшая модель для гуя -- электрическая схема, которая, определенно, состоит из объектов, причем с хорошо определенным интерфейсом. Объекты отлично объединяются в более сложные объекты (можно манипулировать отдельными микросхемами, а можно -- платами). С другой стороны концепция наследования электронщику может разве что в страшном сне присниться.
(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:no subject
Date: 2009-09-23 09:34 am (UTC)no subject
Date: 2009-09-23 09:39 am (UTC)no subject
Date: 2009-09-23 09:35 am (UTC)Использовать же ООП в качестве механизма абстракции данных etc сродни вырезания гланд автогеном и через задницу - то есть если нормальных механизмов нет - и ООП сойдет. Именно поэтому, кстати, наследование из чего угодно ни на фиг не нужно: ничего кроме оверхеда оно не даст.
Другой заменитель - вместо замыканий: тоже стрельба из пушки по воробьям.
no subject
Date: 2009-09-23 09:38 am (UTC)Э, нет. С таким подходом к GUI никогда удобного интерфейса не создать. Модель - это такая хреновинка на поиграться умным людям. Если она при шаге вправо, шаге влево начинает глючить, то на то она и модель.
Как говаривал Смок Беллью, рулетка - сама себе система. GUI это не модель, это сама по себе сисетема для решения вполне real-world задач, стоящих перед пользователем.
(no subject)
From:(no subject)
From:(no subject)
From:no subject
Date: 2009-09-23 11:00 am (UTC)Tk кстати объектный. Объектный подход оказывается очень уместным для таких сложных систем, как GUI.
no subject
Date: 2009-09-23 11:19 am (UTC)(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:no subject
Date: 2009-09-23 03:21 pm (UTC)(no subject)
From:(no subject)
From:(no subject)
From:no subject
Date: 2009-09-23 03:33 pm (UTC)Ну и не везде это есть (увы)
(no subject)
From:no subject
Date: 2009-09-23 11:21 am (UTC)...
Хороший объектно-ориентированный подход это когда в большинстве случаев ты можешь рассматривать объекты как данность, как такие встроенные типы. А наследовать их - только когда задача ДЕЙСТВИТЕЛЬНО нетривиально.
Хорошее сочетание утверждений :)
no subject
Date: 2009-09-23 11:27 am (UTC)(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:О SocketServer
Date: 2009-09-23 11:48 am (UTC)В принципе, для обработки эти объекты не нужны, достаточно было бы указывать просто функцию-обработчик, и (сюрприз!) действительно вместо конструктора класса-наследника RequestHandler можно указать просто функцию, принимающую аргументами request, client_address и server. Правда придётся самому следить, чтобы никакое исключение не вылетело за её пределы и аккуратно подчищать хвосты.
Польза от RequestHandler становится видна, когда используются производные классы — StreamRequestHandler и DatagramRequestHandler, в которых инкапсулируется кое-какая вспомогательная работа. Иначе пришлось бы больше писать в пользовательском коде.
Большой разницы, определить ли функцию-обработчик или класс с методом-обработчиком, нет, зато во втором случае мы можем выделить в родительский класс общий код. Полезно при использовании большого числа почти одинаковых обработчиков.
вы жжоте что ли?
Date: 2009-09-23 02:04 pm (UTC)Re: вы жжоте что ли?
Date: 2009-09-23 02:38 pm (UTC)Re: вы жжоте что ли?
Date: 2009-09-23 03:00 pm (UTC)Re: вы жжоте что ли?
From:Re: вы жжоте что ли?
From:Re: вы жжоте что ли?
From:Re: вы жжоте что ли?
From:Re: вы жжоте что ли?
From:Re: вы жжоте что ли?
From:Re: вы жжоте что ли?
Date: 2009-09-25 05:10 pm (UTC)1) Подход с замыканиями (или, например, скриптами в Tk, которые по сути эмулируют замыкания);
2) Функциональный подход, реализованный, например, в Erlang-биндингах к Tk (и, вероятно, к набирающмм в этом сообществе популярность биндингах к wxWidgets, — тоже);
3) Использование сопроцедур, когда передача управления между циклом обработки сообщений и обработчиками сообщений выглядит как взаимные вызовы обычных процедур друг из друга.
(Наверняка есть что-то ещё, о чём я не знаю.)
Другими словами, основная идея состоит в том, что в GUI, которые, как правило, строятся на событиях, обработчики событий должны совместно оперировать какими-то данными, относящимися к элементам управления этого GUI, а конкретная реализация управления этими данными может быть разной.
no subject
Date: 2009-09-25 05:42 am (UTC)Это скорее примитивный тип. Наличие примитивных типов в С-шных языках связано, подозреваю, с наличием автоматической памяти (обычно стека), которую допустимо инициализировать копированием:
int x = f();
а вызывать каких-либо методов для деинициализации не нужно вовсе - подвинул указатель и все. Ну очень эффективно. С типом, который позволяет от себя наследовать, не выйдет - для него нужно определять методы. А так-то, вроде, есть в языках объектные аналоги. В каком-нибудь Objc-C вместо
{
int x = 8;
}
пришлось бы писать
{
NSNumber *x = [[NSNumber numberWithInt:8] autotelease];
}
:-) Полагаю, для чистой ОО-программы записи
Type x = g();
вообще не должно быть (в С++-ном смысле), равенство должно возвращать ссылку на объект справа, а не "копировать" его, ибо под "копированием" можно понимать все, что хочешь.
no subject
Date: 2009-09-25 04:11 pm (UTC)С++ - расширение Си (с точностью но неинтересных отличий).
В Си все типы POD (plain old data - типы, допускающие копирование побайтовым копированиеи).
С++ просто унаследовал POD и их семантику.
Так что надо ставить вопрос "с чем связано наличие примитивных типов в Си". А это вопрос вроде простой - интегральные типы соответствуют типам процессора, а пользовательские типы данных - простые Sum Types (enum) хорошо мапятся на "массив байт", Structures просто конкатенация её членов, а union - суть кусок памяти sizeof(some_union)=max(sizeof(Xi)), где Xi - i-ый элемент union.
(no subject)
From: