Форум программистов, компьютерный форум, киберфорум
Наши страницы
krapotkin
Войти
Регистрация
Восстановить пароль
Блог. Двадцать пять лет Делфи-практики

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

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

Начав с Делфи-2 двадцать пять лет назад, я прошел все версии, испробовал массу технологий, включая работу с БД, с графикой DirectX, связью с серверами и интернетом, разработку на Андроид и IOS, и многое, многое другое.
____________________________________________________________________________________
P.S. все, о чем здесь написано, всего лишь измышления из головы.
совпадения с реальными людьми и фактами случайны.
Оценить эту запись

Программа и ее настройки

Запись от krapotkin размещена 20.08.2017 в 17:55
Обновил(-а) krapotkin 12.06.2018 в 07:28

Сразу после создания своей самой лучшей программы, встает вопрос, а нельзя ли например запоминать, к какой БД я подключался, каково состояние Form18.CheckBox1.Checked было при выходе из программы, и много других крайне нужных вещей.

Ответ очевиден. Думать об этом стоит ДО создания самой лучшей программы.

Итак, для хранения настроек программы нам понадобятся
1. Класс
Delphi
1
TMySettings = class ... end;
2. Класс или запись
Delphi
1
2
3
4
5
6
7
8
9
TDBSettings = record
  server:string;
  port:integer;
  db:string;
  role:string;
  charset:string;
  login:string;
  pass:string;
end;
3. Библиотека работы с JSON. Я использую XSuperObject
// для старых версий Delphi можно брать заброшенный оригинал этой библиотеки SuperObject
4. Советую также посмотреть видео
5. для более простой работы с настройками я сделал себе класс
TJsonableObject, который удобно использовать для создания класса настроек
Можно взять его тут https://bitbucket.org/vkrapotkin/jsonableobject

Главное, что требуется понять.
Мы создали класс, объект которого будет хранить настройки программы
Использовать его, скорее всего, придется в режиме Singleton, т.е. в начале программы создать один глобальный объект, загрузить его из файла или из интернета и пользоваться во всей программе.

Для некоторых видов работ никто не запрещает нам создавать дополнительные объекты этого класса, например с целью редактирования и последующей перезаписи.
Так, например, чтобы протестировать подключение к БД, я создаю копию TDBSettings, и пользуюсь своей стандартной процедурой подключения (также с отдельными объектами коннекта и запросов).
На основную программу и основные настройки это никак не влияет.

Все, что связано с настройками, уносим в отдельный юнит для каждой программы.
Бывает, что некоторые классы устойчиво кочуют из программы в программу. Это как раз TDBSettings;
Этот класс также вынесен в отдельный юнит.

Вот пример из рабочего проекта с некоторыми изъятиями
Кликните здесь для просмотра всего текста
Delphi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
unit AS_Settings;
interface
uses SysUtils , classes, XSuperObject, JsonableObject, UDBSettings;
type
  TASFTPServerOptions = record
  public
    Host: string;
    Folder: string;
    Login: string;
    Pass: string;
  end;
 
  TASPersCardFormOptions = record
  public
    BaseCode: Integer;
  end;
 
  TASCommonRegFormOptions = record
  public
    Filter: string;
    PrintNum: Boolean;
    Preview: Boolean;
  end;
 
  TAS_Settings = class(TJsonableObject)
  private
  public
    Database:TDBSettings;
    LastImportDatabase:TDBSettings;
    FTP:TASFTPServerOptions;
 
    [DISABLE] // для JSON
    RecentConnections: TStringList;
    [ALIAS('RecentConnections')]
    _RecentConnections:TStringArray;
 
    PersCardForm: TASPersCardFormOptions;
    CommonRegForm: TASCommonRegFormOptions;
 
    procedure BeforeSave(X:ISuperObject); override;
    procedure Afterload(X:ISuperObject); override;
 
 
    constructor Create;  override;
    destructor Destroy; override;
  end;
 
const
  TargetNamespace = '';
 
var
  Settings : TAS_Settings;
 
function GetSettingsFilename():string;
 
implementation
 
function GetSettingsFilename():string;
begin
  result := ExtractFilePath(ParamStr(0))+'settings.json';
end;
 
{ TAS_Settings }
 
procedure TAS_Settings.Afterload(X:ISuperObject);
var
  i: Integer;
begin
  Setter_ArrayToList(_RecentConnections, RecentConnections);
end;
 
procedure TAS_Settings.BeforeSave(X:ISuperObject);
var
  i: Integer;
