Какой не должна быть программа
Какой не должна быть программа
Когда-то я был маленький
Еще не знал кем быть
Но очень уж не нравилось
Фекалии возить
Много книг и статей касаются темы «Какой должна быть программа». Если программы еще нет, то сам такой подход можно считать оптимистическим (может ее никогда и не будет). Рассмотрим похожую тему «Какой не должна быть программа». Если программы еще нет и, следовательно не известно будет ли она, то подход можно назвать пессимистическим (если она будет такой, какой не должна быть, лучше б ее и не было вообще).
Итак, еще не знаем как писать, но на ровном месте нагнетаем обстановку и говорим, допустим, не хочу потратить много времени на отладку, иметь проблемы с модернизацией и сопровождением. Список можно продолжить, но не включая пунктов таких, например, как « не хочу, чтобы мою программу похитили». Это уже оптимизм – надо еще ее сделать такой, чтобы ее кому то хотелось украсть.
Оптимизм и пессимизм по Гегелю – две стороны одного или единство и борьба противоположностей. Поработаешь хорошо на свой пессимизм и станешь оптимистом. Поработай с оптимизмом, но без опыта и почувствуешь законы Мерфи на свой шкуре и можешь впасть в пессимизм и это уже прямой путь к депрессии. Выработать иммунитет легче пессимисту – из депрессии в депрессию хода нет.
Программа начинает свою жизнь при первом запуске кода. Но это стадия эмбриона. На этой стадии пишем и отлаживаем. Если Вы молодой оптимист и пишите сразу длинный код со всеми бантиками – отладка не оставит времени на досуг. Долгая отладка раздражает и утомляет, а дистанция длинная и нужно экономить силы.
Напишите самую суть, отладьте и порадуйтесь жизни – что-то уже есть. Можно улучшать код – скорость, интерфейсные штучки, фишки красивые и т.д.
Программа начинает жизнь после первого показа тому, кто ее не писал.
Хорошо, когда ее ругают – еще есть время исправить ее, а со стороны лучше видно, что в ней плохо.
Программа начинает взрослую жизнь после установки у заказчика (даже если это ознакомительная версия). Теперь могут всплыть самые серьезные ошибки, а увидите их первыми не Вы. Скорее всего вы узнаете об этом по телефону и на ломаном русском вам продиктуют английские буквы сообщения об ошибке. Что делать?
Стоит написать обработчик ошибок.
Где-то в стартовом коде поставим
ON ERROR DO ERRTRAP WITH ERROR(), MESSAGE(), MESSAGE(1), SYS(2018), LINENO()
Можно и не передавать параметры, но бывает это полезно. Например, я вызываю ту же ERRTRAP в Catch’е структуры Try … endtry и передаю через параметры свойства объекта.
Что должно быть в ERRTRAP?
- возможность протолкнуть ошибку или снять задание
- сохранение информации об ошибке (например, в Errors.dbf)
- вызов формы с сообщением об ошибке в таком виде, который по возможности не сильно шокирует юзера и дает ему шанс сообщить Вам что-то полезное по телефону. Если есть модемная связю с юзером, то можно получить файл Errors.dbf и разобраться в ситуации.
Я не считаю свой ERRTRAP показательным, поэтому его здесь не показываю, но эти задачи он решает.
На стадии эмбриона можно избежать некоторых типичных ошибок. У каждого свой путь. Описываю мой.
2. Использование правил при названии переменных и полей.
Пользуюсь рекомендациями MicroSoft. 2 первых символа имени переменной памяти означают область действия (l,g – local,public) и тип переменной (c,n,d,t,l,u,o,a – соответственно текст, число, дата, время, логическое, неопределенное, объект и массив). Поля называю без префикса.
Например, если поле UserName, тогда
lcUserName = UserName
и можно не писать m.lcUserName.
Если надо, чтобы lcUserName была Private, то пишу явно. Public тоже пишу явно, но тогда переменная должна быть gcUserName.
Выделяю и беру в карман текст программы или метода. К функциональной клавише F9 у меня привязан код, по которому в кармане добавляется оператор Local для переменных с префиксом ‘l’:
ON KEY LABEL F9 DO locals
Заменяю выделенное на карман и не боюсь неприятной ошибки, когда в вызываемой программе или методе найдется переменная – двойник.
Текст программы locals:
#DEFINE TAB CHR(9) #DEFINE CRLF CHR(13)+CHR(10) SET EXACT ON LOCAL i, lc1, lc2, lcLeft, lcLine, lcOther, lcPrefix, ; ll, ln, lnEq, lnLine, lnLines, lnLocal lc=_CLIPTEXT lnLines=ALINES(la,lc) STORE " " TO gcExclude DIME laLocals(1), gaOther(1), laDeclare(3) laDeclare(1)="LOCAL " laDeclare(2)="PRIVATE " laDeclare(3)="PUBLIC " laLocals(1)="@" STORE 0 TO gnLocals, gnOther FOR lnLine=1 TO lnLines IF LEFT(la(lnLine),1)="*" LOOP ENDIF lcLine=ALLT( CHRT(la(lnLine),TAB," ")) FOR ln=1 TO ALEN(laDeclare,1) lnLocal=AT(laDeclare(ln) , UPPER(lcLine)) IF lnLocal=1 i=1 DO WHILE RIGHT(lcLine,1) = ";" lcLine=RTRIM(LEFT(lcLine,LEN(lcLine)-1))+CHRT(ALLT(la(lnLine+i)),TAB," ") i=i+1 ENDDO gcExclude=gcExclude+ UPPER(ALLT( SUBS( lcLine,AT( laDeclare(ln), UPPER(lcLine))+6)))+" " LOOP ENDIF NEXT lnLocal=AT("LPARA" , UPPER(ALLT(lcLine))) IF lnLocal=1 gcExclude=gcExclude+ UPPER(ALLT( SUBS( lcLine,AT(" " , UPPER(lcLine)))))+" " LOOP ENDIF NEXT gcExclude=CHRT(gcExclude,","," ") FOR lnLine=1 TO lnLines IF LEFT(la(lnLine),1)="*" LOOP ENDIF lcLine=ALLT(CHRT(la(lnLine),TAB," ")) IF UPPER(LEFT(lcLine,6))="STORE " AND " TO " $ UPPER(lcLine) lc1=SUBS(lcLine,AT(" TO ",UPPER(lcLine))+4)+"," ln=OCCURS(",",lc1) FOR i=1 TO ln lc2=ALLT(LEFT(lc1,AT(",",lc1)-1)) IF "(" $ lc2 LOOP ENDIF =putIt(lc2) lc1=SUBS(lc1,AT(",",lc1)+1) NEXT LOOP ENDIF lnEq=AT("=",lcLine) IF lnEq=0 LOOP ENDIF lcLeft=ALLT(LEFT(lcLine,lnEq-1)) IF LEFT(lcLeft,4)="FOR " =putIt(SUBS(lcLeft,5)) LOOP ENDIF IF LEFT(lcLeft,2)="M." lcLeft=SUBS(lcLeft,3) ENDIF IF "&" $ lcLeft LOOP ENDIF IF " " $ lcLeft OR "." $ lcLeft LOOP ENDIF =putIt(lcLeft) NEXT = ASORT(laLocals) gclocals="LOCAL " lcPrefix="@@" FOR lnLine=1 TO gnLocals lc=laLocals(lnLine) IF LEN(lc)>1 lc=LOWER(LEFT(lc,2))+UPPER(SUBS(lc,3,1))+IIF(LEN(lc)>3,SUBS(lc,4),"") ll = lcPrefix # LEFT(lc,2) AND lnLine>1 lcPrefix=LEFT(lc,2) ELSE lc=LOWER(lc) ll=.F. ENDIF gclocals=gclocals+IIF(ll,TAB+";"+CRLF+TAB,"")+ lc+IIF(lnLine=gnLocals,"",", ") NEXT i=0 lcOther="" FOR lnLine=1 TO gnOther lc=gaOther(lnLine) lcOther=lcOther+IIF(i>6,CRLF,"")+ lc+IIF(lnLine=gnOther,"",", ") i=IIF(i>6,0,i+1) NEXT lc=IIF(LEN(gclocals)<7,"","Карман @"+gclocals) ; +IIF(EMPTY(lcOther),"","@@Другие : "+lcOther) IF !EMPTY(lc) IF messagebox(lc,292,"Делать вставку?")=7 _CLIPTEXT=gclocals+CRLF RETU ENDIF ELSE = messagebox ("OK",0,'') RELEASE la RETURN ENDIF lnInsert=0 lnTabs=0 FOR lnLine=1 TO lnLines lcLeft4=UPPER(LEFT(ALLT(CHRT(la(lnLine),TAB," ")),4)) IF INLIST(lcLeft4,"FUNC","PROC","PARA","LPAR") ; OR "#" $ lcLeft4 OR LEFT(lcLeft4,1)="*" LOOP ENDIF lnInsert=lnLine lnTabs=OCCURS(TAB,la(lnLine)) EXIT NEXT IF BETW(lnInsert,1,lnLines) AND LEN(gclocals)>6 lc="" FOR lnLine=1 TO lnInsert-1 lc=lc+la(lnLine)+CRLF NEXT lc=lc+IIF(lnTabs>0,REPL(TAB,lnTabs),"")+gclocals+CRLF FOR lnLine=lnInsert TO lnLines lc=lc+la(lnLine)+CRLF NEXT _CLIPTEXT=lc KEYBOARD '{CTRL+V}' RETURN ENDIF _CLIPTEXT=gclocals+CRLF FUNC putIt LPARA lc lcUp=UPPER(lc) IF EMPTY(lc) RETURN ENDIF IF " "+lcUp+" " $ gcExclude RETU ENDIF IF "(" $ lc RETU ENDIF IF ! INLIST(LEFT(lcUp,2) , "LC", "LD", "LT", "LU", "LN", "LL", "LO") ; AND !LEFT(lcUp,1) $ "IJ" IF ASCAN(gaOther,lcUp)>0 RETU ENDIF gnOther=gnOther+1 DIME gaOther(gnOther) gaOther(gnOther)=lcUp ELSE FOR lnLine=1 TO ALEN(laLocals) IF UPPER(laLocals(lnLine))=lcUp RETU ENDIF NEXT gnLocals=gnLocals+1 DIME laLocals(gnLocals) laLocals(gnLocals)=lc ENDIF RETU
Стараюсь избегать коротких имен. Раньше писал, например,
FOR i=1 to N
…
Next
Когда требовалось найти или переименовать переменую i, то натыкался на массу слов, в которых есть эта буква. Сейчас пишу
FOR lnLine=1 TO lnLines
…
NEXT
Во-первых, знаю, что речь идет о строке (Line) и, что их количество lnLines, а не безликое N.
1. Проблема открытия и закрытия файла
Не люблю сообщений “file is in use” и ему подобных.
Две подпрограммы AutoLoad(‘список файлов’) и Unload() помогают жить.
lcListOfFiles = AutoLoad(‘partners;people’)
В переменную lcListOfFiles попадает информация для каждого файла из списка о том, был ли он до того открыт. После вызова AutoLoad все файлы будут открыты, если они вообще видны.
Далее с помощью
=Unload(lcListOfFiles)
закрываются файлы, которые ранее не были открыты.
Написать подобные подпрограммы может каждый. В моих присутствует дополнительный код, нужный не каждому.
С помощью подпрограммы Close(‘список файлов’) закрываю файл, если от открыт
FUNC Close LPARA lc1,lc2,lc3,lc4,lc5,lc6,lc7,lc8,lc9,lc10, ; lc11,lc12,lc13,lc14,lc15,lc16,lc17,lc18,lc19,lc20 LOCAL lc, lcFile, lnCol, lnCols, lnLine LOCAL la(1) FOR lnLine=1 TO PARA() lc=EVAL('lc'+ALLT(STR(lnLine))) IF TYPE('lc')='C' lc=ALLT(lc) lnCols=ALINE(la,lc,',') FOR lnCol=1 TO lnCols lcFile = ALLTRIM(la(lnCol)) IF USED(lcFile) SELECT (lcFile) USE ENDIF NEXT ENDIF NEXT
Недавно решил, что и этого мало. Добавил 2 функции. Первая запоминает имена открытых в данный момент файлов. Второй передаю этот список и она закрывает все, что в этот список не входит.
Перед вызовом формы или программы запоминаю то, что открыто, а на выходе закрываю то, что открывалось внутри. Особенно хорошо при коллективном творчестве. Вызываю что-то не мной писанное и не боюсь, что там не закрываются файлы. Опять же SQL любит открывать, а закрывать самому нужно.
3. Оператор Try … EndTry
Программа или ее часть, которая не прошла тяжелую обкатку можно поместить в Try … EndTry. На Catch’е включаю ERRTRAP. При ошибке юзер за один раз проталкивает программу и не попадает в ошибки – следствия первоначальной, а они могут оказаться в цикле и проталкивать больше 1000 раз многие из моих юзеров не любят.
Со временем Try … EndTry можно и убрать.
4. Файл Errors можно смотреть через Browse, но можно написать свою форму для его просмотра, а вызов ее где-то завуалировать или явно поставить где-нибудь в Top Menu.
5. Ошибки – это хорошо. Только их надо исправлять и думать как в следующий раз таких не допустить. Благодаря ошибкам идет развитие программиста как личности. Начинаешь другим прощать ошибки, становишься лучше. Многие из нас хвастаются ленью. Я, мол, ленивый и, поэтому сразу пишу гениально. Потом оказывается, нашлись еще более гениальные ошибки. Хорошо, если это ошибки MicroSoft и своя гениальность (не путать с генитальностью) вне сомнений. Иначе лучше лениво молчать об этом.
Даже если Вы ленивый гений, подумайте как быстро и проще исправлять ошибки. Ошибку в .prg легко исправить, если его нет в проекте. Все включать в проект, т.е. в ехе не обязательно. Frx вообще в проект лучше не включать. Делайте вызов в виде:
lcPrg=’alfa’
do &lcPrg with …
Alfa.prg не будет автоматически включена в проект и Вы сможете отредактировать ее на месте (у юзера).
На этом подходе мной с коллегами создан Framework, который одновременно является выполняемой программой и средством разработки и отладки. Он позволяет без инсталляции Fox’а у юзера исправлять ошибки и модифицировать и отлаживать программу на месте. Это еще называют «на лету». Конечно, создать свой Framework не так быстро. На первых порах можно просто не включать в проект все подряд.
Имея модемную связь с юзером, можно отсылать ему отдельно изменения в формах, репортах, подпрограммах. Лишь бы их не было в проекте.
5. Вынесите Public – переменные, которые могут со временем меняться в отдельную форму, где юзер при необходимости может их сам исправить.
Некоторые прогмены этого не любят. Мол, пусть юзер знает за что платит. Попросит, тогда и сделаю.
Если программа тиражируется, накладно объезжать всех юзеров, да и надо же любить юзера, а не держать его в напряжении даже если он Вас в напряжении держит.
Заключение.
Если Вам покажется часть изложенного правдой, найдите еще что-то свое, что упростит ваше будущее. За юзера сегодня приходится бороться, ведь альтернатива – это работа не по специальности и не по призванию. Вместо ощущения пластмассовости клавиш на кончиках пальцев и общения с коллегами на комповом сленге ощущение дискомфорта от выполнения работы, в которой нет романтики (смотри эпиграф в начале статьи)