уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.

Наверное нет такого программиста или любителя, который не мечтал бы поучаствовать в каком-нибудь большом и важном проекте. Или разработать такую программу, которая была бы полезна не только ему одному, но и, как минимум, знакомым и друзьям. И вот такой момент наступает. Свершилось — пригласили поучаствовать в проекте, или нашел-таки свою идею, такую, которую до тебя ещё никто не пробовал реализовать в виде машинного кода.

Ты рвешься «в бой», руки так и тянутся к клавиатуре, что бы начать работать на программой. Не торопись, дорогой начинающий программист. Не забывай об одном — всегда есть тонкие моменты в программировании, которые весьма не часто можно встретить подробно описанными в литературе на основании которой мы становимся теми кем стремимся стать ежедневно — профессионалами.
Не скажу, что я добился своей цели в программировании и стал профессионалом. Нет. До уровня профессионала мне ещё расти и расти. Однако опыт поспешных разработок имеется и хотелось бы поделиться с Вами теми моментами, из-за которых мне сейчас приходится усердно перелопачивать тысячи строк кода. Итак, начнем по порядку.

1. Комментарии

В явном виде комментарии ни коим образом не касаются работоспособности Ваших разработок. Но представьте себе на момент, что программа может занимать не одну, не две, а сотни тысяч строк?
Начинающий программист может смело сказать: «У меня хорошая память. В любой момент открою исходник и вспомню если не все, то основные моменты». И это первая и грубая ошибка при работе над большими проектами. Можно помнить сотню строк один день, два, неделю. Но потом всё забудется. Свойств памяти не изменить. Поэтому не ленитесь — пишите комментарии как можно чаще, комментируйте каждый важный момент, комментируйте каждую процедуру и функцию. Это займет времени не так уж и много, а сил и времени в будущем сэкономит на порядок больше. Не могу не процитировать одного из программистов:

вовремя написанный и содержательный комментарий в коде, это — не только строчки и объём созданного тобой приложения, но и возможность быстро вспомнить какого волосатого ты наваял здесь две недели назад, и почему оно вообще работает.

Так что не ленитесь — экономьте свое время. Оно вам ещё понадобиться на отлов и исправление багов :)

2. Обработка исключений

Второй момент про который можно забыть в порыве страсти, т.е. в момент напряженной работы. Помните — если программа работает у Вас — это совершенно не означает того, что она будет также складно работать у всех. Иногда встречаются такие индивиды, что диву даешься как они умудряются сделать так, что программа начинает злостно выкидывать окошки с красными крестами и кучей непонятных букв. А все дело в том, что программист вовремя не обработал исключение, поленился, забыл и т.д.
У Delphi, Lazarus есть достаточное количество средств и возможностей для того, чтобы обработать любое исключение. Например, как можно обработать такой код:

procedure AnyProc;
var a: currency;
begin
  [...]
  a:=StrToCurr(Edit1.Text);
  [...]
end;

Казалось бы зачем что-то обрабатывать — всё и так сработает. Сработает, но только не всегда. Лучше лишний раз перестраховаться и написать:

procedure AnyProc;
var a: currency;
begin
  try
    [...]
    a:=StrToCurr(Edit1.Text);
    [...]
  except
    //обрабатываем исключительную сиуацию
  end;
end;

Так мы избежим ситуации, когда пользователь введет в Edit не число или неправильно использует разделитель целой и дробной части числа. Вывод сообщения об ошибке должен быть только в том случае, когда пользователь может принять действия по решению проблемы, в остальных случаях программа должна обработать исключения.
Второй вариант, помогающий избежать ошибки — в случае «неправильной» строки для преобразования, присваивать переменной значение по умолчанию:

procedure AnyProc;
var a: currency;
begin
    a:=StrToCurrDef(Edit1.Text,0);
end;

Но этот вариант лучше часто не использовать, или использовать, но с дополниельным сообщением для пользователя.
Второй пример. Есть у вас свой тип данных, возьмем к примеру такой класс и переменную:

type
  TMyClass = class
    FField1: integer;
    FField2: integer;
   ....
  end;