begin
  _RecentConnections:=Getter_ListToArray(RecentConnections);
end;
 
constructor TAS_Settings.Create;
begin
  inherited;
  RecentConnections:=TStringList.Create;
end;
 
destructor TAS_Settings.Destroy;
begin
  FreeAndNil(RecentConnections);
  inherited;
end;
 
end.

модуль UDBSettings у меня сейчас выглядит вот так
Кликните здесь для просмотра всего текста
Delphi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
unit UDBSettings;
 
interface
type
  TDBSettings = record
    server:String;
    port:string;
    db:string;
    lib:string;
 
    user:string;
    pass:string;
    role:string;
    procedure parseParams;
    procedure ParseConnectionString(s: string);
    function ConnectionString:string;
    function Equals(d:TDBSettings):Boolean;
  end;
 
implementation
uses System.SysUtils;
 
{ TDBSettings }
 
function TDBSettings.ConnectionString: string;
begin
  if server<>'' then
    result:=server
  else
    Result := 'localhost';
  if port<>'' then
    result:=result+'/'+port;
  Result := result + ':' + db;
end;
 
procedure TDBSettings.ParseConnectionString(s:string);
var
  s1,s2:string;
  p:integer;
  a:TArray<string>;
begin
  p:=Pos(':',s);
  if p>0 then
  begin
    server:=Copy(s,1,p-1);
    db:=Copy(s,p+1,s.Length);
  end
  else
  begin
    server:='localhost';
    db:=s;
  end;
  a:=server.Split(['/']);
  if Length(a)>1 then
  begin
    server:=a[0];
    port:=a[1];
  end;
end;
 
function TDBSettings.Equals(d: TDBSettings): Boolean;
begin
  Result := (server = d.server)
      and (port = d.port)
      and (db = d.db)
end;
 
procedure TDBSettings.parseParams;
var
  i:integer;
begin
  i := 1;
  while i <= ParamCount do
  begin
    if AnsiSameText(ParamStr(i), '-D') and (i<paramCount) then
    begin
      ParseConnectionString(ParamStr(i + 1));
      inc(i);
    end
    else if AnsiSameText(ParamStr(i), '-R') and (i<paramCount) then
    begin
      role := ParamStr(i + 1);
      inc(i);
    end
    else if AnsiSameText(ParamStr(i), '-U') and (i<paramCount) then
    begin
      user := ParamStr(i + 1);
      inc(i);
    end
    else if AnsiSameText(ParamStr(i), '-P') and (i<paramCount) then
    begin
      pass :=ParamStr(i + 1);
      inc(i);
    end
    else if AnsiSameText(ParamStr(i), '-L') and (i<paramCount) then
    begin
      lib :=ParamStr(i + 1);
      inc(i);
    end;
    inc(i);
  end;
 
end;
 
end.

здесь видно, что настройки программы хранят в себе настройки разных форм программы, настройки подключения к БД, еще порция настроек БД, где хранится последнее удачное подключение, настройки подключения к FTP, настройки интерфейса для нескольких форм.
Если мы хотим добавить к настройкам какие-то данные, достаточно просто добавить нужные поля к классу и они автоматически начинают записываться и считываться, без написания дополнительного кода.
Вот все, что я делаю для считывания настроек:
Delphi
1
 Settings:=TAS_Settings.CreateFromFile(GetSettingsFilename());
ну и для сохранения
Delphi
1
 Settings.SaveToFile(GetSettingsFilename());
Есть нюанс при использовании свойств наследников TList - TStringList, TObjectList. Они пишут в файл множество своих ненужных public свойств. Для избавления от этого в коде виден механизм ([DIASBLE], [ALIAS...]), когда перед записью и после считывания мы используем временный массив нужных объектов. List не читается/пишется, вместо него работает подставной массив. Потом из массива мы переносим все в List, с которым гораздо удобнее работать, и продолжаем.

Внутри программы глобальная переменная Settings используется где попало
например
Delphi
1
TryConnect( Settings.Database );
или
Delphi
1
2
 if Settings.DiplomaFile<>'' then
    repDiploma.LoadFromFile(Settings.DiplomaFile,true);
Резюмирую. Отличный вариант работы с настройками программы.
Просто. Удобно. Красиво.
Размещено в Без категории
Просмотров 502 Комментарии 0
Всего комментариев 0
Комментарии
 
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2018, vBulletin Solutions, Inc.
Рейтинг@Mail.ru