![]() |
|
Модераторы: LSD |
![]()
|
|
| Domestic Cat |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5452 Регистрация: 3.5.2004 Где: Dallas, US Репутация: 4 Всего: 172 |
Иногда попадаются наезды на ООП...
1. Я "сторонник" ООП 2. Мое глубокое убеждение в том, что каждому инструменту своя задача Тем не менее, почему ООП, или почему его считают "сложным", "бюрократичным", "махиной" и проч? А потому что видимо мало кто понимает зачем оно нужно и что конкретно оно дает. Обьясняю на примере. Я и сам нечто подобное думал написать, но сегодня попался на глаза коммент к статье Джоэла, один в один... Можно считать плагиатом. Итак, я решаю написать нечто на старом добром С и знать не хочу об ООП. Предположим, мне нужен линкованый список, и у меня в руках нет готовой библиотеки. Естественно, я ввожу нужные переменные и пишу ряд функций типа add(), get(), head(), tail(), count(), и проч. Программа пишется себе пишется, и файлик становится нехилым. Конечно же, я делаю программу модульной, то есть выношу список в отдельный модуль. Далее, оказывается, нужны также хешмап и дерево. Соответственно, они попадают в модули map.c, tree.c Это есть гут, но теперь я начинаю путаться в вызовах методов, ведь каждая из этих коллекций имеет похожие методы add(), remove() и так далее. Выход например такой: переназвать методы в addToTree(), addToMap(), addToLinkedList(). Трудимся, и перелопачиваем все эти файлы. Далее, оказывается нужен также особый список, содержащий только элементы определенного типа. Нужно создавать отдельный модуль LinkedStringList и методом копипаста переносить в него содержимое предыдущего файла. Естественно, кое где я забываю подправить код, что стоит мне в дальнейшем многих часов дебаггинга. Далее, вроде все ок, но мне нужно провести поиск по нескольким коллекциям сразу. Простой код поиска в списке необходимо дублировать и для мапа и для дерева, меняя в каждом случае только вызов методов. Другая проблема в том, я решаю использовать хешмап, написанный Васей (он круче) и по незнанию меняю пару переменных из своего кода, тогда как они нужны самому мапу и ни в коей мере не мне. Упс - хешмап оказывается очень неэффективным! Какого спрашивается? Денек - и ответ ясен, не нужно было трогать этих переменных. Теперь посмотрим, можно ли упростить наш кот? Можно. Назовем модуль - классом, и создадим объект этого класса. Тогда решается проблема с названиями: вместо addToTree() мы пишем tree.add(), вместо addToStringLinkedList() мы пишем linkedList.add(). Проще? Проще. Давайте также введем модификаторы доступа: переменные, не предназначенные для публики, объявим private, так что ни Вася, ни Петя не смогут из своего кода напрямую изменить их. Наконец, введем наследование: объявив класс наследующим от другого класса, мы получаем весь его код без копипаста, и вдобавок можем перегружать определенные функции своими, т.е. подстраивать их под себя. Но если два класса наследуют от одного и того же класса, они фактически являются этим классом, т.е. как минимум содержат те же методы. Следовательно, их можно рассматривать как класс-родитель. Теперь нам не нужно писать код отдельно для каждого типа коллекции, мы можем привести все эти коллекции к одному базовому типу и вместо трижды повторенного копипастом кода мы имеем один на всех. Вот и получается, что ООП есть попросту упрощенное процедурное программирование. Конечно, ООП обросло своими специфическими вещеми и своей философией. И это позволило создать громадное количество библиотек за довольно небольшой промежуток времени. Заметьте, только ОО языки превратились в конце концов в "платформы" и "технологии". С помощью Java или C# можно написать любое высокоуровневое приложение - будь то десктоп, веб или корпоративное приложение, база данных или игра. Единственное что отброшено - это низкоуровневость (и скорость по сравнению с С и асмом). Но при настоящем развитии железа это и не нужно. -------------------- |
|||
|
||||
| Mayk |
|
||||||||||
![]() ^аВаТаР^ сообщение>> ![]() ![]() ![]() ![]() Профиль Группа: Участник Сообщений: 2616 Регистрация: 22.5.2005 Где: за границей разум а Репутация: 2 Всего: 134 |
Это отнюдь не естественно. Взять, например gtk или tuxracer. По их стилю это записалось бы вот так: list_add(), list_get(), etc
Не всегда нужен модуль. Нужен препроцессор. И нет никакой правки кода.
А вообще я тоже сторонник ООП. Просто препроцессор люблю. Жалко редко удается использовать. Это сообщение отредактировал(а) Mayk - 29.6.2005, 21:53 -------------------- Здесь был кролик. Но его убили. Человеки < кроликов, йа считаю. |
||||||||||
|
|||||||||||
| Domestic Cat |
|
||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5452 Регистрация: 3.5.2004 Где: Dallas, US Репутация: 4 Всего: 172 |
Какая разница, в любом случае название метода менять надо.
На препроцессор еще дядя Страуструп ругался Добавлено @ 22:03 Да и я бы не стал относить препроцессор к процедурному программированию, это фича компилятора не более того. -------------------- |
||||
|
|||||
| S.A.P. |
|
|||
|
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Участник Клуба Сообщений: 2664 Регистрация: 11.6.2004 Репутация: 1 Всего: 71 |
Сейчас придет Oleg1973 и разьяснит что к чему
|
|||
|
||||
| Mayk |
|
||||||||||||
![]() ^аВаТаР^ сообщение>> ![]() ![]() ![]() ![]() Профиль Группа: Участник Сообщений: 2616 Регистрация: 22.5.2005 Где: за границей разум а Репутация: 2 Всего: 134 |
Если ты сразу назовешь ф-ции для работы с листом как list_XXX, а ф-ции для работы с деревом как tree_xxx, то никакой неразбирихе не будет. Это этика написания и её надо придерживаться, иначе даже ООП не спасет. Пример(Что он делает?):
Правильно, это скалярное произведение векторов Кстати писать имена классов перед функциями предпочтительнее, потому что так код смотрится красивее(четко видны блоки где что-то делается исключительно с list'ом).
Вот здесь полностью согласен. Препроцессор жутко уродлив и нечитаем особенно если один define идёт на два экрана. Например так сделано во freeciv'е в common/map.h в макросе iterate_outward(freeciv1.13: 36 строк). У него есть еще недостатки: нельзя делать макросы на ходу, типа #define aaa(xxx) \ #define aaa_##xxx А жаль, получилась бы хорошая замена template'ам
Ну если постараться, то в с++ тоже можно сделать труднопонимаемый код, если наиграться с перегрузкой операторов... Хотя ты кажется уже считал полезность их перегрузки сомнительной
Ухх, не врубился. [QUOTE=Domestic Cat, 29.6.2005, 22:55] Да и я бы не стал относить препроцессор к процедурному программированию, это фича компилятора не более того. [/quoute] Тема называется ООП против остальных. Препроцессор и есть часть остальных Кроме того эта очень мощная фича. Ведь виртуальные функции - это тоже фича. Их можно сделать и вне ООП Кроме того надо не забывать про недостатки ООП. 1)Переопределение: class A{ int primeNumber() { return 2;} } class B:A{ int primeNumber() { return 65539;} } B* b = new B; A* a = b; b->primeNumber() = 65539 a->primeNumber() = 2 А это не всегда желательно. 2) Вызов методов для получения и/или установки значений. Вот пример из sim-0.9.3/sim/api/
Здесь только socket() определена как inline функция. А вот setSocket будет вызываться. А как с этим дела обстоят в жабке? Там же все функции по умолчанию виртуальны? Значит всевозможные тело getX() нельзя подставить в код заместо вызова, если разработчик библиотеки зазевался(кстати о птичках, пишу в 3 часа ночи) и не развиртуалил функцию(блин, не помню как это называется) ЗЫ. Ну вот, опять всплыла тема препроцессор против плюсов Это сообщение отредактировал(а) Mayk - 29.6.2005, 23:19 -------------------- Здесь был кролик. Но его убили. Человеки < кроликов, йа считаю. |
||||||||||||
|
|||||||||||||
| Domestic Cat |
|
||||||||||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5452 Регистрация: 3.5.2004 Где: Dallas, US Репутация: 4 Всего: 172 |
Предположим, что тебе нужно теперь вместо листа использовать дерево. Придется тебе идти по коду и менять названия методов
Плохой кот можно делать на любом языке.
Это не С конечно, но иллюстрация к тому, что можно сделать директивами
Если это нежелательно то не надо так делать, нужно метод по-другому назвать. Наоборот, данное свойство позволяет динамически привязывать методы и фактически является основой полиморфизма.
Опять таки, это свойство компилятора, к ООП не имеет отношения. То же самое можно сказать и о функциях в С. Насчет Java - любой объявленный final метод привязывается статически, и JIT делает его inline. -------------------- |
||||||||||||
|
|||||||||||||
| Mayk |
|
||||||
![]() ^аВаТаР^ сообщение>> ![]() ![]() ![]() ![]() Профиль Группа: Участник Сообщений: 2616 Регистрация: 22.5.2005 Где: за границей разум а Репутация: 2 Всего: 134 |
Ах, ты в этом смысле. Ничего, есть достойное решение и этой проблемы. Search&Replace - selected only Ищем list_, заменяем на tree_. И все листы становятся деревьями. Не многим сложнее, чем заменить название одного класса, не так ли? Опять же, еще можно взять препроцессор, а дальше все зависит от фантазии. И, опять же, добавление в лист отличается от добавления к дереву. Так что код придется менять в любом случае.
Не соглашусь. Это имеет отношение к ООП, так как ООП предоставляет возможность делать языки программирования в которых легко забыть о том, что getX() будет вызыван и не подставлен. В функциональном программирования виртуальных функций нет как класса, там это невозможно. А препроцессор си гарантированно сделает "функцию" inline'ной. А языки программирования в свою очередь предсоставляют возможность писать библиотеки, в которых getX() не объявлен как финальный. Взять для примера робокот. getX() там НЕ объявлен финальным. Его можно перегрузить, хотя польза от подобной перегрузки весьма сомнительна. Ну я вот не могу придумать зачем отдельно взятому роботу перегружать getX()... (Тем более, что в заблуждение врагов это не вводит Ладно, поехали дальше 3) Проектирование классов. Можно разделить на две подпроблемы - 3.1) не знаешь что куда запихать. Такое иногда случается. Хотя ладно, неудачная реализация классов говорит плохо скорее о создателях либы. В функционалке они бы тоже наверняка напортачили. Просто ООП даёт больше возможностей напортачить. Сиди, гадай как получить, скажем, вес колеса машины: не то Car.getDetailWeight("Wheel1"); не то Car.getGrouppedDetails("Wheels").getSingleDetail(1).weight(), а может колесо вообще деталью не счиатают и надо Car.getWheelWeight(). 3.2) непосредственное написание классов. Это довольно утомительное занятие писать классы, если эти классы тривиальны и большинство функций в них - это get/setы. Пример - математический вектор. В здравом уме в си никто и не подумает писать getX() для вектора. Максимум - это #define vector_getX(vector) ((vector).x). А в ООП здравый ум так делать не будет. -------------------- Здесь был кролик. Но его убили. Человеки < кроликов, йа считаю. |
||||||
|
|||||||
| Domestic Cat |
|
||||||||||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5452 Регистрация: 3.5.2004 Где: Dallas, US Репутация: 4 Всего: 172 |
Точнее не название класса, а тип переменной. Но этот самый серч и реплейс не есть выход, речь ведь не о технических средствах... Название методов менять сложнее чем тип переменной
Во-первых, не всякую функцию, а объявленную так. Во-вторых инлайн не всегда хорошо, поскольку приводит к увеличению кода.
Это незначительно уменьшает скорость вызова метода, и все. Приложения, для которых подобное различие является существенным - это скорее реал-тайм, их и так на асме или С пишут.
Дык потому что это никак не повлияет на производительность кота. Ну предположим создатель заменил бы все что можно на финал. Предположим, за счет этого бы был выигран дополнительный фрейм в секунду (в чем я очень сомневаюсь). Но мы бы этого и не заметили, потому что для глаза что 40 что 39 фреймов в секунду - все едино.
Подобные вещи решает архитектор проекта, который дает кодеру готовую УМЛ диаграмму где все расписано. Или ты сам продумываешь структуру приложения. Только ты знаешь нужно тебе колесо в дальнейшем или нет, есть ли вообще возможность что понадобится создать класс колеса и вести их учет. Основываясь на этом, ты легко можешь решить какой метод писать.
Писать дольше, зато реюзать легче. А вообще-то в любой ИДЕ есть такая штука как создание класса/поля/метода/итп где ве что надо делать это кликать на ок и вбивать название. -------------------- |
||||||||||||
|
|||||||||||||
| Mayk |
|
||||||||||
![]() ^аВаТаР^ сообщение>> ![]() ![]() ![]() ![]() Профиль Группа: Участник Сообщений: 2616 Регистрация: 22.5.2005 Где: за границей разум а Репутация: 2 Всего: 134 |
Не все библиотеки написаны мною. В скаченных либах UML диаграмм обычно не наблюдается. Зато наблюдается непонятные имена функций. Напрмимер в QT шной qwidget.h почему есть getWFlags(), если почти все остальные get'ы названы как size(), parent()? Кроме того, при просмтре .hшки возникает вопрос - чем windowState() отличается от getWState()? Оба возвращают uint.
Запусти бой между 10 роботами на 1000 раундов и сверни робокот неужели и сейчас выигрыш не виден? Кстати, скорость компиляции ОО языков меньше чем функциональных: все же компилятору легче распарсить один идентификатор list_add нежели три list, ->, add. Разумеется, в функциональных языках все идентификаторы записываются в одну хэш таблицу, в то время как в ОО их можно распихать в несколько, но при хорошем хэшировании это будет не страшно и даже не заметно(если интересно, что такое хорошое хэширование, советую посмотреть как хэшируется содержимое PAKов в QuakeForge).
Ну пусть размер файла увеличится даже на 10%. Ну и кого это волнует, если память измеряется мегабайтами? Лично мне всё равно сколько весит екзежник - метр, или метр и сто кб. Меня размеры экзежника не волновали даже на 40 метровом винте. Размер исполяемого файла обычно иного меньше размера всяких .dat файлов. А тебя так волнует размер исполняемого файла? Только честно.
Во-первых на это ты уже сам ответил
Во-вторых не всякая IDE сделает код для get и set функций. Ну объявятся они в .cpp, но return var; и var=value; всё равно придется писать руками. -------------------- Здесь был кролик. Но его убили. Человеки < кроликов, йа считаю. |
||||||||||
|
|||||||||||
| Domestic Cat |
|
||||||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5452 Регистрация: 3.5.2004 Где: Dallas, US Репутация: 4 Всего: 172 |
Ну понятно, что такой метод будет вызываться медленнее, ну будет проигрыш по времени в данном случае на, скажем, секунду. Это на что-то влияет? Плата за вызов метода компенсируется полиморфизмом и наследованием, программист должен выбрать: то ли пользоваться этим и проиграть на скорости вызова метода, то ли не пользоваться. В большинстве приложений проигрыш по скорости ни на что не влияет, тогда и ОО язык и имеет преимущество.
Опять, скорость компиляции при существующем железе будет практически неотличима. Я не могу на глаз отличить скорость gcc на Маке и jikes под виндой.
Ты ж робокота юзаешь Вообще размер исполняемого файла тоже по идее должен влиять на скорость исполнения, ведь он должен быть загружен в память?
Хорошо, можно написать ИДЕ которая будет из имени переменной и ее типа вставлять также эти ретурны и и var=value... Когда что-то вводится в язык, в результате приходится больше писать, но ведь есть и какой-то выигрыш. Возьмем васик - помнится в васике "подпрограммы" писались через указание заведомо большого номера строки, в конце стоял goto. В процедурных языках для таких целей используется функция или процедура. Писать больше (типы, название, вызов), зато такую процедуру можно реюзать много раз, тогда как бейсиковские "процедуры" были одноразовыми (тк гото всегда возвращал на одну и ту же строку). Первоначально программы писались в один файл. Потом оказалось что при большом размере программы работать с ней становится невозможно, и появились модули. Модуль нужно писать - создавать файл, называть, писать хедер, труда больше но и выигрыш очевиден. С ООП то же самое, ты пишешь больше кода, но это облегчает жизнь впоследствии. Если не делать гет/сет методы, то чтобы определить, кто и когда изменил переменную (паблик) нужно отрудиться. Если же указан даже "пустой" сет метод var=value, то чтобы определить кто вызвал метод, достаточно дописать туда две строки... ПС Если на то пошло, то например в Intellij IDEA нажатием 2-3 кнопок можно создавать комменты, блоки (циклы, трай-кетчи, ифы, итп), методы, переходить к определению любого метода или переменной, просматривать сорсы любого класса включая библиотечные, и миллион разных вещей - и все это с 2-3 кнопками. По скорости написания любой процедурный язык обгонишь, как бы мало там не надо было писать. Потому я и говорю о том, что тех.средства не показатель. -------------------- |
||||||||
|
|||||||||
| Mayk |
|
||||||||||||||||||||
![]() ^аВаТаР^ сообщение>> ![]() ![]() ![]() ![]() Профиль Группа: Участник Сообщений: 2616 Регистрация: 22.5.2005 Где: за границей разум а Репутация: 2 Всего: 134 |
Да. Я так тестирую робота
Gaim компилировался быстрее, чем sim. Хотя первый в архиве весил 5 метров, а второй 3. Но gaim написан на си, а сам на плюсах. Разумеется здесь еще очень сильно тормозят шаблоны, но gaim всё равно компилируется быстрее при большем весе
Некорректное сравнение. .с - исходник. .class - откомпилированный исходник. Или ты про .java? Ну... Я вот сделал один в один. 364 байта против 364ех
Должен-то конечно должен, но сотня килобайт не большой объем для винта не сыплющего бэды. Кроме того у меня notepad.exe загружается когда за полcекунды, а когда и за секунду. Слишком много второстепенных факторов делают загрузку нескольких дополнтельных килобайт абсолютно незаметными. Пусть лучше программа загрузится не за 1,00 сек, а за 1,01 сек, но будет работать быстрее.
А можно в очередной раз обратиться за помощью к препроцессору. Он есть во всех сях. А ИДЕ не все любят. Например, я люблю vim+ctags+пару скриптов для создания классов и заголовочных файлов.
В конце стоял RETURN. А вызывалась "функция" посредством GO SUB. При некотором опыте код GO SUB 5000 был довольно читаем. Например можно было разместить там чтение клавиши q/a/o/p/space. Помню, у меня на спеки последние строки были 9998 STOP //брякпоинт 9999 RANDOMIZE USR 15616: REM: SAVE "program" //запись на дискету
Итак, делаем вывод. ООП хорош для больших проектов, чьи системные требования относительно высоки. Теоретически можно написать столь же быстрое приложение на плюсах, как и на сях. Но реализация того же сишного быстродействия потребует большего времени, нежели на чистом си. Реюзабельный код можно писать на любом языке. ООП встаёт во всей своей красе при работе с большим кол-вом абстрактных данных(пример - построение GUI), в то время как при отстутствии абстракции рулит функционалка. -------------------- Здесь был кролик. Но его убили. Человеки < кроликов, йа считаю. |
||||||||||||||||||||
|
|||||||||||||||||||||
| Domestic Cat |
|
||||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5452 Регистрация: 3.5.2004 Где: Dallas, US Репутация: 4 Всего: 172 |
Вообще-то я говорил про исполняемые файлы. Или ты имел в виду сорсы?
Забыл совсем
С предыдущим я согласен, но вот не пойму что значит количество абстрактных данных? Данные в любом случае - абстракция. -------------------- |
||||||
|
|||||||
| Mayk |
|
||||||||||
![]() ^аВаТаР^ сообщение>> ![]() ![]() ![]() ![]() Профиль Группа: Участник Сообщений: 2616 Регистрация: 22.5.2005 Где: за границей разум а Репутация: 2 Всего: 134 |
Я имел в виду сорсы. А сравнивать виртуальную машину и реальную не совсем верно. Разве в .class'ы линкуются статичные либы?
Не совсем точно выразился. Переформулирую подробней: В UI есть лабелы, кнопки, поля для редактирования текста, картинки, панели, меню, тулбары и так далее. И со всеми с ними можно выполнять множество одинаковых действий, как то их активирование, кликание на них, сброс в них(drop), изменение enable, показ, прятание, перемещение, изменение размера. Здесь ООП с виртуальным наследованием подойдет гораздо лучше. Во всяком случае не хочется писать что-нить типа того:
Плюснутое class Link : public Text{ void clicked(); } гораздо локаничнее. Сравнить:
и
Обрати внимание на преобразования типа в вызове add_child. Это напрягает. Ведь в функционалке нет преобразования типов, здесь отсутствует абстракция типов: нет отвлечния от действительного типа, есть лишь преобразования типа вручную. И это преобразование потребуется в каждом вызове add_child, remove_child, is_child_exist, set_parent, при каждом обращении к виртуальной функции. В GUI много абстракции. По сути всё GUI - абстракция. -------------------- Здесь был кролик. Но его убили. Человеки < кроликов, йа считаю. |
||||||||||
|
|||||||||||
| Domestic Cat |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5452 Регистрация: 3.5.2004 Где: Dallas, US Репутация: 4 Всего: 172 |
Разве такая ситуация редка или специфична для гуя? По-моему, нет. -------------------- |
|||
|
||||
| Mayk |
|
|||
![]() ^аВаТаР^ сообщение>> ![]() ![]() ![]() ![]() Профиль Группа: Участник Сообщений: 2616 Регистрация: 22.5.2005 Где: за границей разум а Репутация: 2 Всего: 134 |
ГУЙ был взят в качестве примера. -------------------- Здесь был кролик. Но его убили. Человеки < кроликов, йа считаю. |
|||
|
||||
![]()
|
| Правила ведения Религиозных войн | |
|
|
1. Уважайте собеседника 2. Собеседник != враг 3. Старайтесь воздерживаться от тем вида "Windows Rulez" или "Linux Rulez" С уважением, Smartov. |
| 0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей) | |
| 0 Пользователей: | |
| « Предыдущая тема | Религиозные войны | Следующая тема » |
|
|
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности Powered by Invision Power Board(R) 1.3 © 2003 IPS, Inc. |