Riddle napisał(a):
A co jest nie tak z wydzieleniem kodu do funkcji tak jak napisałem, i potem tych dwóch funkcji do klasy? Wtedy nie mamy problemu z niepotrzebnie widocznymi funkcjami.
Nieważne jak się wspólne fragmenty wydzieli, wymusi to naklepanie większej ilości kodu oraz wymusi duplikację (wywołań tych funkcji). Plus wydzielenie fragmentów do funkcji innych niż osadzone (czyli np. do prywatnych metod klasy) spowoduje spuchnięcie klasy okna i uwidoczni logikę, która nie powinna być widoczna. Najgorszym rozwiązaniem jest wydzielenie tych fragmentów do osobnej klasy, bo wymusi to naklepanie jeszcze większej ilości kodu, uwidoczni całą logikę tych fragmentów (bagatela jednorazowych) i duplikację wywołań, a także wymusi przekazywanie danych w parametrach (lub operowanie na globalnej referencji formularza).
To co podałem wyżej to był krótki i prosty przykład, acz obsługujący wiele rozgałęzień. Teraz spróbuj wykluczyć goto
w poniższym zdarzeniu — tym razem zdarzenie kontrolujące rozkaz zamknięcia projektu. Dwie flagi (Modified
oraz Stored
), cztery różne dialogi potwierdzające (cztery różne sytuacje, bo cztery kombinacje stanów tych dwóch flag) plus możliwość powtórzenia wyboru pliku docelowego.
Logika siedzi w jednym zdarzeniu, brak duplikacji, brak obfuskacji pętlami, brak uwidoczniania jednorazowych fragmentów, obsługa absolutnie wszystkich przypadków (i co istotne, w odpowiedniej kolejności), kod krótki, zwięzły, z jawnym przepływem sterowania, łatwy do zrozumienia i wybitnie prosty do debuggowania.
procedure TFormMain.MenuItemFileCloseClick(ASender: TObject);
label
ProjectClose, DialogProjectOpen, DialogProjectPrepare;
begin
if not FontsEditor.Project.Modified then
if FontsEditor.Dialogs.Message.Execute('Chcesz zamknąć projekt?', MB_ICONQUESTION or MB_YESNO or MB_DEFBUTTON2) = mrYes then
goto ProjectClose
else
exit;
if FontsEditor.Project.Stored then
case FontsEditor.Dialogs.Message.Execute('Chcesz zapisać zmiany?', MB_ICONWARNING or MB_YESNOCANCEL or MB_DEFBUTTON3) of
mrCancel: exit;
mrNo: goto ProjectClose;
mrYes:
if FontsEditor.Project.SaveToFile(FontsEditor.Project.FileName, True) then
goto ProjectClose
else
begin
FontsEditor.Dialogs.Message.Execute('Błąd zapisu, nie zamykam projektu.', MB_ICONERROR or MB_OK or MB_DEFBUTTON1);
exit;
end;
end
else
case FontsEditor.Dialogs.Message.Execute('Chcesz wybrać plik i zapisać projekt?', MB_ICONWARNING or MB_YESNOCANCEL or MB_DEFBUTTON3) of
mrCancel: exit;
mrNo: goto ProjectClose;
mrYes: goto DialogProjectPrepare;
end;
DialogProjectPrepare:
DialogSaveProject.FileName := 'new font.bin';
DialogSaveProject.InitialDir := GetCurrentDir();
DialogProjectOpen:
if DialogSaveProject.Execute() then
if FontsEditor.Project.SaveToFile(DialogSaveProject.FileName, True) then
goto ProjectClose
else
if FontsEditor.Dialogs.Message.Execute('Błąd zapisu, chcesz wybrać inny plik?', MB_ICONERROR or MB_YESNO or MB_DEFBUTTON2) = mrYes then
goto DialogProjectOpen;
exit;
ProjectClose:
FontsEditor.Project.Close();
// Aktualizacja kontrolek interfejsu.
end;
Treść messageboxów uprościłem, żeby łatwiej było zrozumieć o co program pyta użytkownika.