|
|
|||||||||||||||||||||||||||||
|
Упростите свои Delphi-приложения - Часть 2Источник: deviabe
В первой части мы кратко рассмотрели основные правила. Кроме того, у нас было время подумать, что должен делать наш код, поэтому отразить это во второй части будет хорошей идеей. 1. ВведениеУ нас было время подумать над тем, что должен делать наш код, и, наверняка, у Вас уже есть есть идеи. К сожалению, люди, пишущие код, редко задумываются о нем. Далее мы ближе познакомимся с процессом программирования, увидим некоторые недостатки и попробуем устранить их.
2. Старый кодОбычно разработчики предпочитают считывать настройки приложения в тот момент, когда пользователь может захотеть их изменить. Когда пользователь закончил редактирование, они снова записываются в реестр. Также, очевидно, что чтение и сохранение настроек происходит при открытии и закрытии формы соответственно. Итак, на главной форме мы имеем какой-то такой код: 01. procedure TfrmMain . FormShow(Sender: TObject); 02. var 03. aRegistry: TRegistry; 04. begin 05. aRegistry := TRegistry . Create; 06. try 07. aRegistry . OpenKey( 'the_registry_path' , True); 08. // FString, FInteger и FBoolean локальные поля 09. // формы TfrmMain. 10. FString := aRegistry . ReadString( 'string_setting_key' ); 11. FInteger := aRegistry . ReadInteger( 'string_setting_key' ); 12. FBoolean := aRegistry . ReadBool( 'string_setting_key' ); 13. finally 14. aRegistry . Free; 15. end ; 16. end ; 17. 18. procedure TfrmMain . FormClose(Sender: TObject; var Action: TCloseAction); 19. var 20. aRegistry: TRegistry; 21. begin 22. aRegistry := TRegistry . Create; 23. try 24. // FString, FInteger и FBoolean локальные поля 25. // формы TfrmMain. 26. aRegistry . OpenKey( 'the_registry_path' , True); 27. aRegistry . WriteString( 'string_setting_key' , FString); 28. aRegistry . WriteInteger( 'string_setting_key' , FInteger); 29. aRegistry . WriteBool( 'string_setting_key' , FBoolean); 30. finally 31. aRegistry . Free; 32. end ; 33. end ; 34. 35. procedure TfrmMain . mnuEditSettingsClick(Sender: TObject); 36. var 37. aEditSettingsForm: TfrmEditSettings; 38. begin 39. aEditSettingsForm := TfrmEditSettings . Create(Self); 40. try 41. aEditSettingsForm . TheString := FString; 42. aEditSettingsForm . TheInteger := FInteger; 43. aEditSettingsForm . TheBoolean := FBoolean; 44. 45. if (aEditSettingsForm . ShowModal = mrOK) then 46. begin 47. FString := aEditSettingsForm . TheString; 48. FInteger := aEditSettingsForm . TheInteger; 49. FBoolean := aEditSettingsForm . TheBoolean; 50. end ; 51. finally 52. FreeAndNil(aEditSettingsFrom) 53. end ; 54. end ; 3. Как это будет работать?Как видите, код рабочий. Настройки будут считываться при отображении главной формы и храниться в некоторых локальных переменных. При закрытии формы настройки будут сохранены. Кроме того, у нас есть код, позволяющий открыть форму редактирования настроек, передать туда текущие настройки, и получить обратно измененные при закрытии формы редактирования нажатием кнопки OK.
4. В чем же проблема?
Выполняется ли код в нужный момент времени?На мой взгляд события OnShow и OnClose - не лучшее место для выполнения этого кода. А что, если настройки связаны с визуальным отображением формы? Вы действительно хотите загружать и сохранять их при каждом показе формы? А может у Вас есть настройки, которые необходимо загрузить до отображения первой формы? В любом случае Вам может потребоваться загружать или сохранять настройки в разное время работы Вашего приложения. В данном примере Вы загружали их только при загрузке формы. Плохое решение - просто переписать или скопировать этот же код в другие участки проекта. Верите или нет, но это случается довольно часто.
Что произойдет в случае ошибки при загрузке/сохранении настроек?Данный код не идеален, и интересные вещи могут произойти, если в реестре отсутствует требуемый ключ. Код вызовет исключение, что приведет к загрузке/сохранению только части настроек и получению неверных значений. В нашем случае было бы неплохо использовать значение по умолчанию при отсутствии соответствующего ключа в реестре. Кроме того, когда пользователь открывает форму редактирования настроек и закрывает ее нажатием кнопки OK, новые настройки передаются главной форме, но еще не сохраняются в реестре. Если теперь Вы решите взять настройки из реестра в другом участке кода, то получите несоответствие текущим настройкам на главной форме.
Неужели главная форма должна загружать/хранить настройки?Нет, абсолютно нет! Что если мы захотим создать консольную версию приложения? Нам все еще может понадобиться загружать/сохранять настройки из реестра, но никаких форм у нас не будет. А что если мне нужно создать поток для вычислений, и считывать настройки непосредственно в этом потоке? Как видите, главная форма на самом деле не должна отвечать за загрузку/хранение настроек. Она может вызывать какой-либо метод для этого, но основной код никогда не должен находится в коде главной формы. Еще одно мое правило заключается в том, что форма должна содержать только код, влияющий на визуальные характеристики ее самой или ее компонентов. Загрузка/сохранение настроек никак не относится к визуальному отображению формы, соответственно код ей принадлежать не должен. Вам может понадобиться изменить некоторые визуальные характеристики на основании загруженных настроек, но само чтение - не в компетенции формы.
5. Есть ли лучшая альтернатива?Сказать, что это плохо - не трудно, но в таком случае нужно предложить что-то получше. Давайте немного подумаем, что мы хотим, и что нам требуется. В общем, нам нужен кто-то, кто будет загружать и сохранять настройки приложения. Было бы замечательно, если бы у нас был код, который можно было использовать в любом приложении в будущем. На самом деле не важно, какое количество настроек мы хотим сохранить, как они называются, или в каком формате они хранятся, единственное, что нам нужно - возможность Загрузки/Сохранения/Редактирования и Применения их. Как это будет сделано не важно, но, предположим, класс ApplicationSettings позволяет это делать. Если мы хотим применить настройки к приложению, нужно добавить код наподобие следующего: 01. var 02. aApplicationSettings: TApplicationSettings; 03. begin 04. aApplicationSettings := TApplicationSettings . Create( '<OUR Key Registry>' ); 05. try 06. // Код для загрузки настроек 07. aApplicationSettings . LoadFromRegistry; 08. 09. // Код для редактирования настроек 10. if (aApplicationSettings . EditSettings) then 11. begin 12. // Настройки изменены, и, возможно, 13. // мы что-то хотим при этои сделать. 14. end ; 15. 16. // Код для сохранения настроек 17. aApplicationSettings . SaveToRegistry; 18. finally 19. FreeAndNil(aApplicationSettings); 20. end ; 21. end ; Кто что делает?Как видите, наша приложение/форма или что бы то ни было не должны знать о том, как считывать настройки, сохранять, отображать или редактировать их. Единственное, нужно знать, что это умеет класс TApplicationSettings. С другой стороны наш класс настроек должен знать какие настройки нужны приложению, какие это настройки, и как их необходимо загружать/сохранять.
Есть ли преимущества у данного подхода?Конечно, иначе бы я не стал тратить на это время. Наша главная цель - использовать Delphi как объектно-ориентированный язык программирования, а не просто как RAD-инструмент. В дальнейшем мы создадим несколько классов, основанных на данном подходе и основном смысл в том, что использовать эти классы когда нам необходимо. Если завтра нам потребуется написать другое приложение и ему будет необходимо загружать/сохранять настройки, мы только укажем классу, какие это настройки, их название и тип. Остальная часть кода останется такой же. Мы потратим больше времени на создание класса настроек и написание соответствующего кода, но в следующий раз, все (почти все) уже будет готово.
Что дальше?Мы уже многое обдумали и обговорили, но толком ничего не запрограммировали. Скорее всего немногие думают, что способны написать код уже после прочтения этих двух статей. Конечно, это возможно, я видел такое... Печально, но однажды я был тем, кого наняли для отладки приложения 5-ти летней давности в поисках ошибки в работе некоторых классов. Я также был в ситуации, когда пришлось пробегаться по тысячам строчек кода, удивляясь, почему некоторые настройки никогда не сохраняются или получают неверные значения. И конечно, я всегда желал, что они уделили подумали над кодом до его написания :-P. В любом случае, в следующий раз я покажу Вам, как это сделать, и надеюсь, поделюсь кодом.
|
|