Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Религиозные войны > ООП против Остальных


Автор: Domestic Cat 29.6.2005, 21:09
Иногда попадаются наезды на ООП... smile Сразу сообщаю что
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 29.6.2005, 21:50
Цитата(Domestic @ 29.6.2005, 22:09)
Естественно, я ввожу нужные переменные и пишу ряд функций типа add(), get(), head(), tail(), count(), и проч.

Это отнюдь не естественно. Взять, например gtk или tuxracer. По их стилю это записалось бы вот так: list_add(), list_get(), etc


Цитата(Domestic @ 29.6.2005, 22:09)
Далее, оказывается нужен также особый список, содержащий только элементы определенного типа. Нужно создавать отдельный модуль LinkedStringList

Не всегда нужен модуль. Нужен препроцессор. И нет никакой правки кода.
Код

#ifndef __stringlist_h
#define __stringlist_h
typedef list_t string_list_t;
typedef list_node_t string_list_node_t;

#define string_list_get(list) (string) list_get(list)
#define string_list_head(list) list_head
//cпецифичные для класса функции
//string_list_node_t* string_list_get_longest_string(string_list_t*);
#endif



Цитата(Domestic @ 29.6.2005, 22:09)
Далее, вроде все ок, но мне нужно провести поиск по нескольким коллекциям сразу. Простой код поиска в списке необходимо дублировать и для мапа и для дерева, меняя в каждом случае только вызов методов.

Код