var MyClass: TMyClass;

Никогда не будет лишним сделать такую проверку

  if Assigned(MyClass) then
    begin
       //значит можно работать с переменной
    end;

Таким образом мы избежим нелепых ситуаций, когда переменная ещё не создана, а мы уже пытаемся в неё что-то записать.
Не отходя далеко от темы классов, скажу также, что не стоит забывать о своевременном освобождении ресурсов компьютера. Конечно, современные компьютеры обладают достаточно мощными ресурсами, но и они не резиновые. Например, ту же переменную MyClass можно уничтожить так:

try
  [....]
  MyClass.FField1:=1;
  [....]
finally
   FreeAndNil(MyClass) //переменная больше не нужна - можно освободить память
end;

Следующий момент, касающийся исключений — их корректное описание для пользователя. Обычно в спешке или по природной лени сообщение об ошибке получается очень не информативным. Думаю, пользователь или тот, кто будет осуществлять поддержку Вашего проекта, вряд ли будет в восторге от сообщений типа; «Опа! Что-то тут не то. Пеши правЕльно!» или «Ошибка №100500 в строке 8762» и т.д. Старайтесь давать пользователю именно полезную информацию, а не констатируйте факт об ошибке. Для этого удобно использовать классы исключений. Например, пусть у вас в блоке try…except…end выполняются следующие операции:
преобразование строки в число
работа с классом
ну и не будем долго мудрить — печать документа
логично обработать три типа исключений таким образом:

try
  [...] //осуществляем операции
except
  on e: EConvertError do
    begin
       //произошла ошибка преобразования типов
    end;
  on e: EAccessViolation do
    begin
       //произошла ошибка при обращении к ещё не созданной переменной
    end;
  on e: EPrinter do
    begin
       //ОС сообщила об ошибке принтера
    end;
end;

И так логично, понятно и доходчиво объясняем пользователю суть возникшей ситуации.

3. Работа со строками

Здесь можно второпях наловить не одну сотню проблем.
Во-первых, если вам необходимо сравнить две строки — не забывайте приводить их к верхнему или нижнему регистру.
Во-вторых, если вам необходимо делать проверку на наличие в строке печатных символов — никогда не делайте так:

if Length(str)=0 then

Помните, что всегда есть возможность записать в строку 1 пробел и ваше условие вернет true. Здесь, кстати, можно воспользоваться модулем StrUtils для работы со строками, например функцией Trim, убирающей лишние пробелы в начале и конце строки:

if Length(Trim(str))=0 then

В-третьих, избегайте изобретения велосипеда. Понятно, что в основном те, кто пишет на Delphi — выходцы из Pascal и там не хватало очень много полезных функций по работе со строками, например функций по перевороту строки или замене подстрок. И теперь, мы по старой доброй памяти волокём в Delphi уйму хитроумных процедур, функций, которые якобы не предусмотрены. Если ваше приложение завязано на работе с текстом, строками — не забывайте, что всегда под рукой есть модуль StrUtils где есть достаточно много функций по поиску подстрок, по очистке строк и замене подстрок. Зачем городить огород и забивать модули никому не нужным кодом? Используйте все прелести Delphi — берите готовое.
Кстати, для работ с датами также есть свой модуль — DateUtils.

4. Работа с числами. Правильно выбирайте тип данных

Если ваша программа работает с большим объемом вычислений, например пишите Вы свой супер-калькулятор, то всегда с осторожностью используйте типы данных.
Довольно часто в книгах по Pascal и Delphi авторы тонких моментов по работе с вещественными типами. Обычно всё сводится к справочной информации типа:

Тип Single
количество значащих цифр 7-8
диапазон значений 1,5*10^-45…3,4*10^38

и т.д. Естественно, вы как порядочный программист стараетесь минимизировать затраты ресурсов компьютера и подбираете такой тип, который по вашему мнению сочетает в себе и объем занимаемой памяти и удобство использования. Проведем простой эксперимент. Открываем Delphi и пишем:

