Social Icons

воскресенье, 21 марта 2010 г.

Упростите свои Delphi-приложения - Часть 2

В первой части мы кратко рассмотрели основные правила. Кроме того, у нас было время подумать, что должен делать наш код, поэтому отразить это во второй части будет хорошей идеей.

1. Введение

У нас было время подумать над тем, что должен делать наш код, и, наверняка, у Вас уже есть есть идеи. К сожалению, люди, пишущие код, редко задумываются о нем. Далее мы ближе познакомимся с процессом программирования, увидим некоторые недостатки и попробуем устранить их.

2. Старый код

Обычно разработчики предпочитают считывать настройки приложения в тот момент, когда пользователь может захотеть их изменить. Когда пользователь закончил редактирование, они снова записываются в реестр. Также, очевидно, что чтение и сохранение настроек происходит при открытии и закрытии формы соответственно.

Итак, на главной форме мы имеем какой-то такой код:
procedure TfrmMain.FormShow(Sender: TObject);
var
  aRegistry: TRegistry;
begin
  aRegistry := TRegistry.Create;
  try
    aRegistry.OpenKey('the_registry_path', True);
    // FString, FInteger и FBoolean локальные поля
    // формы TfrmMain.
    FString  := aRegistry.ReadString('string_setting_key');
    FInteger := aRegistry.ReadInteger('string_setting_key');
    FBoolean := aRegistry.ReadBool('string_setting_key');
  finally
    aRegistry.Free;
  end;
end;

procedure TfrmMain.FormClose(Sender: TObject; var Action: TCloseAction);
var
  aRegistry: TRegistry;
begin
  aRegistry := TRegistry.Create;
  try
    // FString, FInteger и FBoolean локальные поля
    // формы TfrmMain.
    aRegistry.OpenKey('the_registry_path', True);
    aRegistry.WriteString('string_setting_key', FString);
    aRegistry.WriteInteger('string_setting_key', FInteger);
    aRegistry.WriteBool('string_setting_key', FBoolean);
  finally
    aRegistry.Free;
  end;
end;

procedure TfrmMain.mnuEditSettingsClick(Sender: TObject);
var
  aEditSettingsForm: TfrmEditSettings;
begin
  aEditSettingsForm := TfrmEditSettings.Create(Self);
  try
    aEditSettingsForm.TheString := FString;
    aEditSettingsForm.TheInteger := FInteger;
    aEditSettingsForm.TheBoolean := FBoolean;

    if (aEditSettingsForm.ShowModal = mrOK) then
    begin
      FString  := aEditSettingsForm.TheString;
      FInteger := aEditSettingsForm.TheInteger;
      FBoolean := aEditSettingsForm.TheBoolean;
    end;
  finally
    FreeAndNil(aEditSettingsFrom)
  end;
end;

3. Как это будет работать?

Как видите, код рабочий. Настройки будут считываться при отображении главной формы и храниться в некоторых локальных переменных. При закрытии формы настройки будут сохранены. Кроме того, у нас есть код, позволяющий открыть форму редактирования настроек, передать туда текущие настройки, и получить обратно измененные при закрытии формы редактирования нажатием кнопки OK.

4. В чем же проблема?


Выполняется ли код в нужный момент времени?

На мой взгляд события OnShow и OnClose - не лучшее место для выполнения этого кода. А что, если настройки связаны с визуальным отображением формы? Вы действительно хотите загружать и сохранять их при каждом показе формы? А может у Вас есть настройки, которые необходимо загрузить до отображения первой формы?

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

Что произойдет в случае ошибки при загрузке/сохранении настроек?

Данный код не идеален, и интересные вещи могут произойти, если в реестре отсутствует требуемый ключ. Код вызовет исключение, что приведет к загрузке/сохранению только части настроек и получению неверных значений. В нашем случае было бы неплохо использовать значение по умолчанию при отсутствии соответствующего ключа в реестре.

