Delphi i nie tylko. Efektywne programowanie

25% Status
Z Wikibooks, biblioteki wolnych podręczników.

Wstęp[edytuj]

Niniejszy podręcznik ma na celu nauczenie cię efektywnego programowania i usuwania błędów w środowisku Borland Delphi, choć niektóre informacje mogą być użyteczne także w innych IDE. Zakładam, że znasz podstawy języka Object Pascal i obsługi środowiska Delphi. Jeśli nie, możesz kliknąć w powyższe linki aby zaznajomić się z informacjami.

Sztuczki[edytuj]

Zmniejszenie ilości kodu[edytuj]

Dużo nie kodować, dobrze programować

Możemy zaoszczędzić sobie pisania, a program będzie wykonywał to samo. W tym rozdziale podam kilka sztuczek.

Operacje występujące jedna po drugiej[edytuj]

W DOS-ie niektóre klawisze klawiatury powodują przekazanie do programu kodów: 0 (zero) oraz kod klawisza. Na przykład klawiszowi F1 odpowiada sekwencja: 0, 59. Kod mało doświadczonego programisty wyglądał by tak:

if readkey=#0 then if readkey=#59 then instrukcja;

I teraz przydaje się sztuczka:

if (readkey=#0) and (readkey=#57) then instrukcja;

Program przed wykonaniem operacji AND wykona obie instrukcje, w kolejności. Dlatego instrukcja wykona się tylko wtedy, gdy w buforze klawiatury będą znaki #0 i #57. Sztuczka ta jest bardzo przydatna także w pętlach, np. repeat - until. Przykład kodu niepotrzebnie wydłużonego (wyjście z pętli po wciśnięciu F1):

repeat
 ...
 instrukcje;
 ...
 if readkey=#0 then if readkey=#59 then instrukcja;
until false;

I kod skrócony:

repeat
 ...
 instrukcje;
 ...
until (readkey=#0) and (readkey=#57);
Ćwiczenie

Przekształć poniższy kod na postać skróconą. Program reaguje na wpisanie kolejno ciągów "Delphi", "jest", "fajny".

procedure TForm1.Button1Click(Sender: TObject);
begin
 if InputBox('Wpisz', 'Wpisz tekst', ' ')='Delphi' then
  if InputBox('Wpisz', 'Dobrze, więc...', ' ')='jest' then
   if InputBox('Wpisz', 'no i...', ' ')='fajny' then ShowMessage('Zgoda!');
end;

Zobacz rozwiązanie ćwiczenia.

Istniejące funkcje[edytuj]

Niektórzy nie wiedzą o przydatnych procedurach/funkcjach (dostępnych zwłaszcza w VCL). Oto lista takich funkcji (na początku podano nazwę typu, o ile funkcja jest w klasie):

  • TMemo.Lines.Clear - czyści zawartość komponentu TMemo
  • MaxValue, MaxIntValue - parametr jest tablicą wypełnioną wartościami Double (MaxValue) lub Integer (MaxIntValue). Funkcja zwraca maksymalną wartość (nie klucz!) ze wszystkich.
  • W Turbo Pascalu: Str, Val - zamieniają liczbę na string lub odwrotnie. Szczegółowe informacje w pomocy Turbo Pascala.

Tworzenie funkcji dla zrytualizowanych czynności[edytuj]

W Turbo Pascalu brakuje użytecznych funkcji IntToStr oraz StrToInt. Zamiast męczyć się ze zmiennymi przy Str i Val, można utworzyć funkcje - ich odpowiedniki. Te, i inne często używane funkcje można umieścić w unit'cie i korzystać z nich w swoich programach.

Ćwiczenie: zrób takie funkcje, umieść je w unicie "DelphiFunc". Dodaj też typ bool, będący aliasem typu boolean. Rozwiązania możesz znaleść tutaj.

Komponenty największym wrogiem (inteligentnych) programistów[edytuj]

Skąd taki tytuł? Bo właściwości komponentu trzeba dłuuugo pisać! Np. aby zrobić właściwość, której zmiana aktualizowała by komponent, należy kodować tak:

type TKomponent=class(TPersistant)
 ...
 private
  FWlasciwosc1: word;
  FWlasciwosc2: integer;
  procedure SetWlasciwosc1(value:word);
 ...
 published
  property Wlasciwosc1: word read FWlasciwosc1 write SetWlasciwosc1;
  property Wlasciwosc2: integer read FWlasciwosc2 write SetWlasciwosc2;
  ...

implementation
 ...
 procedure TKomponent.SetWlasciwosc1(value:word);
 begin
  FWlasciwosc1 := value;
  Aktualizuj;
 end;
 
 procedure TKomponent.SetWlasciwosc2(value:word);
 begin
  FWlasciwosc2 := value;
  Aktualizuj;
 end;
 
 ...

Z pomocą przychodzą tu rekordy. Wystarczy zadeklarować typ rekordu i jedną właściwość, jedną zmienną pomocniczą, jedną procedurę:

type 
 TWlasciwosci=record
  Wlasciwosc1:word;
  Wlasciwosc2:integer;
 end;

 TKomponent=class(TPersistant)
 ...
 private
  FWlasciwosci: TWlasciwosci;
  procedure SetWlasciwosc1(value:TWlasciwosci);
 ...
 published
  property Wlasciwosci: TWlasciwosci read FWlasciwosci write SetWlasciwosci;
 ...

implementation
 ...
 procedure TKomponent.SetWlasciwosci(value:TWlasciwosci);
 begin
  FWlasciwosci := value;
  Aktualizuj;
 end;
 ...

Można też skorzystać z generatora kodu, obecnie pracuje nad jednym.

Edycja w Delphi - przydatne sztuczki[edytuj]

Code Completion a nieznane typy[edytuj]

Jeśli nie znasz jakiegoś typu, np. klasy komponentu, nie musisz zaglądać do pomocy! Najpierw w dowolnej procedurze (np. TForm.FormCreate) napisz nazwę typu, kropkę i wciśnij Ctrl+Spacja. Jeśli procedur jest dużo, a chcesz wyszukać np. czyszczącą, wpisz Clear. Przykładowy, fikcyjny komponent może wyświetlić następującą listę po wpisaniu "TFikcyjnyKomponent.Clear:

procedure ClearAll;
procedure ClearItems;
function ClearS : boolean;

Teraz już wiesz, jakie funkcje czyszczące zawiera ta klasa. Możesz ich użyć w bardziej sensownym fragmencie kodu. Ale to jeszcze nie wszystko! Jeśli klasa zawiera inną klasę lub rekord, możesz zrobić to samo:

  • Wpisz TFikcyjnyKomponent.Items i wciśnij Ctrl+Spacja
  • Przykładowe elementy CodeCompletion:
   property ItemsList: TFikcyjneItemy;
   property ItemsImages: TFikcyjneObrazki;
   function ItemValue(index:word): TFikcyjnyItem;
  • Wpisz np. ItemsImages.
  • Pojawi się lista wszystkich elementów
  • Wpisz np. Total (będzie to: TFikcyjnyKomponent.ItemsImages.Total)
  • Pojawi się:
   function TotalItems: word;
   function TotalSize: Cardinal;
  • Już wiadomo, jakie funkcje podające informacje na temat całości zawiera ta klasa.

Kompilacja w trakcie tworzenia programu[edytuj]

Jeśli nie jesteś pewien działania jakiegoś fragmentu kodu, np. czy w Turbo Pascalu można porównywać rekordy (podpowiem, że NIE), możesz skompilować program bez uruchamiania. Wtedy wyświetlą się wszystkie błędy składni i inne czasu kompilacji. Zrób tak po jednej "niepewnej" instrukcji, aby nie trzeba było poprawiać więcej.

Efektywne debugowanie[edytuj]

Kolejnym problemem programistów jest to, że program nie chce działać tak, jak trzeba. W takim przypadku:

  1. Sprawdź, czy na pewno dobrze obsługujesz program (jako użytkownik)
  2. Przejrzyj kod, popraw ewentualne zauważone błędy
  3. Sprawdź ponownie, jeśli nie działa:
  4. Załóż breakpoint na pierwszą instrukcję z felernej procedury
  5. Uruchom program, włącz tę funkcję
  6. Sprawdzaj, jak zachowuje się program, poprawiaj błędy
  7. Jeśli do tej pory nie wyeliminowałeś błędu, obejrzyj DOKŁADNIE kod, zwróć szczególną uwagę na powtarzające się fragmenty
  8. Nie ma rady, baw się watches'ami
  9. Jeśli użycie podglądu (i zmiany) zmiennych nie daje efektów, napisz na forum dyskusyjne, polecam 4programmers.net lub forum.ks-ekspert.pl

Obsługa narzędzi do debugowania w Delphi[edytuj]

(w planach)

Pewien przykład[edytuj]

Oto przykład kodu, nad którym się sporo natrudziłem, doszedłem do siódmego punktu i udało mi się "odbugować" (błąd został podkreślony).

 ...
 AssignFile(f1, Edit1.Text);
 ReSet(f1);
 AssignFile(f2, Edit1.Text);
 ReSet(f2);
 ...

A mianowicie program służy do porównywania plików, a tu... porównuje dwa takie same! Kod poprawny:

 ...
 AssignFile(f1, Edit1.Text);
 ReSet(f1);
 AssignFile(f2, Edit2.Text);
 ReSet(f2);
 ...

Zakończenie[edytuj]

Rozwiązania ćwiczeń[edytuj]

Ćwicz. "Operacje występujące..."[edytuj]

Kod rozwiązania:

procedure TForm1.Button1Click(Sender: TObject);
begin
 if (InputBox('Wpisz', 'Wpisz tekst', ' ')='Delphi') and 
    (InputBox('Wpisz', 'Dobrze, więc...', ' ')='jest') and
    (InputBox('Wpisz', 'no i...', ' ')='fajny') then ShowMessage('Zgoda!');
end;

Ćwicz. "Tworzenie funkcji..."[edytuj]

unit DelphiFunc;
interface
 type bool = boolean;
 function IntToStr(i : Integer) : String;
 function StrToInt(s : String) : Integer;

implementation
 function IntToStr(i : Integer) : String;
 var s : String;
 begin
  Str(i, s);
  IntToStr := s;
 end;
 
 function StrToInt(s : String) : Integer;
 var i, c : Integer;
 begin
  Val(s, i, c);
  if c <> 0 then i := 0;
  StrToInt := i;
 end;
end.