procedure TMainForm.FormCreate(Sender: TObject);
var a,b,c:single;
begin
  a:=9.8;
  b:=1000000;
  c:=a/b;
  ShowMessage(FloatToStr(a)+#10#13+FloatToStr(b)+#10#13+FloatToStr(c))
end;

Тут мы явно задаем значения двум переменным, а третью считаем и результат выводим в виде сообщения. Теперь запустите приложение. У меня получился вот такой вот каламбур:

single

Спрашивается, что произошло с переменной а? Переменная была задана явно. От этого и переменная С получилась непонятного содержания. Понятно, что у single точности хватает на 7-8 знаков, но зачем и откуда в «хвосте» появляются непонятные числа? Вполне возможно (не утверждаю, т.к. досконально не разбирался), что происходит это по той причине, что арифметический сопроцессор всегда обрабатывает числа в формате extended, а остальные типы получаются усечением результатов до нужного размера.
Можем проверить. Пишем так:

procedure TMainForm.FormCreate(Sender: TObject);
var a,b,c:extended;
begin
  a:=9.8;
  b:=1000000;
  c:=a/b;
  ShowMessage(FloatToStr(a)+#10#13+FloatToStr(b)+#10#13+FloatToStr(c))
end;

Запускаем программу и получаем:

extended

Заметьте — сменился только тип, а результат другой. Отсюда следует простой логичный вывод: если Вам необходима высокая точность вычислений в Вашей программе — используйте extended или double. Да, эти типы данных занимают в памяти больше места в 2-2,5 раза, чем single, но так Вы гарантированно получите максимум точности от операций с плавающей точкой.
По своему опыту скажу, что в свое время попал на грабли как раз с single. Хотел как лучше, а отхватил хуже даже, чем всегда. На таких вот мелких огрехах в итоге выползли ошибки в несколько процентов.

5. Простота — залог успеха

При разработке больших и сложных программ всегда стоит помнить, что даже лишняя кнопка может оказаться той преградой для пользователя, которая заставит его отказаться от использования вашего труда.
Конечно, программистам бывает интересно посидеть иногда тихими тёплыми вечерами за компом и поразбираться с какой-нибудь программой со сложным запутанным интерфейсом. Но это программисты. А простому пользователю подчас лень нажать «Ок» в окошке — хочется «Чтобы он сам…».
Так почему бы, где это возможно не сделать?
Не стоит накручивать интерфейс и писать блокнот с использованием Ribbon :) Хоть это и красиво, но очень усложняет работу с программой. Всегда следуйте правилу «Простота — залог успеха». Чем больше Ваша программа будет уметь «делать сама» и причём делать точно и правильно — тем большую аудиторию пользователей Вы соберете вокруг себя.
Также не стоит при каждом случае выкидывать пользователю окошко с красным крестом и заголовком «Еггог!!». Это может развить у пользователя комплекс неполноценности :) — типо «я не могу разобраться с программой, я неудачник и т.д.» . Шутка, конечно, но красные крестики настроения не подымают точно. Где возможно без риска обработать исключение «по-тихой» — делайте это — пусть пользователь спит спокойно. Если пользователь объективно не может повлиять на работу программы в ходе возникновения исключительной ситуации — пусть программа сама обработает исключение, без лишних сообщений. Например, при тех же преобразованиях типов. Никто ведь не обидится, если Вы самостоятельно замените неправильный разделитель целой и дробной части на тот, который используется в системе? А спокойствие пользователю сохраните.
Вот пожалуй и все на сегодня. Теперь можете идти работать :)
UPDATE 04.01.2010: статья доработана с учётом замечаний, оставленных внимательными читателями блога.

0 0 голоса
Рейтинг статьи
уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.
Подписаться
Уведомить о
16 Комментарий
Межтекстовые Отзывы
Посмотреть все комментарии
Vitas
Vitas
05/12/2009 12:56

Эти замечания, конечно, все правильные, но я также обратил бы внимание на необходимость этапа проектирования перед непосредственным кодированием. Просто большинству хочется сразу начинать «кодить»,а по-хорошему надо сначала хорошо подумать, что делать и как, и только после этого запускать Delphi!

Наиль
Наиль
05/12/2009 20:30