Кроме того, когда пользователь открывает форму редактирования настроек и закрывает ее нажатием кнопки OK, новые настройки передаются главной форме, но еще не сохраняются в реестре. Если теперь Вы решите взять настройки из реестра в другом участке кода, то получите несоответствие текущим настройкам на главной форме.

Неужели главная форма должна загружать/хранить настройки?

Нет, абсолютно нет!

Что если мы захотим создать консольную версию приложения? Нам все еще может понадобиться загружать/сохранять настройки из реестра, но никаких форм у нас не будет. А что если мне нужно создать поток для вычислений, и считывать настройки непосредственно в этом потоке?

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

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

5. Есть ли лучшая альтернатива?

Сказать, что это плохо - не трудно, но в таком случае нужно предложить что-то получше. Давайте немного подумаем, что мы хотим, и что нам требуется. В общем, нам нужен кто-то, кто будет загружать и сохранять настройки приложения. Было бы замечательно, если бы у нас был код, который можно было использовать в любом приложении в будущем. На самом деле не важно, какое количество настроек мы хотим сохранить, как они называются, или в каком формате они хранятся, единственное, что нам нужно - возможность Загрузки/Сохранения/Редактирования и Применения их. Как это будет сделано не важно, но, предположим, класс ApplicationSettings позволяет это делать.

Если мы хотим применить настройки к приложению, нужно добавить код наподобие следующего:
var
  aApplicationSettings: TApplicationSettings;
begin
  aApplicationSettings := TApplicationSettings.Create('');  
  try
    // Код для загрузки настроек  
    aApplicationSettings.LoadFromRegistry;
    
    // Код для редактирования настроек
    if (aApplicationSettings.EditSettings) then    
    begin
      // Настройки изменены, и, возможно, 
      // мы что-то хотим при этои сделать.
    end;
        
    // Код для сохранения настроек
    aApplicationSettings.SaveToRegistry;    
  finally
    FreeAndNil(aApplicationSettings);  
  end;
end;

Кто что делает?

Как видите, наша приложение/форма или что бы то ни было не должны знать о том, как считывать настройки, сохранять, отображать или редактировать их. Единственное, нужно знать, что это умеет класс TApplicationSettings.

С другой стороны наш класс настроек должен знать какие настройки нужны приложению, какие это настройки, и как их необходимо загружать/сохранять.

Есть ли преимущества у данного подхода?

Конечно, иначе бы я не стал тратить на это время. Наша главная цель - использовать Delphi как объектно-ориентированный язык программирования, а не просто как RAD-инструмент. В дальнейшем мы создадим несколько классов, основанных на данном подходе и основном смысл в том, что использовать эти классы когда нам необходимо.

Если завтра нам потребуется написать другое приложение и ему будет необходимо загружать/сохранять настройки, мы только укажем классу, какие это настройки, их название и тип. Остальная часть кода останется такой же. Мы потратим больше времени на создание класса настроек и написание соответствующего кода, но в следующий раз, все (почти все) уже будет готово.

Что дальше?

Мы уже многое обдумали и обговорили, но толком ничего не запрограммировали. Скорее всего немногие думают, что способны написать код уже после прочтения этих двух статей.

Конечно, это возможно, я видел такое...

Печально, но однажды я был тем, кого наняли для отладки приложения 5-ти летней давности в поисках ошибки в работе некоторых классов. Я также был в ситуации, когда пришлось пробегаться по тысячам строчек кода, удивляясь, почему некоторые настройки никогда не сохраняются или получают неверные значения. И конечно, я всегда желал, что они уделили подумали над кодом до его написания :-P.

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

Оригинал: http://www.devia.be/news/article/simplify-your-delphi-code-using-some-basic-rules-oo-techniques-refacto/

Комментариев нет:

Отправить комментарий

Поделитесь с друзьями!

 

Подписчики

Статистика