Delphi i nie tylko. Efektywne programowanie

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.