Статья полезная но содержит массу неточностей. 2 совет. Комментарий «//тут сообщение для пользователя» наводит на мысль, что нужно всегда выводить сообщение для пользователя. Далее начинаем путать читателя «Также не стоит при каждом случае выкидывать пользователю окошко с красным крестом». И в конце добиваем «Где возможно без риска обработать исключение “по-тихой” – делайте это». Не стоит забывать, что статья содержит материал для тех, кто только начинает. Поэтому нужно было сказать о том, что вывод сообщения об ошибке должен быть только в том случае, когда пользователь может принять действия по решению проблемы, а в остальных случаях программа должна обработать исключения. Кроме того,… Подробнее »

Steff
Steff
07/12/2009 15:09

Всегда интересовал вопрос: а нужны ли комментарии ? Частенько приходилось копаться в чужом коде и не могу припомнить ни одного полезного комментария.  Разве что краткое описание  назначения функции или процедуры. На мой взгляд намного полезней внятно называть функции и переменные. Названия «работают» лучше коментариев.
В любом работающем коде можно разобраться за довольно короткий промежуток времени. И даже если это мой код мне не нужно помнить что и как там работает. Я вспомню алгоритм просто читая код.
Хотелось бы услышать мнение коллег на эту тему. Как часто Вы встречали полезные комментарии ?

Vladimir
08/12/2009 17:30

Спасибо за статью, хотя почти все знал. Но вот про 2.0*2.5 тоже не подумал :)

Rus-Youtube
Rus-Youtube
09/12/2009 01:54

простота- палка о двух концах;
порой, отсутствие большого функционала выглядит как самопальное творение школьника 9″Б»

dimbosik
dimbosik
11/12/2009 21:54

Солидарна с автором статьи о том, что все свои действия необходимо комментировать подробно. На собственном опыте проверено, когда вроде помнишь важные моменты, а проходит немало времени оказывается, что многое забыто… Ручка и лист бумаги, компьютер и документ ворда — не важно, где вы будет фиксировать все свои действия, главное, что они не сотрутся так же быстро из памяти, как из головы человека…

Snejok
15/12/2009 12:14

думаю самая большая проблема — это написание комментариев, все знают что их нужно писать и притом писать коменты несущие смыл действия (а не «тут мы умножаем на 2»), и многие продолжают не писать, надеясь что либо так запомнят, либо потом напишут комментариии…..

Aleksey Timohin
22/12/2009 01:49

Опечатка видимо:     try […] //осуществляем операции except on e: EConvertError do begin //произошла ошибка преобразования типов end; on e: EAccessViolation do begin //произошла ошибка при обращении к ещё не созданной переменной end; on e: EAccessViolation do begin //ОС сообщила об ошибке принтера end; end; Вероятно бесполезное замечание по теме try .. except try except // здесь лучше писать не сообщение для пользователя, а корректно // освобождать занятые ресурсы, закрывать коннекты, делать Rollback-и транзакциям, // или как вариант, устанавливать курсор в control в который введено неверное значение // делать запись сообщения об ошибке в лог(если ведётся), а потом вызывать… Подробнее »

Алексей Тимохин

1: там два раза написано «on e: EAccessViolation do» — зачем дважды?
2:
> Правда иногда всё-аки пользователю надо сообщать об ошибке
В обработчике исключения есть raise, который возобновит исключение, и затем его обработает Application и именно Application покажет пользователю диалог с ошибкой.

Irwin_noteam
Irwin_noteam
12/01/2010 17:27

есть замечательная книга «Ремесло программиста // практика написания хорошего кода», автор Питер Гудлиф. Советую =)

PavelDev
22/05/2010 23:37

Прочитал в коментах, что достаточно информативно называть переменные и процедуры-функции. Но вы представьте, что вы разрабатываете базу данных. И, если вы не будете описывать в комментариях созданные таблицы, хотя они будут понятно названы — все равно останется двоякость их понимания. Так что комментарии обязательно.
Можно еще добавить, что переменные, процедуры, функции, и другие объекты надо так же обязательно называть понятными именами.