vvoid |
Отправлено: 11.04.2005, 15:15 |
|
Машинист паровоза
Группа: Участник
Сообщений: 171
|
Не раз встрчал на форуме: выражение "парень, а у тебя качественный код". Смотрел я этот код, ну и вообщем-то ничего особенного я там не видел, разве что всегда была туева хуча коментариев. Такое впечатление, что качество кода оценивают наличием коментариев. Я сейчас не говорю про какие-то программерские сверх навороты и придерживаюсь мнения, что и Hello word можно написать красиво! Вот у меня и возник вопрос, а как Вы оцениваете качество (красоту, понятность, реюзабельность ) кода?
Спасибо за ответы, уверен, что будет интересно почитать разные мнения разных прораммистов.
|
|
Konstantine |
Отправлено: 11.04.2005, 16:05 |
|
Мастер участка
Группа: Модератор
Сообщений: 545
|
для меня — в первую очередь — это отступы, т.к. без них — это похоже на молитву — Бог (проц) поймёт, а окружающие — пофиг.
затем — понятливость алгоритмов — если используются заумные нестандартные алгоритмы, то нужно их краткое описание (в т.ч. комментариях)
немаловажны названия переменных — типа для счётчиков обычно используются i, j,k; для указателей — p... и т.д.
Функции также требуют внимания — если из названия функции и входных переменных непонятно что нужно в качестве входных переменных и что она с ними делает (извините не смог описать проще ) то это должно быть описано (кратко и понятно)
кстати — если используются неклассические формы (ну типа ++q=p++ или сложнее) — это тоже требует быть описаным
кстати эти все требования я стараюсь сам для себя соблюдать — зато когда открыл проект через год — всё сразу не вспомнил а изучил
|
|
vvoid |
Отправлено: 11.04.2005, 16:43 |
|
Машинист паровоза
Группа: Участник
Сообщений: 171
|
Отступы — без сомнения. Нужна строгая система, которая прослеживается на протяжении всего проэкта. (классное сравнение с молитвой )
Описание нестандартных алгоритмов — согласен.
Названия функций и переменных — должны по-возмжности исключать необходимость коментариев. Считаю, что даже временные переменные надо называть полными именми.
Ещё желательно, что бы был указан тип переменных, ведь int это не unsigned int , где что используется, там то и должно быть.
Пусть функция имеет название хоть в 60 символов, но описывает свои действия (у меня такие функции встречались).
А вот использование нестандартних конструкций... Я не приветствую, пусть лучше всё будет записано в несколько строк, и разделено скобками, но будет понятно, какое действие за каким делать. (не то что бы я не мог разобрать, что делается при ++q=p++, но ведь могут быть и покруче накрутки). Оптимизатор себе отоптимизирует как надо, и количество строк сейчас принципиальное значение не имеет, зато понятно и приятно
|
|
Sl@Sh |
Отправлено: 11.04.2005, 17:33 |
|
Мастер участка
Группа: Участник
Сообщений: 383
|
Если все, кто читают код всё (или почти всё) понимают, то этот код можно назвать просто читаемым (читебельным).
А качественный код включает в себя много всего. Например грамотность оформления подсказок, высококачественные алгоритм и.т.п.
Алгоритмы вообще важная штука. Вот например простой алгоритм, который меняет местами переменные :
1)
CODE |
int a = 15, b = 20, c;
c = a;
a = b;
b = c;
|
2)
CODE |
int a = 15, b = 20;
a = a + b;
b = a — b;
a = a — b;
|
Разница в том, что первый случай обходит создание новой переменной.
|
|
Георгий |
Отправлено: 11.04.2005, 23:21 |
|
Почетный железнодорожник
Группа: Модератор
Сообщений: 874
|
ещё один критерий — отсутствие дублирования, высокое повторное использование кода.
например алгоримт обмена значений 2х переменных должен быть реализован так swap( a, b ); т.е. изобретать велосипед не стоит.
чёткость и непротиворечивость комментариев.
цикломатическая сложность — структурная сложность программного блока по вычислительным циклам. рекомендуется считать, что "цена" оператора вне цикла = 1, внутри цикла = 10, а внутри вложенного цикла = 100. и так далее.
число строк кода на функцию. например я не люблю функции длинее 20-30 строчек. если они длинее, то значит, что две и более логических функций были объединены в один кусок кода.
а вообще SQAP, стандарт IEEE оценивает качество, как результат "опроса" по списку контральных вопросов с вынесением относительных оценок и сравнении со средними по фирме. |
|
Guest |
Отправлено: 12.04.2005, 10:00 |
|
Не зарегистрирован
|
"оно" как минимум должно "работать не хуже заданного". Чесно говря за 11 лет не встречал системы более менее сложной которую не хотелосьбы переписать. Всегда есть таракан!
Это собственно про то что этим кодом реализуется, а вот про сам код слово красиво наверно больше про вкусы...как с женщинами. |
|
Asher |
Отправлено: 12.04.2005, 13:48 |
|
Мастер участка
Группа: Модератор
Сообщений: 550
|
Если для исправления какого-то таракана надо переписать всю систему...
Лучше с таким кодом не встречаться.
|
|
vvoid |
Отправлено: 12.04.2005, 15:36 |
|
Машинист паровоза
Группа: Участник
Сообщений: 171
|
Георгий затронул интересный ряд: "отсутствие дублирования, высокое повторное использование кода", позволю себе добавить следующее звено: Модульность
Сам себя часто ловлю на том, что это я где-то уже писал... . Терпеть не могу такме моменты. БРРР. Считаю, что про повтоное использование действительно хорошей функции(й) надо задумаваться сразу, объединять сходные по смыслу, выполняющимся заданиям функции в отдельные модули и когда есть уверенность в действительно качественной и безошибочной реализации "куска кода" можно собирать библиотеки (или библиотечки ;-) ).
Я не с проста упомянул про качественную и безошибочную реализацию, поскольку во многих случаях, открывая свою когдашнюю (прошлую) реализацию чего либо, у меня появляется желание написать это заново — знаний и навыков то ведь добавилось! Наверняка каждый сталкивался с ситуацией, когда до конца понимаешь, что ты делаешь уже после того, как довёл это до какого-либо стабильного состояния. Не зря японцы в несколько раз больше времени тратят на общумаывание решения задачи, чем на рализацию этого решения (может не самым лучшим образом сформулировал предложение — извиняйте )
|
|
62316e |
Отправлено: 12.04.2005, 15:45 |
|
Дежурный стрелочник
Группа: Участник
Сообщений: 38
|
От скажыте как будет лутше?
1)CODE | int a=50;
int b=10;
int c=a+b; |
2) CODE | int a(50);
int b(10);
int c(a+b); |
Или в даном случае "лутше" не причем?
|
|
Asher |
Отправлено: 12.04.2005, 17:29 |
|
Мастер участка
Группа: Модератор
Сообщений: 550
|
Лучше по коду или оформлению?
По генерируемому коду они абсолютно идентичны.
Или это простой пример общего случая?
Тогда будет ли между этими варантами разница зависит от специфических свойств конкретного типа
|
|
62316e |
Отправлено: 12.04.2005, 18:23 |
|
Дежурный стрелочник
Группа: Участник
Сообщений: 38
|
QUOTE | Лучше по коду или оформлению? | По коду.
QUOTE | По генерируемому коду они абсолютно идентичны. | Нет.
QUOTE | Или это простой пример общего случая? | В даном случее тут есть конструктор и если его зделали то не просто так!???
|
|
vvoid |
Отправлено: 12.04.2005, 19:12 |
|
Машинист паровоза
Группа: Участник
Сообщений: 171
|
QUOTE | В даном случее тут есть конструктор и если его зделали то не просто так!??? |
А зачем он здесь сделан? (если это задумка программиста).
Если суть вопроса в том, зачем это вообще было добавлено в С++, то, возможно, это можно объяснить тем, что С++ — типа объектно-ориентированный язык, значит все переменные в нём должны рассматриваться как объекты неких классов. Класс, как известно, должен всегда находиться в некотором стабильном, чётко определённом, состоянии (инвариантность классов), для этого и был придуман конструктор, который должен вызываться только в момент создания объекта любого класса. Получается, что инициализация переменных, которая в С делалась таким образом:
CODE |
int a=50;
int b=10;
int c=a+b; |
, в С++, для придерживания принципов объектно-ориентированного программирования, должна проводится так:
CODE |
int a(50);
int b(10);
int c(a+b); |
PS*
Хотя мне лично больше нарвится так:
CODE |
int a;
int b;
int c;
a = 50;
b = 10;
с = a + b;
|
Я считаю, что по возможности объявление переменных должно быть отдельно от логики программы, разумеется с классами дела обстоят по-другому, но я создание объктов классов всегда визуально отделяю от объявления простых переменных пустой строкой — получается чисто и опрятно .
Отредактировано vvoid — 12/04/2005, 19:14
|
|
62316e |
Отправлено: 12.04.2005, 20:30 |
|
Дежурный стрелочник
Группа: Участник
Сообщений: 38
|
QUOTE | ...пустой строкой — получается чисто и опрятно | Выше пишеш одно делаеш другое! Если тебе нравитса Паскаль пишы как в Паскале...
|
|
Георгий |
Отправлено: 12.04.2005, 21:47 |
|
Почетный железнодорожник
Группа: Модератор
Сообщений: 874
|
возможно я чтото не понимаю, но причём тут ООП?
C++ в отличии от C имеет:
механизм полиморфного поведения (классы)
механизм упрощенной обработки ошибок (исключительных ситуаций)
механизм метапрограммирования — шаблоны (похоже в последние 2-3 года наиболее активно развивается именно это направление)
+ такая мелочь как улучшенный контроль преобразования типов
и всё это только для снижения интеллектуальной сложности кода, вернее не только кода, а структуры ПО и соответствующему ей кода.
таким образом, с точки зрения качества структуры и кода, использование ООП или процедурного программирования, или непроцедурного (например язык программирования потока управления) не имеет никакого значения.
качество от инструмента не зависит. от инструмента зависит трудоёмкость.
Отредактировано Георгий — 12/04/2005, 21:51 |
|
** Harold |
Отправлено: 13.04.2005, 00:49 |
|
Не зарегистрирован
|
Типы, ООП, и т.д.
НО, знакомые очень"волосатые"(без преувеличения) программеры на MS VISUAL'e пишущие с "закрытыми" глазами (надеюсь, будет мало возражений по поводу кашерности, тем паче, что и мне, как дятлу, Builder ближе, но и мой Борландовый интерфейс (все остальное — фигня — "встречают по одежке" ) скрипя зубами принимают и даже пишут под него СОМ-объекты, ну чтоб с MS'ом потом личных проблем поменьше иметь...
Ну, так я о качестве кода — все типы описываются ВСЕГДА максимально понятно — iInteger, fFloat, bBoolean, dwDoubleWord и т.д...
Даже мне, дятлу, мало понимающему в современном программировании понятно (типа, Enlish ещё в школе вдолбили)... Пример (не мой)
CODE |
OxidePlant1->InsertHardwareBlock(0x7042,0x0C,(IUnknown**)&m_pBlock7042_C);
notifyBlock7042_C.OnIsWorkedEvents = TForm1::OnIsWorkedEvents;
notifyBlock7042_C.Connect( m_pBlock7042_C );
// создаем DigitalOut выходы 0-13
m_pBlock7042_C->InsertOxideTemperatureController(
402, // идентификатор устройства
0, // задействованы выходы начиная с DigitalOut_0
14, // Всего выходов
(IUnknown**)&m_pTempCtrl_C );
m_pTempCtrl_C->AddRef();
notifyAnalogIn.OnAlarmEvents = TForm1::OnLogDevAlarmEvents;
notifyAnalogIn.OnInfoAnalogEvents = TForm1::OnLogDevInfoAnalogEvents;
notifyAnalogIn.OnInfoDigitalEvents = TForm1::OnLogDevInfoDigitalEvents;
notifyAnalogIn.Connect( m_pTempCtrl_C );
m_pTempCtrl_C->SetFeedBack( m_pTermocouple2 );
|
Все, конечно, красиво и ООП-кашерно, но мне, лично, через if-else проще, и работает не хуже... Ну, или учиться под MS писать или все тоже самое по простому — топориком — компы-то нынче ого-го. Ну и пусть себе Борландовые обертки мой бред "разворачивают"... зато мне понятнее. НЕ ХОЧУ Я API в полном объеме и сразу! Поспорьте, может, я и не прав (но цель-то достигнута — все работает — не хочу я пока MS — слишком много заморочек и СРАЗУ. Не желаю продираться через ЧАЩУ всякой синтаксической ПУРГИ)...
К чему всё это?
Да так... наверное, от моего НЕУМЕНИЯ... |
|
** Harold |
Отправлено: 13.04.2005, 01:11 |
|
Не зарегистрирован
|
Н-да, у Борланда, с редактором(!) — БЕДА... было все КРАСИВО... Но, сама их идея мне нравится — дятлам ЛЕГКО...
PS. Ну, про хелп, вообще, молчу.С MSDN'a, чтоль, пример взяли бы...
Зачем неплохую идею так изгадили...?! (в сердцах...) |
|
Asher |
Отправлено: 13.04.2005, 08:25 |
|
Мастер участка
Группа: Модератор
Сообщений: 550
|
QUOTE |
По генерируемому коду они абсолютно идентичны.
Нет.
|
А в отладчике посмотреть?
Я же уточнил именно этот случай кодя — или это просто пример более общего случая использования?
Но даже в общем случае если это класс-тип с явно объявленным конструктором, то в обоих случаях просто вызовется этот конструктор. Разницы не будет.
Проверено отладчиком.
|
|
Sl@Sh |
Отправлено: 13.04.2005, 09:44 |
|
Мастер участка
Группа: Участник
Сообщений: 383
|
Млять, забыл файл присоеденить, жаль в редактировании нельзя
|
|
vvoid |
Отправлено: 13.04.2005, 15:19 |
|
Машинист паровоза
Группа: Участник
Сообщений: 171
|
QUOTE (62316e @ 12/04/2005, 20:30) | QUOTE | ...пустой строкой — получается чисто и опрятно | Выше пишеш одно делаеш другое! Если тебе нравитса Паскаль пишы как в Паскале... |
То ли ты меня не розумієш, то ли я тебя.
Паскаль мне, кстати, не нравится.
На сколько я понял, ты имеешь ввиду, что я писал:
QUOTE | в С++, для придерживания принципов объектно-ориентированного программирования, должна проводится так:
CODE |
int a(50);
int b(10);
int c(a+b); |
|
а сам привёл в корне другой пример! Скажи, ты всегда знаешь нужное значение переменной при её объявлении?
И ещё заметь, С++ именно типа объектно-ориентированный язык! И вообще, кто тебе сказал, что объектно-ориентированное программирование всегда хорошо!!??
PS* если я начал разглагольствовать не по той теме, которую ты имел ввиду — пиши — я прокоментриую!
|
|
vvoid |
Отправлено: 13.04.2005, 15:38 |
|
Машинист паровоза
Группа: Участник
Сообщений: 171
|
2Harold
Не влазя в логику:
CODE |
#define DeviceID (402)
#define OutputPinStartIndex (0)
#define OutputPinTotalCount (14)
OxidePlant1->InsertHardwareBlock(0x7042,
0x0C,
(IUnknown**)&m_pBlock7042_C);
notifyBlock7042_C.OnIsWorkedEvents = TForm1::OnIsWorkedEvents;
notifyBlock7042_C.Connect(m_pBlock7042_C);
// создаем DigitalOut выходы 0-13
m_pBlock7042_C->InsertOxideTemperatureController
(DeviceID,
OutputPinStartIndex,
OutputPinTotalCount,
(IUnknown**)&m_pTempCtrl_C);
m_pTempCtrl_C->AddRef();
notifyAnalogIn.OnAlarmEvents = TForm1::OnLogDevAlarmEvents;
notifyAnalogIn.OnInfoAnalogEvents = TForm1::OnLogDevInfoAnalogEvents;
notifyAnalogIn.OnInfoDigitalEvents = TForm1::OnLogDevInfoDigitalEvents;
notifyAnalogIn.Connect(m_pTempCtrl_C);
m_pTempCtrl_C->SetFeedBack(m_pTermocouple2);
|
Если бы я ещё знал, что такое 0x7042, 0x0C — объявил бы эти числа в виде констант, чтоб всем было понятно, да чтоб сам мог разобраться, если через годик-два понадобится вспомнить, а что что я тут делал? :-)
По-моему с таким кодом легче работать, хотя это дело вкуса...
Harold, я помню, что код не твой, поэтому не принимай мои замечания на свой счёт.
Народ, давайте поэкспериментируем с этим куском кода. Что с ним ещё можно сделать и как вам мой вариант?
Отредактировано vvoid — 13/04/2005, 15:40
|
|
FataLL |
Отправлено: 16.04.2005, 08:45 |
|
Дежурный стрелочник
Группа: Участник
Сообщений: 37
|
Все-таки сделал бы const int вместо #define и еще не нравятся мне эти подчеркивания... Хотя мне и TForm1 не нравится. Если этот m_pxxxx_C наследник от TForm1 не ясно, зачем эти присваивания. А если не наследник, то почему бы его им не сделать. Или написать методы:
CODE |
TForm1::SetEvents( TNotifyBlock& notifyBlock );
TForm1::SetEvents( TNotifyAnalog& notifyAnalog );
|
и делать там все присвоения. Тогда бы это все стало выглядеть еще короче:
CODE |
const int cnDeviceID = 402;
const int cnOutputPinStartIndex = 0;
const int cnOutputPinTotalCount = 14;
OxidePlant1->InsertHardwareBlock(0x7042,
0x0C,
(IUnknown**)&m_pBlock7042_C);
TForm1::SetEvents( notifyBlock7042_C );
notifyBlock7042_C.Connect(m_pBlock7042_C);
// создаем DigitalOut выходы 0-13
m_pBlock7042_C->InsertOxideTemperatureController
(DeviceID,
OutputPinStartIndex,
OutputPinTotalCount,
(IUnknown**)&m_pTempCtrl_C);
m_pTempCtrl_C->AddRef();
TForm1::SetEvents( notifyAnalogIn );
notifyAnalogIn.Connect(m_pTempCtrl_C);
m_pTempCtrl_C->SetFeedBack(m_pTermocouple2);
|
|
|
Георгий |
Отправлено: 16.04.2005, 11:36 |
|
Почетный железнодорожник
Группа: Модератор
Сообщений: 874
|
переменные и методы конеч то же хорошо названы, но у меня до сих пор есть вопрос — "что же делает этот кусок кода".
в начале можно написать:
1. краткое описание — например: "блок чтения температуры с датчиков используется COM объект"
2. т.к. заголовка функции не увидел, то перечень использованных глобальных объектов
3. по простенькому комментарию-памятке к каждому вызову функции |
|
Sl@Sh |
Отправлено: 16.04.2005, 18:55 |
|
Мастер участка
Группа: Участник
Сообщений: 383
|
Интерестно, кто как считает, какой из этих вариантов лучше :
1) void Func(const int &a);
2) void Func(int a);
Оба случая поидее недопускают изменения переменной, которая передана в функцию. Однако первый случай не копирует переменную.
Какой вариант грамотней, если переменную менять не нужно ?
|
|
FataLL |
Отправлено: 17.04.2005, 12:50 |
|
Дежурный стрелочник
Группа: Участник
Сообщений: 37
|
Если переменную менять не надо вообще (даже в функции не допускается изменений), то грамотнее:
CODE |
void Func( const int& a )
{
a++; // здесь ошибка на этапе компилирования
}
|
Второй вариант не меняет передаваемый параметр, но допускает его изменение в теле функции:
CODE |
void Func( int a )
{
a++; // ошибки нет, это допускается
}
|
|
|
Sl@Sh |
Отправлено: 17.04.2005, 17:59 |
|
Мастер участка
Группа: Участник
Сообщений: 383
|
to FataLL
Вы фактически повторили мои слова. Вопрос я задал потому, что видел много функций, в которых параметр передавался по значению, но в функции не менялся.
|
|
FataLL |
Отправлено: 18.04.2005, 00:03 |
|
Дежурный стрелочник
Группа: Участник
Сообщений: 37
|
to Sl@Sh
Я не повторял ничего. Я сказал, что это совершенно разные варианты описания функций. И применяется этот модификатор const для отлова потенциальных ошибок еще на этапе компиляции.
Отредактировано FataLL — 18/04/2005, 00:04
|
|
Guest |
Отправлено: 18.04.2005, 09:58 |
|
Не зарегистрирован
|
QUOTE (Sl@Sh @ 16/04/2005, 18:55) | Интерестно, кто как считает, какой из этих вариантов лучше :
1) void Func(const int &a);
2) void Func(int a);
Оба случая поидее недопускают изменения переменной, которая передана в функцию. Однако первый случай не копирует переменную.
Какой вариант грамотней, если переменную менять не нужно ? |
Только первый вариант и есть "грамотный",
а второй — просто — распространенный |
|
Asher |
Отправлено: 18.04.2005, 10:48 |
|
Мастер участка
Группа: Модератор
Сообщений: 550
|
QUOTE | Только первый вариант и есть "грамотный",
а второй — просто — распространенный |
То не все так очевидно, с выбором "грамотности" вариантов.
Расширяя пример как
1) void Func(const T &a);
2) void Func(T a);
можно заметить, что T-может иметь размер и меньше размера ссылки, что может быть существенно, например при глубокой рекурсии, а во вторых в многопоточном приложении, я лично, предпочел бы поработать с один раз сделанной копией значения, чем непосредственно несколько раз обращаться к этому значению в функции.
В третих применив fastcall, для второго случая, вы вообще можете избавиться от лишних операций по доступу к памяти, особенно при inline.
Отредактировано Asher — 18/04/2005, 11:55
|
|
FataLL |
Отправлено: 18.04.2005, 14:39 |
|
Дежурный стрелочник
Группа: Участник
Сообщений: 37
|
Правильно, универсального решения нет. Так и должно быть. Все зависит от конкретного случая. Вопрос в том, что понимать под "грамотностью". Я говорил с точки зрения безопасности. Если ошибка всплывает на этапе компиляции — это сохраняет время и нервы по отлову скрытых багов в будущем.
|
|
Asher |
Отправлено: 18.04.2005, 14:45 |
|
Мастер участка
Группа: Модератор
Сообщений: 550
|
Согласен.
В таком варианте void Func(const T a);
компилятор выругается аналогично void Func(const T &a);
|
|