#define __foreach_start(collection_type_name, __collection_var, __iterator) \
do{\
collection_type_name##_t* __iterator;\
for(__iterator=collection_type_name##_head(__collection_var);__iterator;\
__iterator=__iterator->next)

#define __foreach_end() }while(0)


А вообще я тоже сторонник ООП. Просто препроцессор люблю. Жалко редко удается использовать.

Автор: Domestic Cat 29.6.2005, 21:55
Цитата(Mayk @ 29.6.2005, 12:50)

Это отнюдь не естественно. Взять, например gtk или tuxracer. По их стилю это записалось бы вот так: list_add(), list_get(), etc


Какая разница, в любом случае название метода менять надо.

Цитата(Mayk @ 29.6.2005, 12:50)
Не нужен модуль. Нужен препроцессор. И нет никакой правки кода.

На препроцессор еще дядя Страуструп ругался smile Хотя да, он же ООПшник smile Но в любом случае препроцесор может сделать код настолько нечитабельным и непонятным, что ужос. По крайней мере разобраться в приведенном тобой коде было бы *очень* сложно, при его чтении нужно было бы держать в памяти все директивы. Я вообще на препроцессорных директивах мозгу почти Java кот написать, отличие будет в одной точке smile
Добавлено @ 22:03
Да и я бы не стал относить препроцессор к процедурному программированию, это фича компилятора не более того.

Автор: S.A.P. 29.6.2005, 22:11
Сейчас придет Oleg1973 и разьяснит что к чему smile .

Автор: Mayk 29.6.2005, 23:14
Цитата(Domestic @ 29.6.2005, 22:55)
Какая разница, в любом случае название метода менять надо.

Если ты сразу назовешь ф-ции для работы с листом как list_XXX, а ф-ции для работы с деревом как tree_xxx, то никакой неразбирихе не будет. Это этика написания и её надо придерживаться, иначе даже ООП не спасет.
Пример(Что он делает?):
Код

class dfjdsfjfdsfds{
   double boogieBa, baogieBa, boagieBa;
   double dsada(dfjdsfjfdsfds& dsaba){
          return boogieBa*dsaba.boogieBa+baogieBa*dsaba.baogieBa+boagieBa*dsaba.boagieBa;      
   }
};

Правильно, это скалярное произведение векторов smile
Кстати писать имена классов перед функциями предпочтительнее, потому что так код смотрится красивее(четко видны блоки где что-то делается исключительно с list'ом).

Цитата(Domestic @ 29.6.2005, 22:55)
Но в любом случае препроцесор может сделать код настолько нечитабельным и непонятным, что ужос.

Вот здесь полностью согласен. Препроцессор жутко уродлив и нечитаем особенно если один define идёт на два экрана. Например так сделано во freeciv'е в common/map.h в макросе iterate_outward(freeciv1.13: 36 строк). У него есть еще недостатки:
нельзя делать макросы на ходу, типа
#define aaa(xxx) \
#define aaa_##xxx
А жаль, получилась бы хорошая замена template'ам smile


Цитата(Domestic @ 29.6.2005, 22:55)
По крайней мере разобраться в приведенном тобой коде было бы *очень* сложно

smile
Ну если постараться, то в с++ тоже можно сделать труднопонимаемый код, если наиграться с перегрузкой операторов... Хотя ты кажется уже считал полезность их перегрузки сомнительной smile

Цитата(Domestic @ 29.6.2005, 22:55)
Я вообще на препроцессорных директивах мозгу почти Java кот написать, отличие будет в одной точке smile

Ухх, не врубился.

[QUOTE=Domestic Cat, 29.6.2005, 22:55]
Да и я бы не стал относить препроцессор к процедурному программированию, это фича компилятора не более того.
[/quoute]
Тема называется ООП против остальных. Препроцессор и есть часть остальных smile
Кроме того эта очень мощная фича. Ведь виртуальные функции - это тоже фича. Их можно сделать и вне ООП

Кроме того надо не забывать про недостатки ООП.
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.h:
class EXPORT SSLClient : public SocketNotify, public Socket
{ public:
...
    Socket *socket() { return sock; }
    void setSocket(Socket *s);
...
//sslclient.cpp
void SSLClient::setSocket(Socket *s){   
    sock = s; }

Здесь только socket() определена как inline функция. А вот setSocket будет вызываться.
А как с этим дела обстоят в жабке? Там же все функции по умолчанию виртуальны? Значит всевозможные тело getX() нельзя подставить в код заместо вызова, если разработчик библиотеки зазевался(кстати о птичках, пишу в 3 часа ночи) и не развиртуалил функцию(блин, не помню как это называется)


ЗЫ. Ну вот, опять всплыла тема препроцессор против плюсов smile



Автор: Domestic Cat 30.6.2005, 00:00
Цитата(Mayk @ 29.6.2005, 14:14)
Если ты сразу назовешь ф-ции для работы с листом как list_XXX, а ф-ции для работы с деревом как tree_xxx, то никакой неразбирихе не будет. Это этика написания и её надо придерживаться, иначе даже ООП не спасет.
Пример(Что он делает?):


Предположим, что тебе нужно теперь вместо листа использовать дерево. Придется тебе идти по коду и менять названия методов smile А потом окажется что нужен массив

Цитата(Mayk @ 29.6.2005, 14:14)
Ну если постараться, то в с++ тоже можно сделать труднопонимаемый код, если наиграться с перегрузкой операторов... Хотя ты кажется уже считал полезность их перегрузки сомнительной smile

Плохой кот можно делать на любом языке. smile

Цитата(Mayk @ 29.6.2005, 14:14)
Ухх, не врубился.

Это не С конечно, но иллюстрация к тому, что можно сделать директивами
Код

#define public public : 
#define System cout
#define out operator
#define println <<
#define String char* 
#include <iostream>
using namespace std;
class Test
{
    public Test()
    {
        System.out println(getString());
    }
    
    String getString()
    {
        return "Hello World";
    }
}


Цитата(Mayk @ 29.6.2005, 14:14)
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
А это не всегда желательно.


Если это нежелательно то не надо так делать, нужно метод по-другому назвать. Наоборот, данное свойство позволяет динамически привязывать методы и фактически является основой полиморфизма.


Цитата(Mayk @ 29.6.2005, 14:14)
Здесь только socket() определена как inline функция. А вот setSocket будет вызываться.
А как с этим дела обстоят в жабке? Там же все функции по умолчанию виртуальны? Значит всевозможные тело getX() нельзя подставить в код заместо вызова, если разработчик библиотеки зазевался(кстати о птичках, пишу в 3 часа ночи) и не развиртуалил функцию(блин, не помню как это называется)


Опять таки, это свойство компилятора, к ООП не имеет отношения. То же самое можно сказать и о функциях в С.
Насчет Java - любой объявленный final метод привязывается статически, и JIT делает его inline.

Автор: Mayk 30.6.2005, 09:51
Цитата(Domestic @ 30.6.2005, 01:00)
Придется тебе идти по коду и менять названия методов

Ах, ты в этом смысле. Ничего, есть достойное решение и этой проблемы. Search&Replace - selected only smile
Ищем list_, заменяем на tree_.
И все листы становятся деревьями. Не многим сложнее, чем заменить название одного класса, не так ли?
Опять же, еще можно взять препроцессор, а дальше все зависит от фантазии. И, опять же, добавление в лист отличается от добавления к дереву. Так что код придется менять в любом случае.

Цитата(Domestic @ 30.6.2005, 01:00)
Это не С конечно, но иллюстрация к тому, что можно сделать директивами

smile


Цитата(Domestic @ 30.6.2005, 01:00)
Опять таки, это свойство компилятора, к ООП не имеет отношения.

Не соглашусь. Это имеет отношение к ООП, так как ООП предоставляет возможность делать языки программирования в которых легко забыть о том, что getX() будет вызыван и не подставлен. В функциональном программирования виртуальных функций нет как класса, там это невозможно. А препроцессор си гарантированно сделает "функцию" inline'ной. А языки программирования в свою очередь предсоставляют возможность писать библиотеки, в которых getX() не объявлен как финальный. Взять для примера робокот. getX() там НЕ объявлен финальным. Его можно перегрузить, хотя польза от подобной перегрузки весьма сомнительна. Ну я вот не могу придумать зачем отдельно взятому роботу перегружать getX()... (Тем более, что в заблуждение врагов это не вводитsmile)

Ладно, поехали дальше

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 30.6.2005, 10:07
Цитата(Mayk @ 30.6.2005, 00:51)

Ах, ты в этом смысле. Ничего, есть достойное решение и этой проблемы. Search&Replace - selected only smile
Ищем list_, заменяем на tree_.

Точнее не название класса, а тип переменной. Но этот самый серч и реплейс не есть выход, речь ведь не о технических средствах... Название методов менять сложнее чем тип переменной

Цитата(Mayk @ 30.6.2005, 00:51)
А препроцессор си гарантированно сделает "функцию" inline'ной.

Во-первых, не всякую функцию, а объявленную так. Во-вторых инлайн не всегда хорошо, поскольку приводит к увеличению кода.

Цитата(Mayk @ 30.6.2005, 00:51)
Это имеет отношение к ООП, так как ООП предоставляет возможность делать языки программирования в которых легко забыть о том, что getX() будет вызыван и не подставлен.

Это незначительно уменьшает скорость вызова метода, и все. Приложения, для которых подобное различие является существенным - это скорее реал-тайм, их и так на асме или С пишут.

Цитата(Mayk @ 30.6.2005, 00:51)
Взять для примера робокот. getX() там НЕ объявлен финальным. Его можно перегрузить, хотя польза от подобной перегрузки весьма сомнительна. Ну я вот не могу придумать зачем отдельно взятому роботу перегружать getX()... (Тем более, что в заблуждение врагов это не вводитsmile)

Дык потому что это никак не повлияет на производительность кота. Ну предположим создатель заменил бы все что можно на финал. Предположим, за счет этого бы был выигран дополнительный фрейм в секунду (в чем я очень сомневаюсь). Но мы бы этого и не заметили, потому что для глаза что 40 что 39 фреймов в секунду - все едино.

Цитата(Mayk @ 30.6.2005, 00:51)
Сиди, гадай как получить, скажем, вес колеса машины: не то Car.getDetailWeight("Wheel1"); не то Car.getGrouppedDetails("Wheels").getSingleDetail(1).weight(), а может колесо вообще деталью не счиатают и надо Car.getWheelWeight().

Подобные вещи решает архитектор проекта, который дает кодеру готовую УМЛ диаграмму где все расписано. Или ты сам продумываешь структуру приложения. Только ты знаешь нужно тебе колесо в дальнейшем или нет, есть ли вообще возможность что понадобится создать класс колеса и вести их учет. Основываясь на этом, ты легко можешь решить какой метод писать.


Цитата(Mayk @ 30.6.2005, 00:51)
3.2) непосредственное написание классов.
Это довольно утомительное занятие писать классы, если эти классы тривиальны и большинство функций в них - это get/setы. Пример - математический вектор. В здравом уме в си никто и не подумает писать getX() для вектора. Максимум - это #define vector_getX(vector) ((vector).x). А в ООП здравый ум так делать не будет.

Писать дольше, зато реюзать легче. А вообще-то в любой ИДЕ есть такая штука как создание класса/поля/метода/итп где ве что надо делать это кликать на ок и вбивать название.

Автор: Mayk 30.6.2005, 15:09
Цитата(Domestic @ 30.6.2005, 11:07)
Подобные вещи решает архитектор проекта, который дает кодеру готовую УМЛ диаграмму где все расписано. Или ты сам продумываешь структуру приложения. Только ты знаешь нужно тебе колесо в дальнейшем или нет, есть ли вообще возможность что понадобится создать класс колеса и вести их учет. Основываясь на этом, ты легко можешь решить какой метод писать.

Не все библиотеки написаны мною. В скаченных либах UML диаграмм обычно не наблюдается. Зато наблюдается непонятные имена функций. Напрмимер в QT шной qwidget.h почему есть getWFlags(), если почти все остальные get'ы названы как size(), parent()? Кроме того, при просмтре .hшки возникает вопрос - чем windowState() отличается от getWState()? Оба возвращают uint.


Цитата(Domestic @ 30.6.2005, 11:07)
Дык потому что это никак не повлияет на производительность кота. Ну предположим создатель заменил бы все что можно на финал. Предположим, за счет этого бы был выигран дополнительный фрейм в секунду (в чем я очень сомневаюсь).

Запусти бой между 10 роботами на 1000 раундов и сверни робокот smile А теперь думаем. Сколько раз будет вызываться getX(), getY() за раунд? Много, это очевидно. Умножаем это кол-во на 1000, домножаем на время вызова виртуальной функции. А теперь скажи,
неужели и сейчас выигрыш не виден?
Кстати, скорость компиляции ОО языков меньше чем функциональных: все же компилятору легче распарсить один идентификатор
list_add нежели три list, ->, add. Разумеется, в функциональных языках все идентификаторы записываются в одну хэш таблицу, в то время как в ОО их можно распихать в несколько, но при хорошем хэшировании это будет не страшно и даже не заметно(если интересно, что такое хорошое хэширование, советую посмотреть как хэшируется содержимое PAKов в QuakeForge).


Цитата(Domestic @ 30.6.2005, 11:07)
Во-вторых инлайн не всегда хорошо, поскольку приводит к увеличению кода.

Ну пусть размер файла увеличится даже на 10%. Ну и кого это волнует, если память измеряется мегабайтами? Лично мне всё равно сколько весит екзежник - метр, или метр и сто кб. Меня размеры экзежника не волновали даже на 40 метровом винте. Размер исполяемого файла обычно иного меньше размера всяких .dat файлов. А тебя так волнует размер исполняемого файла? Только честно.

Цитата(Domestic @ 30.6.2005, 11:07)
А вообще-то в любой ИДЕ есть такая штука как создание класса/поля/метода/итп где ве что надо делать это кликать на ок и вбивать название.

Во-первых на это ты уже сам ответил
Цитата(Domestic @ 30.6.2005, 11:07)
речь ведь не о технических средствах

Во-вторых не всякая IDE сделает код для get и set функций. Ну объявятся они в .cpp, но return var; и var=value; всё равно придется писать руками.



Автор: Domestic Cat 30.6.2005, 17:59
Цитата(Mayk @ 30.6.2005, 06:09)
Запусти бой между 10 роботами на 1000 раундов и сверни робокот smile А теперь думаем. Сколько раз будет вызываться getX(), getY() за раунд? Много, это очевидно. Умножаем это кол-во на 1000, домножаем на время вызова виртуальной функции. А теперь скажи,
неужели и сейчас выигрыш не виден?

Ну понятно, что такой метод будет вызываться медленнее, ну будет проигрыш по времени в данном случае на, скажем, секунду. Это на что-то влияет?
Плата за вызов метода компенсируется полиморфизмом и наследованием, программист должен выбрать: то ли пользоваться этим и проиграть на скорости вызова метода, то ли не пользоваться. В большинстве приложений проигрыш по скорости ни на что не влияет, тогда и ОО язык и имеет преимущество.

Цитата(Mayk @ 30.6.2005, 06:09)
Кстати, скорость компиляции ОО языков меньше чем функциональных: все же компилятору легче распарсить один идентификатор
list_add нежели три list, ->, add. Разумеется, в функциональных языках все идентификаторы записываются в одну хэш таблицу, в то время как в ОО их можно распихать в несколько, но при хорошем хэшировании это будет не страшно и даже не заметно(если интересно, что такое хорошое хэширование, советую посмотреть как хэшируется содержимое PAKов в QuakeForge).

Опять, скорость компиляции при существующем железе будет практически неотличима. Я не могу на глаз отличить скорость gcc на Маке и jikes под виндой.

Цитата(Mayk @ 30.6.2005, 06:09)

Ну пусть размер файла увеличится даже на 10%. Ну и кого это волнует, если память измеряется мегабайтами? Лично мне всё равно сколько весит екзежник - метр, или метр и сто кб. Меня размеры экзежника не волновали даже на 40 метровом винте. Размер исполяемого файла обычно иного меньше размера всяких .dat файлов. А тебя так волнует размер исполняемого файла? Только честно.

Ты ж робокота юзаешь smile Например, размер класс файла получается меньше, чем соответствующего С файла.
Вообще размер исполняемого файла тоже по идее должен влиять на скорость исполнения, ведь он должен быть загружен в память?

Цитата(Mayk @ 30.6.2005, 06:09)
Во-вторых не всякая IDE сделает код для get и set функций. Ну объявятся они в .cpp, но return var; и var=value; всё равно придется писать руками.

Хорошо, можно написать ИДЕ которая будет из имени переменной и ее типа вставлять также эти ретурны и и var=value...

Когда что-то вводится в язык, в результате приходится больше писать, но ведь есть и какой-то выигрыш.
Возьмем васик - помнится в васике "подпрограммы" писались через указание заведомо большого номера строки, в конце стоял goto. В процедурных языках для таких целей используется функция или процедура. Писать больше (типы, название, вызов), зато такую процедуру можно реюзать много раз, тогда как бейсиковские "процедуры" были одноразовыми (тк гото всегда возвращал на одну и ту же строку).
Первоначально программы писались в один файл. Потом оказалось что при большом размере программы работать с ней становится невозможно, и появились модули. Модуль нужно писать - создавать файл, называть, писать хедер, труда больше но и выигрыш очевиден.
С ООП то же самое, ты пишешь больше кода, но это облегчает жизнь впоследствии. Если не делать гет/сет методы, то чтобы определить, кто и когда изменил переменную (паблик) нужно отрудиться. Если же указан даже "пустой" сет метод var=value, то чтобы определить кто вызвал метод, достаточно дописать туда две строки...

ПС Если на то пошло, то например в Intellij IDEA нажатием 2-3 кнопок можно создавать комменты, блоки (циклы, трай-кетчи, ифы, итп), методы, переходить к определению любого метода или переменной, просматривать сорсы любого класса включая библиотечные, и миллион разных вещей - и все это с 2-3 кнопками. По скорости написания любой процедурный язык обгонишь, как бы мало там не надо было писать. Потому я и говорю о том, что тех.средства не показатель.

Автор: Mayk 30.6.2005, 19:31
Цитата(Domestic @ 30.6.2005, 18:59)

Ну понятно, что такой метод будет вызываться медленнее, ну будет проигрыш по времени в данном случае на, скажем, секунду. Это на что-то влияет?

Да. Я так тестирую робота smile Если он будет тестроваться за 59 секунд, а не за минуту, то я буду рад.

Цитата(Domestic @ 30.6.2005, 18:59)
Опять, скорость компиляции при существующем железе будет практически неотличима. Я не могу на глаз отличить скорость gcc на Маке и jikes под виндой.

Gaim компилировался быстрее, чем sim. Хотя первый в архиве весил 5 метров, а второй 3. Но gaim написан на си, а сам на плюсах. Разумеется здесь еще очень сильно тормозят шаблоны, но gaim всё равно компилируется быстрее при большем весе smile Как, впрочем, и все си программы... Во-всяком случае не могу вспомнить прогу на сях, которая бы компилировалась столь же долго, как и аналогичная по объему и/или функциональности прога на плюсах.

Цитата(Domestic @ 30.6.2005, 18:59)
Например, размер класс файла получается меньше, чем соответствующего С файла.

Некорректное сравнение. .с - исходник. .class - откомпилированный исходник. Или ты про .java? Ну... Я вот сделал один в один.
364 байта против 364ех smile
Код

package sample;
import robocode.*;
public class MyFirstRobot extends Robot
{
    public void run() {
        while (true) {
            ahead(100);
            turnGunRight(360);
            back(100);
            turnGunRight(360);
        }
    }
    public void onScannedRobot(ScannedRobotEvent e) {
        fire(1);
    }
    public void onHitByBullet(HitByBulletEvent e) {
        turnLeft(90 - e.getBearing());
    }
}

Код

#include"robocode.h"
static robot r = {run, fire, onHit};
static void run()
{
    while(1){
        ahead(&r,100);
        turnGunRight(&r,360);
        back(&r,100);
        turnGunRight(&r,360);
    }
}
static void fire(ScannedRobotEvent* e){
    fire(&r,1);
}
static void onHit(HitByBulletEvent* e){
    turnLeft(&r,90 - e->getBearing());
}
robot* EXPORT init()
{
    return &r;
}


Цитата(Domestic @ 30.6.2005, 18:59)
Вообще размер исполняемого файла тоже по идее должен влиять на скорость исполнения, ведь он должен быть загружен в память?

Должен-то конечно должен, но сотня килобайт не большой объем для винта не сыплющего бэды. Кроме того у меня notepad.exe загружается когда за полcекунды, а когда и за секунду. Слишком много второстепенных факторов делают загрузку нескольких дополнтельных килобайт абсолютно незаметными. Пусть лучше программа загрузится не за 1,00 сек, а за 1,01 сек, но будет работать быстрее.
Цитата(Domestic @ 30.6.2005, 18:59)
Хорошо, можно написать ИДЕ которая будет из имени переменной и ее типа вставлять также эти ретурны и и var=value...

А можно в очередной раз обратиться за помощью к препроцессору. Он есть во всех сях. А ИДЕ не все любят. Например, я люблю vim+ctags+пару скриптов для создания классов и заголовочных файлов.
Код

#define __Property(var_t, __variable, get_func, set_func) \
private: var_t __variable; \
public: var_t get_func() const{return __variable;}
public: void set_func(var_t __new_value){__variable=__new_value;}
class Wheel{
__Property(double, m_weight, weight, setWeight)
};

Цитата(Domestic @ 30.6.2005, 18:59)
Возьмем васик - помнится в васике "подпрограммы" писались через указание заведомо большого номера строки, в конце стоял goto.

В конце стоял RETURN. А вызывалась "функция" посредством GO SUB. При некотором опыте код GO SUB 5000 был довольно читаем.
Например можно было разместить там чтение клавиши q/a/o/p/space.
Помню, у меня на спеки последние строки были
9998 STOP //брякпоинт
9999 RANDOMIZE USR 15616: REM: SAVE "program" //запись на дискету


Цитата(Domestic @ 30.6.2005, 18:59)
Когда что-то вводится в язык, в результате приходится больше писать, но ведь есть и какой-то выигрыш.

Итак, делаем вывод. ООП хорош для больших проектов, чьи системные требования относительно высоки. Теоретически можно написать столь же быстрое приложение на плюсах, как и на сях. Но реализация того же сишного быстродействия потребует большего времени, нежели на чистом си. Реюзабельный код можно писать на любом языке. ООП встаёт во всей своей красе при работе с большим кол-вом абстрактных данных(пример - построение GUI), в то время как при отстутствии абстракции рулит функционалка.

Автор: Domestic Cat 30.6.2005, 19:39
Цитата(Mayk @ 30.6.2005, 10:31)
Некорректное сравнение. .с - исходник. .class - откомпилированный исходник. Или ты про .java? Ну... Я вот сделал один в один.
364 байта против 364ех smile

Вообще-то я говорил про исполняемые файлы. Или ты имел в виду сорсы?

Цитата(Mayk @ 30.6.2005, 10:31)
В конце стоял RETURN. А вызывалась "функция" посредством GO SUB. При некотором опыте код GO SUB 5000 был довольно читаем.
Например можно было разместить там чтение клавиши q/a/o/p/space.
Помню, у меня на спеки последние строки были
9998 STOP //брякпоинт
9999 RANDOMIZE USR 15616: REM: SAVE "program" //запись на дискету

Забыл совсем smile

Цитата(Mayk @ 30.6.2005, 10:31)
в то время как при отстутствии абстракции рулит функционалка.

С предыдущим я согласен, но вот не пойму что значит количество абстрактных данных? Данные в любом случае - абстракция.

Автор: Mayk 30.6.2005, 22:00
Цитата(Domestic @ 30.6.2005, 20:39)
Вообще-то я говорил про исполняемые файлы. Или ты имел в виду сорсы?

Я имел в виду сорсы. А сравнивать виртуальную машину и реальную не совсем верно. Разве в .class'ы линкуются статичные либы?

Цитата(Domestic @ 30.6.2005, 20:39)
предыдущим я согласен, но вот не пойму что значит количество абстрактных данных?

Не совсем точно выразился. Переформулирую подробней:
В UI есть лабелы, кнопки, поля для редактирования текста, картинки, панели, меню, тулбары и так далее. И со всеми с ними можно выполнять множество одинаковых действий, как то их активирование, кликание на них, сброс в них(drop), изменение enable, показ, прятание, перемещение, изменение размера. Здесь ООП с виртуальным наследованием подойдет гораздо лучше. Во всяком случае не хочется писать что-нить типа того:
Код

memcpy(link_vtable, text_vtable, sizeof(*link_vtable));
link_vtable->clicked=link_clicked()
link_vtable->typeId=TYPE_LINK;

Плюснутое class Link : public Text{ void clicked(); } гораздо локаничнее. Сравнить:
Код

MainWindow : Dialog() {
    addChild(new Button(this,"quit"));
}

void MainWindow::hideAllButtons()
{
    for (Widget* iter = child; iter; iter=iter->next){
        Button* btn = dynamic_cast<Button*>(iter);
        if(btn) btn->hide();
    }
}

и
Код

typedef struct mainwindow_s{
    union{
    widget_t widget; 
    dialogwindow_t dialog;
    };
}mainwindow_t;

#define CREATE_WIDGET NULL

dialogwindow_t dialog_new(dialogwindow_t* dlg=CREATE_WIDGET);

mainwindow_t* mainwindow_new(mainwindow_t* win=CREATE_WIDGET)
{
    if(win==0){
        win = calloc(1,sizeof(*win));
        dialog_new(&win->dialog);
    }    
    win->widget.vtable = mainwindow_vtable;
    add_child((widget_t*)button_new(CREATE_WIDGET, win, "hello"))
    return win;
}

void mainwindow_hide_all_buttons(mainwindow_t* w)
{
    for (widget_t* iter = w->child; iter; iter=iter->next){
        if(iter->vtable->typeinfo == TYPE_BUTTON)
              iter->vtable->hide(iter); 
    }
}

Обрати внимание на преобразования типа в вызове add_child. Это напрягает. Ведь в функционалке нет преобразования типов, здесь отсутствует абстракция типов: нет отвлечния от действительного типа, есть лишь преобразования типа вручную.
И это преобразование потребуется в каждом вызове add_child, remove_child, is_child_exist, set_parent, при каждом обращении к виртуальной функции. В GUI много абстракции. По сути всё GUI - абстракция.

Автор: Domestic Cat 1.7.2005, 07:25
Цитата(Mayk @ 30.6.2005, 13:00)
Обрати внимание на преобразования типа в вызове add_child. Это напрягает. Ведь в функционалке нет преобразования типов, здесь отсутствует абстракция типов: нет отвлечния от действительного типа, есть лишь преобразования типа вручную.
И это преобразование потребуется в каждом вызове add_child, remove_child, is_child_exist, set_parent, при каждом обращении к виртуальной функции. В GUI много абстракции. По сути всё GUI - абстракция.

Разве такая ситуация редка или специфична для гуя? По-моему, нет.

Автор: Mayk 1.7.2005, 15:46
Цитата(Domestic @ 1.7.2005, 08:25)
специфична для гуя

ГУЙ был взят в качестве примера.

Автор: December 1.7.2005, 17:05
Пардон, что вклиниваюсь, появилась ли какая-нибудь технология (идеология?), конкурирующя с ООп за последние лет 10?

Автор: Дрон 1.7.2005, 17:33
Цитата(December @ 1.7.2005, 18:05)
конкурирующя с ООп

Ну... В каких областях конкурирующая?

Вон, для научных целей до сих Fortran основным языком считается... А там ООП и близко нет smile

Автор: simanyay 1.7.2005, 17:46
Цитата(December @ 1.7.2005, 19:05)
Пардон, что вклиниваюсь, появилась ли какая-нибудь технология (идеология?), конкурирующя с ООп за последние лет 10?


Не знаю, насколько это верно, но по моему АОП (Аспектно Ориентированное Программирование)

Автор: Дрон 1.7.2005, 17:47
simanyay
И о чём там?

Автор: simanyay 1.7.2005, 18:24
Цитата
И о чём там?


Об аспектах :-) Я сам толком не знаю, только научно-популярные статьи читал пару раз. Как мне показалось, что-то вроде триггеров в СУБД. Т.е. у любого метода есть пред и пост условия (или действия).

Автор: Дрон 1.7.2005, 18:33
simanyay
Было бы интересно посмотреть.

Ведь я согласен с December, что ООП это очень естественная вещь. Мне в нём не нравится только пораждаемый им фанатизм и нежелание понимать, что всё что мы делаем -- мы делаем для пользователя и ему действительно плевать на то, какой мы использовали подход smile

Автор: Domestic Cat 1.7.2005, 18:37
Цитата
Мне в нём не нравится только пораждаемый им фанатизм


Как раз фанатизма хватает "с другой стороны" smile Я видел сайт (на него Джоэл ссылку дает), так там ООП сравнивался с коммунизмом.
Цитата
что всё что мы делаем -- мы делаем для пользователя и ему действительно плевать на то, какой мы использовали подход

Многое делается и для программиста, те же библиотеки например. Когда они сделаны через одно место, то тут не все равно smile

Автор: simanyay 1.7.2005, 18:37
Цитата
что всё что мы делаем -- мы делаем для пользователя и ему действительно плевать на то, какой мы использовали подход smile


Ну тут я с тобой не согласен. Пользователь пользователем, а о себе тоже думать надо. А скорее всего именно ты и будешь поддерживать твой же код.

Автор: Дрон 1.7.2005, 18:45
Цитата(simanyay @ 1.7.2005, 19:37)
Ну тут я с тобой не согласен.

Твоё право smile

Цитата(simanyay @ 1.7.2005, 19:37)
А скорее всего именно ты и будешь поддерживать твой же код.

Надо уметь проектировать расширяемость. ООП тут не причём.

Я сейчас на С++ не пишу... Но какой же это кайф, после всего этого C# и пр. взять и наваять какую-нить мелкую утилитку на Си++ с его указателями и небезопасностью...

Автор: Domestic Cat 1.7.2005, 18:50
Цитата
Но какой же это кайф, после всего этого C# и пр. взять и наваять какую-нить мелкую утилитку на Си++ с его указателями и небезопасностью...


Кому как...
Добавлено @ 18:52
Цитата

Надо уметь проектировать расширяемость. ООП тут не причём.

ООП код легче поддерживать, чем не-ООП код.

Автор: En_t_end 1.7.2005, 18:52
"наваять какую-нить мелкую утилитку на Си++ с его указателями и небезопасностью..."
Последнее зависет от рук.

Автор: Дрон 1.7.2005, 18:56
Цитата(Domestic @ 1.7.2005, 19:50)
ООП код легче поддерживать, чем не-ООП код.

Я уже приводил пример, как мне пришлось в воскресенье ехать на работу, чтобы добавить доступ к одному из полей класса, которое, естественно, было protected.

Легче, говорите? smile

Автор: Domestic Cat 1.7.2005, 19:02
Цитата
Я уже приводил пример, как мне пришлось в воскресенье ехать на работу, чтобы добавить доступ к одному из полей класса, которое, естественно, было protected.

Легче, говорите? smile

При чем тут поддержка кода? Это непродуманная архитектура. Если бы случайно потер написанный за день код - ты бы тоже стал обвинять ООП?

Автор: Дрон 1.7.2005, 19:07
Domestic Cat
Это непредвиденные обстоятельства smile

Кто ж мог знать, что впоследствии понадобиться внутренее состояние класса узнавать снаружи.
Добавлено @ 19:10
Нехочу я тут спорить... Будет время -- напишу про ООП в другом ключе. Повеселее smile

Автор: Дрон 1.7.2005, 21:43
Не... Чего-то у меня совсем мысля не идёт. В общем-то оно и понятно. Как ни крути ООП -- вещь полезная и на данном этапе его недостатки воспринимаются как неизбежность.

Domestic Cat
Кстати, твой первый пост о недостатках процедурного программирования тоже можно списать на непродуманную архитектуру.

Я в спорах во Флейме больше не участвую smile smile

Автор: December 1.7.2005, 22:23
OOP vs Procedural - не шибко захватывающее зрелище. Приелось. Вроде больше соревноваться не кому и не с кем... Почитал про АОП - вапще ничё не понял smile Кто-нить может объяснить его преимущество кратко но доступно? smile

Автор: Domestic Cat 1.7.2005, 23:30
Цитата(December @ 1.7.2005, 13:23)
Почитал про АОП - вапще ничё не понял smile Кто-нить может объяснить его преимущество кратко но доступно? smile

Я слышал, и тоже не понял. Говорят что круто, а времени разобраться нет.

Автор: Earnest 12.7.2005, 19:45
Аспектно-ориентированное проектирование - это вовсе не альтернатива ООП, а в некотором смысле его дальнейшее развитие. Аспекты - это как бы запчасти, из которых можно собирать объекты-компоненты. Например, пишем умный указатель. Кроме автоматизации управления памятью мы можем захотеть варьировать следующие "аспекты": владение хранимым объектом - будет ли это у нас что-то типа auto_ptr или указатель с подсчетом ссылок; потокобезопасность для подсчета ссылок - будем обеспечивать или нет; допустимо ли присваивание с приведением downcast; кроме того, можно запихать в smart-указатель какой-нибудь другой ресурс... Это пример из Александреску (по памяти, за точность не ручаюсь). Некоторые авторы называют АОП поперечным или горизонтальным, в отличие от ООП - вертикального. Другие возможные аспекты: обработка ошибок, поддержка транзакций, синхронизация ... все это не может быть выражено классическими объектами, а является просто кусками кода. На С++ эможно писать мелкие классы-примеси с заданным интерфейсом (статическим или динамическим, по задаче), из которых собираются классы-объекты.
В общем, ничего супер неожиданного; просто некоторое обобщение того, что все и так вроде знают.
А во многих книгах да, мутно описано.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)