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

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

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

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

Как правильно сделать форму логина

Запись от krapotkin размещена 13.08.2017 в 08:46
Обновил(-а) krapotkin 16.05.2018 в 09:29

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

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

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

Итак. Мы создадим главную форму TMainForm и форму логина TLoginForm.
(примечание про AutoCreate http://www.cyberforum.ru/blogs/469693/blog4873.html)
LoginForm - два Label два Edit и две TButton с заполненным ModalResult mrOk и mrCancel соответственно.
BorderStyle=bsDialog. Position=poMainFormCenter

В MainForm переопределяем конструктор и пишем туда... оп! не плодить простыни в коде!
Пишем туда
Delphi
1
2
inherited; // нужно вызвать конструктор предка
CheckLogin(); // а тут будут действия по проверке логина
так же стараемся себя вести и в других местах, выделяя логические блоки в отдельные процедуры и функции
например, сама проверка пароля у нас будет выполняться в функции LoginCorrect(login,pass:string):boolean;
и она ничего вообще не будет знать о форме логина, ибо мало ли откуда к нам придут логин и пароль впоследствии...

итого:
Кликните здесь для просмотра всего текста
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
unit UMainForm;
 
interface
 
uses
  classes, types, forms, Controls, StdCtrls, Dialogs, SysUtils;
 
type
  TMainForm = class(TForm)
  private
    procedure CheckLogin;
    function LoginCorrect(login, pass: String): Boolean;
  public
    constructor Create(AOwner: TComponent); override;
  end;
 
var
  MainForm: TMainForm;
 
implementation
 
uses
  ULoginForm;
 
 
{$R *.dfm}
 
function TMainForm.LoginCorrect(login,pass:String):Boolean;
begin
  result := true;
end;
 
procedure TMainForm.CheckLogin();
var
  f:TLoginForm;
  r:TModalResult;
begin
  f:=TLoginForm.Create(NIL);
  try
    repeat
      r:=f.ShowModal;
      if r=mrOk then
      begin
        // придумаем функцию LoginCorrect() которая возвращает нам True или False
        // откуда она берет эти данные, из базы или файла или придумывает сама, нам все равно
        if LoginCorrect(f.eLogin.text, f.ePassword.text) then
          Break
      end;
    until r=mrCancel;
    if r=mrCancel then
    begin
      ShowMessage('Отказ от авторизации. Программа завершает свою работу.');
      Application.Terminate();
      Abort;
    end;
  finally
    f.free;
  end;
end;
 
 
constructor TMainForm.Create(AOwner: TComponent);
begin
  inherited;
  CheckLogin;
end;
 
end.


Кликните здесь для просмотра всего текста
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
unit ULoginForm;
 
interface
 
uses
  Windows, Messages, Forms, StdCtrls, Classes, Controls;
 
type
  TLoginForm = class(TForm)
    eLogin: TEdit;
    ePassword: TEdit;
    bOk: TButton;
    bCancel: TButton;
  private
    { Private declarations }
  public
    { Public declarations }
  end;
 
 
implementation
 
{$R *.dfm}
 
end.

вот и все

UPD
Более жизненный сценарий старта программы я привел здесь
Размещено в Без категории
Просмотров 1390 Комментарии 25
Всего комментариев 25
Комментарии
  1. Старый комментарий
    Аватар для DenNik
    а я считаю, что форму авторизации лучше показывать ДО создания главной формы, да и мигание этой формочки при неправильном вводе тоже некомильфо. я делаю обычно так

    форма авторизации
    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
    
    unit Unit2;
     
    interface
     
    uses
      Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
      Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
     
    type
      TfrmLogin = class(TForm)
        Button1: TButton;
        Button2: TButton;
        edtLogin: TEdit;
        edtPass: TEdit;
        procedure Button1Click(Sender: TObject);
      end;
     
      // функция авторизации. ее можно вызывать потом и при работе программы (ну мало ли :))
      // проверять результат и принимать решение
      function Authorized: boolean;
     
    implementation
     
    {$R *.dfm}
     
    // отображение окна авторизации
    function Authorized: boolean;
    begin
      with TfrmLogin.Create(Application) do
      begin
        Result:= ShowModal = mrOk;
        Free;
      end;
    end;
     
    // DUMP-функция. проверка карректности данных
    function CheckLogin(const Login,Password: string): boolean;
    begin
      Result:= true;
    end;
     
    procedure TfrmLogin.Button1Click(Sender: TObject);
    begin
      // click on OK button
      if CheckLogin(edtLogin.Text,edtPass.Text) then ModalResult:= mrOk else
      begin
        ShowMessage('Incorrect!');
        edtLogin.Clear;
        edtPass.Clear;
        edtLogin.SetFocus;
      end;
    end;
     
    end.


    главный модуль проекта
    Delphi
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    
    program Project1;
     
    uses
      Vcl.Forms,
      Unit1 in 'Unit1.pas' {frmMain},
      Unit2 in 'Unit2.pas' {frmLogin};
     
    {$R *.res}
     
    begin
      Application.Initialize;
      if not Authorized then Exit;                // добавим всего одну строчку
      Application.MainFormOnTaskbar := True;
      Application.CreateForm(TfrmMain, frmMain);
      Application.Run;
    end.


    да и код по итогу короче получается
    Запись от DenNik размещена 18.08.2017 в 16:41 DenNik на форуме
  2. Старый комментарий
    Аватар для krapotkin
    Не факт. Или не обязательно факт.
    Попробую аргументировать.
    Во-первых, для проверки логина часто нужно подключиться к БД или взять данные из интернета. А настройки подключения считать из файла. Тогда придется и это пихать сюда же. Перебор.
    Более жизненный сценарий старта программы я привел здесь
    Во-вторых, мигания (по крайней мере у меня) не заметно.
    В-третьих, ужасно не люблю вносить правки в DPR.
    Запись от krapotkin размещена 21.08.2017 в 17:22 krapotkin вне форума
    Обновил(-а) krapotkin 21.08.2017 в 17:24
  3. Старый комментарий
    Аватар для DenNik
    Цитата:
    ужасно не люблю вносить правки в DPR.
    почему так?
    это сугубо личный субъективный принцип кодинга?
    Запись от DenNik размещена 22.08.2017 в 10:13 DenNik на форуме
  4. Старый комментарий
    Аватар для krapotkin
    это старая история. IDE бывало знатно глючила. До сих пор иногда беспричинно портит DPR и PAS
    все дело в том, что она пользуется автоматическим парсингом dpr файла
    и когда там много ей непонятного, фиг знает, что у нее слетит )
    так что да, сугубо личное и субъективное. предрассудки типа не дразнить красной тряпкой черную кошку )
    Запись от krapotkin размещена 22.08.2017 в 10:32 krapotkin вне форума
    Обновил(-а) krapotkin 22.11.2017 в 01:33
  5. Старый комментарий
    Аватар для DenNik
    Цитата:
    До сих пор иногда беспричинно портит DPK
    вероятно, DPR )
    да, если строки
    Application.CreateForm(TfrmMain, frmMain);
    Application.Run;

    заключить в begin..end
    а если вариант с Exit, то все good )
    Запись от DenNik размещена 21.11.2017 в 09:58 DenNik на форуме
  6. Старый комментарий
    Аватар для Mr_Cipa
    Здравствуйте. У меня проблема в том, что в форме логина в разделе type не отображаются компоненты Tedit. Следовательно я не могу (не знаю как) вставить в функцию проверки на главной форме... Я что то делаю не так?
    Запись от Mr_Cipa размещена 20.03.2019 в 00:25 Mr_Cipa вне форума
  7. Старый комментарий
    Аватар для krapotkin
    Создайте тему на форуме, там и решим
    Запись от krapotkin размещена 20.03.2019 в 07:35 krapotkin вне форума
  8. Старый комментарий
    Аватар для Usaga
    Цитата:
    Во-первых, для проверки логина часто нужно подключиться к БД или взять данные из интернета. А настройки подключения считать из файла. Тогда придется и это пихать сюда же. Перебор.
    Что "перебор"? Проверка реквизитов пользователя в базе - нормальная часть бизнес-логики.
    Запись от Usaga размещена 20.03.2019 в 09:01 Usaga вне форума
  9. Старый комментарий
    Аватар для Rius
    DenNik +1
    Не надо всё пихать в одну форму, тем более такой костыль, как вызов одного окна из конструктора другого. Программа может всё нужное отработать и до показа каких-либо окон.
    Запись от Rius размещена 20.03.2019 в 09:57 Rius вне форума
  10. Старый комментарий
    Аватар для krapotkin
    перебор - это то, что для произведения данного действия потребуется выполнить действия, с ним непосредственно не связанные
    например, загрузка настроек программы - это наверное не дело формы логина? а если еще в командной строке какие-то параметры идут, перекрывающие считанные настройки? тогда форма логина и их должна разбирать?
    нарушается принцип разделения логики. Этот кусок становится монолитным и непереносимым в другую программу.
    Хотя я уже писал выше, что это вполне имеет место быть удобным/юзабельным для Вас, но неудобно/неприемлемо для меня
    Я описываю РАБОЧИЙ и довольно сбалансированный концептуально вариант. И нигде не говорю, что он единственно верный...
    Запись от krapotkin размещена 20.03.2019 в 10:27 krapotkin вне форума
  11. Старый комментарий
    Аватар для Rius
    А разбор настроек или параметров командной строки - разве дело основной формы?
    У вас монолит и получается, так как вся логика понапихана в одну форму.
    Рабочих вариантов много, красивых мало.
    Запись от Rius размещена 20.03.2019 в 10:49 Rius вне форума
  12. Старый комментарий
    Аватар для Usaga
    Форма не только не должна настройки разбирать\загружать, но и в сама в базу ходить. Всё это должны делать отдельные модули приложения, а из формы всё это только "пинаться" должно. А значит должно существовать до формы и вообще без неё.

    А если для вас возможность из формы логина в базу сходить - перебор, так это показатель того, что у вас уже монолит неповоротливый и никуда не переносимый.
    Запись от Usaga размещена 21.03.2019 в 11:03 Usaga вне форума
  13. Старый комментарий
    Аватар для krapotkin
    ну, то, что вы мне приписываете, вовсе не следует это из моего текста )
    главная форма в конструкторе вызывает чужие методы и для разбора и для обращений к базе
    я уже писал выше, что просто стараюсь не писать ничего в DPR, потому что единственное место, куда можно переместить всю эту логику - туда.

    так что никакого монолита не возникает.
    у меня несколько программ используют модули друг друга
    соответственно, архитектура вполне себе модульная
    Запись от krapotkin размещена 21.03.2019 в 12:46 krapotkin вне форума
  14. Старый комментарий
    Аватар для Rius
    Вообще тут явно говорится: правильно вызывать форму логина из конструктора основной формы. Причём метод проверки логина является членом класса основной формы, и в ней же, очевидно (из показываемого) идёт обращение к БД.
    Что плохого в коде в dpr? Это, как я понимаю, аналог Program.Main() из C#. Нормальное место для подготовки данных, ещё не требующих показа форм.

    В дельфях DI так и не применяется?
    Запись от Rius размещена 21.03.2019 в 20:01 Rius вне форума
  15. Старый комментарий
    Аватар для krapotkin
    обратите внимание. весь пример здесь умещен в 50 строк. Сознательно. Чтобы речь шла о принципе.
    ни о какой БД речи не идет. почему мы не можем проверить логин и пароль например через веб-запрос?

    код в dpr делфи пытается автоматически анализировать. и не думаю, чтобы там стоял мощный AI.
    А когда ей чего-то не удается, я не верю в стойкость ее алгоритмов. Она и напортить может.
    Хотя я абсолютно не возражаю, если опытные программисты будут делать ЭТО в DPR. Но считаю достаточным и разумным компромиссом советовать новичкам делать вот так.
    Например, возникнет вопрос, куда помещать компоненты доступа к БД чтобы проверить логин, если он в базе. Вариант с динамическим созданием точно выше среднего уровня. Вариант с глобальным датамодулем, который создается ДО главной формы уже мне не очень нравится. Я как-то люблю сам создавать и хранить нужные мне вещи. Что и делаю иногда в том же конструкторе. А иногда нет.
    Есть много различных моментов, которые могут повлиять на выбор стратегии. А эта статья не призвана раскрывать тонкости, тут только объясняется принцип - открывать форму логина, не вкладывая в нее никакой логики. Сделано это потому что 99% всяких интернет - находок делают не так.

    Про DI не понял.
    Запись от krapotkin размещена 21.03.2019 в 20:28 krapotkin вне форума
  16. Старый комментарий
    Аватар для Rius
    Внедрение зависимости (англ. Dependency injection, DI)
    Вкратце: создать и считать настройки до формы, распознать все параметры командной строки до формы, передать то и другое в конструктор формы уже готовым к применению.

    Компромисс между "новичкам", "правильнее" и "попроще"... ОК.
    Запись от Rius размещена 21.03.2019 в 20:58 Rius вне форума
  17. Старый комментарий
    Аватар для Usaga
    Можем проверить и через веб-запрос, только с точки зрения потребителя (формы авторизации) разницы нет вообще никакой.

    Ходилки в БД или веб-сервисы и вообще все сервисы создаются до создания формы и внедряются ей в конструктор (DI). К этому моменту все настройки уже разобраны, все параметры командной строки уже распаршены и применены. Какова природа полученного в конструкторе сервиса (БД или веб-сервис) - форме не известно.

    Всё это должно работать и без UI. Вот это правильно и именно такое нужно новичкам прививать. А ужимать пример до пятидесяти строк для "упрощения" архитектуры, когда вы пример этой самой архитектуры показываете... Ну вы поняли...
    Запись от Usaga размещена 22.03.2019 в 08:13 Usaga вне форума
  18. Старый комментарий
    Аватар для Mr_Cipa
    Цитата:
    Сообщение от Usaga Просмотреть комментарий
    Всё это должно работать и без UI. Вот это правильно и именно такое нужно новичкам прививать...
    А не могли бы вы показать наглядно? Как это с вашей точки зрения должно выглядеть в коде.
    Запись от Mr_Cipa размещена 22.03.2019 в 08:26 Mr_Cipa вне форума
  19. Старый комментарий
    Аватар для krapotkin
    нет в делфи необходимости чего-то "внедрять" ))
    создайте объект, ссылку на него используйте, вот и все внедрение. Тогда все в делфи изначально injected )))

    дело в том, что в делфи исторически программа практически ассоциирована со своей главной формой, а не с
    program XXX;
    begin end.
    а требовать, чтобы форма логина и пароля была в программе без UI это вы у себя так делайте, радибоха ))
    буду ждать ваши простые и доступные статьи как рассказать новичкам о создании микросервисной архитектуры

    и я по-прежнему не вижу, откуда взялись обвинения, что ничего не будет работать без UI ? прямо вдруг интересно стало
    Запись от krapotkin размещена 22.03.2019 в 08:47 krapotkin вне форума
    Обновил(-а) krapotkin 22.03.2019 в 08:56
  20. Старый комментарий
    Аватар для Usaga
    Цитата:
    А не могли бы вы показать наглядно? Как это с вашей точки зрения должно выглядеть в коде.
    Речь о разделении функционала на отдельные сервисы со строгими обязанностями. Форма к этим сервисам обращается по мере надобности и не напрямую, а через один из вариантов MVC\MVP\MVVM\Presentation Model\Document-View и тому подобное. В двух словах и формате комментария показывать такое себе)

    Цитата:
    нет в делфи необходимости чего-то "внедрять" ))
    А дельфи не язык программирования общего назначения, чтоли?)))

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

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

    И микросервисы тут причём?
    Запись от Usaga размещена 22.03.2019 в 12:01 Usaga вне форума
    Обновил(-а) Usaga 22.03.2019 в 12:02
 
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2019, vBulletin Solutions, Inc.