R10-04.DOC

(521 KB) Pobierz
Szablon dla tlumaczy

Architektura komponentów: VCL i CLX

 

Niektórzy programiści pamiętają zapewne bibliotekę OWL (Object Windows Library) stanowiącą część Turbo Pascala. Jej pojawienie się uprościło znacznie tradycyjny proces programowania w Windows, czyniąc pracę programistów bardziej efektywną i produktywną — przynajmniej w stosunku do tego, z czym przyszło im się zmagać do tej pory. Obiekty OWL uwol­niły programistę od wielu nużących szczegółów związanych z klasyfikowaniem komunikatów w rozbudowanych instrukcjach case, zarządzaniem oknami itp. Innymi słowy, otwarta została droga do nowej metodologii two­rzenia aplikacji — programowania obiektowego.

Biblioteka VCL wprowadzona w Delphi 1 okazała się godnym następcą OWL, zbudowa­nym jednakże na odmiennych zasadach. W kolejnych wersjach Delphi biblioteka ta ule­gała ciągłemu rozwojowi, chociażby w związku z rozwojem samego Object Pascala, czy też przejściem (w Delphi 2) na platformę 32-bitową; jej natura pozostała jednak jak dotąd niezmieniona. W Delphi 6 pojawił się natomiast element dla niej konkurencyjny — biblioteka CLX, anonsowana przez Borland jako „biblioteka nowej generacji, stanowiąca podstawę tworzenia rodzimych aplikacji dla Windows i Linuksa oraz projektowania komponentów do wielokrotnego użytku”. Zarówno VCL, jak i CLX zaprojektowane zostały do współpracy ze środowiskiem Delphi 6; umożliwia to wizualne tworzenie aplikacji, zamiast żmudnego wpisywania w kod źródłowy fragmentów odpowiadających poszczególnym aspektom funkcjonalności okien.

 

Biblioteki VCL i CLX są dość skomplikowane, a to, w jakim stopniu będziemy musieli zapoznać sie z ich szczegółami, zależy od celu naszej pracy — możemy budować kompletne aplikacje lub projektować nowe komponenty. W pierwszym przypadku wystarczająca jest zazwyczaj znajomość interfejsu używanych komponentów — ich właściwości, metod i zdarzeń — oraz oczywiście zrozumienie zasad programowania obiektowego: enkapsulacji, dziedziczenia i polimorfizmu. Projektowanie nowych komponentów wymaga dogłębnej znajomości wewnętrznych szczegółów bibliotek VCL i CLX, wszak jedną z pierwszych decyzji projektowych jest wybór właściwej klasy bazowej dla nowego komponentu; niezbędna jest też w większości przypadków umiejętność operowania niskopoziomowymi mechanizmami systemów operacyjnych (np. komunikatami Windows), jak również znajomość zasad integracji komponentów z elementami środowiska IDE.

Niniejszy rozdział jest wprowadzeniem do bibliotek VCL i CLX. Przedstawiamy tu ogólną hierarchię ich komponentów oraz ich przeznaczenie, a także najważniejsze ich właściwości, metody i zdarzenia. Na zakończenie zajmiemy się niektórymi szczegółami mechanizmu RTTI.

 

CLX — odsłona pierwsza

Międzyplatformowa biblioteka CLX, będąca jedną z nowości Delphi 6, składa się z czterech elementów wymienionych w tabeli 10.1.

 

Tabela 10.1. Funkcjonalny podział biblioteki CLX

Grupa

Zawartość

VisualCLX

Międzyplatformowe komponenty interfejsu użytkownika i funkcje graficzne. Komponenty dla Windows i Linuksa mogą różnić się między sobą.

DataCLX

Komponenty aplikacji-klientów bazodanowych, realizujące stronę klienta w bazodanowych aplikacjach lokalnych, klient-serwer i wielowarstwowych. Każdy komponent tej grupy jest wspólny dla Windows i Linuksa.

NetCLX

Komponenty internetowe realizujące m.in., technologie Apache DSO i CGI Web Broker. Wspólne dla Windows i Linuksa.

RTL

Biblioteka czasu wykonania (runtime library). Jej kod źródłowy jest identyczny dla Windows i Linuksa, z tym, że pod Linuksem nosi ona nazwę BaseRTL.

 

W niniejszym rozdziale zajmiemy się głównie komponentami z grupy VisualCLX. Zrealizowane zostały na bazie wyprodukowanej przez firmę Troll Tech biblioteki Qt („cute” lub, jak chcą autorzy, „kyu-tee”) i w chwili obecnej obsługują dwie platformy systemowe — Windows i Linuksa.

Co to jest komponent?

Komponenty to podstawowe „cegiełki”, z których autor aplikacji tworzy elementy interfejsu programisty oraz te elementy aplikacji, które co prawda nie są bez­pośrednio widoczne, lecz stanowią nie mniej ważną jej część. Komponenty zmagazynowane są w palecie komponentów, skąd możemy je pobierać na formularz, modyfikować ich właściwości, a także wypełniać treścią procedury zdarzeniowe, określając w ten sposób reakcje komponentu na różnorodne zachowania końcowego użytkownika aplikacji i różnorodne zdarzenia zachodzące w systemie.

Przez twórców nowych komponentów postrzegane są przede wszystkim jako klasy w języku programowania — w Delphi jest to Object Pascal, choć niewykluczone jest użycie w tym celu również C++ i języka asemblera. Funkcjonalna złożoność komponentu ograniczona jest przy tym jedynie inwencją (i zamierzeniami) projektanta, tak więc obok np. prostych etykiet tekstowych (TLabel), mogą w aplikacji istnieć komponenty realizujące kompletne arkusze kalkulacyjne!

Kluczem do zrozumienia bibliotek VCL i CLX jest znajomość różnorodnych typów komponentów zawartych w tych bibliotekach. Pożądane jest zrozumienie wspólnych elementów komponentów, jak również zapoznanie się z hierarchią samych komponentów i przeznaczeniem poszczególnych szczebli tej hierarchii.

Hierarchia komponentów

Rysunki 10.1 i 10.2 przedstawiają hierarchię komponentów w bibliotekach (odpowiednio) VCL i CLX; już na pierwszy rzut oka można zauważyć duże podobieństwo pomiędzy obydwiema bibliotekami.

 

Rysunek 10.1. Hierarchia klas VCL

 

Rysunek 10.2. Hierarchia klas CLX

Komponenty niewizualne

Komponenty niewizualne (nonvisual components) nie są widoczne dla końcowego użytkownika aplikacji, są jednak dostępne w czasie projektowania aplikacji, kiedy to za pomocą inspektora obiektów można modyfikować ich właściwości determinujące określone zachowanie, jak również programować obsługę wybranych zdarzeń. Przykładem komponentów niewizualnych są TTimer, TTable, a także standardowe okienka dialogowe (np. TOpenDialog). Jak widać na rysunkach 10.1 i 10.2, komponenty niewizualne wywodzą się bezpośrednio z klasy TComponent.

Komponenty wizualne

Zgodnie ze swą nazwą, komponenty wizualne są widocznymi dla użytkownika elementami interfejsu graficznego, przy czym niekoniecznie muszą wykazywać przejawy interakcji z użytkownikiem. Komponenty te wywodzą się bezpośrednio z klasy TControl, która definiuje właściwości związane m.in. z „geometrią” i wyglądem komponentu, jak Top, Left, Color itp.

 

Notatka

Należy zdawać sobie sprawę z różnicy pomiędzy często używanymi określeniami „komponent” i „kontrolka” (control); kontrolka jest widzialnym ele­mentem interfejsu użytkownika, natomiast komponentem jest każdy obiekt, który może pojawić się w palecie komponentów, i którego właściwościami można operować na etapie projektowania. Każda kontrolka wywodzi się w Delphi z klasy TControl, będącej z kolei pochodną klasy TComponent — tak więc każda kontrolka jest jednocześnie komponentem, nie każdy komponent jest jednak kontrolką.

 

Wszystkie kontrolki podzielić można na dwie rozłączne grupy: pierwszą grupę stanowią te, które mogą przyjmować skupienie (focus), drugą — wszystkie pozostałe.

Kontrolki zdolne przyjmować skupienie

Ta grupa kontrolek wywodzi się z klasy TWinControl (w bibliotece VCL) lub TWidgetControl (w CLX). Kontrolki bazujące na klasie TWinControl stanowią otoczki standardowych kontrolek Windows (stąd nazywane są często kontrolkami okienkowymiwindowed controls), natomiast kontrolki wywodzące się z klasy TWidgetControl „obudowują” obiekty ekranowe biblioteki Qt. Wśród najważniejszych cech charakteryzujących opisywane kontrolki wymienić należy następujące:

 

·         mogą przyjmować skupienie i tym samym stanowić obiekt docelowy dla zdarzeń generowanych przez klawiaturę;

·         posiadają elementy interakcji z użytkownikiem;

·         mogą pełnić rolę kontrolek rodzicielskich (parents) dla innych kontrolek, zwanych kontrolkami potomnymi (childrens);

·         posiadają właściwość Handle, stanowiącą łącznik z odpowiednim mechanizmem systemu operacyjnego.

 

Notatka

Właściwość Handle występuje zarówno w klasie TWinControl, jak i TWidgetControl. W klasie TWinControl reprezentuje ona okno związane z kontrolką i zawiera fizycznie uchwyt tego okna, natomiast w klasie TWidgetControl stanowi wskaźnik do odpowiedniego obiektu ekranowego; wskaźnik ten nazywany bywa często gadżetem. Fakt, iż rzeczona właściwość posiada tę samą nazwę w obydwu bibliotekach (VCL i CLX) przyczynia się do łatwiejszego tworzenia aplikacji międzyplatformowych, upraszcza też proces przenoszenia aplikacji z VCL do CLX.

 

Dalszymi szczegółami kontrolek TWinControl i TWidgetControl zajmiemy się w następnych rozdziałach.

<ramka>

Uchwyty

Uchwytami (handles) nazywamy 32-bitowe liczby całkowite, stanowiące wskaźniki do określonych obiektów Win32. Tych ostatnich nie należy mylić z obiektami Delphi — w Win32 występują trzy rodzaje obiektów:

·          obiekty jądra (kernel) — na przykład zdarzenia, pamięciowe odwzorowania plików (memory-mapped files) i procesy;

·          obiekty użytkownika (user) — kontrolki edycyjne, listy, przyciski itp.

·          obiekty graficzne (GDI) — bitmapy, pędzle, pióra, czcionki itd.

Każde okno w Win32 posiada unikatowy uchwyt, który reprezentuje je w odwołaniach do funkcji API. Dzięki Delphi użytkownik uwolniony jest w większości przypadków od operowania tymi funkcjami (na rzecz operowania równoważnymi metodami obiektów), jeżeli jednak uchwyt odnośnego okna okazuje się niezbędny, jest dostępny pod właściwością Handle.

<ramka>

Kontrolki niezdolne do przyjmowania skupienia

Niektóre kontrolki, mimo iż widoczne na ekranie, nie są w stanie przyjmować skupienia, nie posiadają więc zdolności interakcji z użytkownikiem. Są one (przeważnie) używane jedynie do wyświetlania elementów interfejsu i z tego względu nazywane są kontrolkami graficznymi (graphical controls). Ich klasą bazową jest TGraphicControl (por. rysunki 10.1 i 10.2).

Ponieważ kontrolki graficzne nie mogą przyjmować skupienia, nie posiadają reprezentującego je uchwytu (lub w CLX — gadżetu). Skutkuje to mniejszym (w stosunku do kontrolek okienkowych) obciążeniem zasobów, lecz niesie ze sobą pewne ograniczenia: kontrolki graficzne nie mogą posiadać kontrolek potomnych. Przykładami kontrolek graficznych są m.in. TImage, TBevel i TPaintBox.

Struktura komponentu

Każdy komponent jest obiektem języka Object Pascal, mieszczącym w sobie wybrane aspekty i mechanizmy, których używa projektant tworzący aplikację. Będąc obiektem, każdy komponent posiada naturalne cechy obiektowe (pola, metody i właściwości); należy jednakże do domeny określonej klasy (TComponent) i posiada również cechy charakterystyczne tylko dla niej.

Notatka

Należy odróżnić od siebie pojęcia „klasy” i „komponentu”: klasa jest strukturą języka Object Pascal, natomiast komponent jest klasą posiadającą możliwości współpracy ze środowiskiem IDE.

 

Właściwości komponentu

Idea właściwości (properties) została opisana w rozdziale 2. Są one mechanizmem umożliwiającym odczytywanie oraz przypisywanie wartości polom komponentu, w sposób określony przez projektanta. Zaś same przedmiotowe pola są najczęściej polami ukrytymi przed użytkownikiem (posiadają kategorię private lub protected), co zabezpiecza je przed niekontrolowaną modyfikacją.

Dostęp do pól za pośrednictwem właściwości

Dzięki mechanizmowi właściwości, odczytywanie i zmiana wartości pól obiektu nie dokonują się już za pomocą zwykłego kopiowania wartości; definicja właściwo­ści może spowodować, że przypisanie (odczytanie) wartości będzie się wiązać z wykona­niem pewnej metody. Spójrzmy na poniższy przykład:

TCustomEdit = class(TWinControl)

Private

  FMaxLength: integer;

Protected

  procedure SetMaxLength(Value: integer);

published

  Property MaxLength: Integer read FMaxLength write SetMaxlength default 0;

end;

Deklaracja właściwości MaxLength stanowi, iż reprezentowana przez nią wartość bę­dzie typu integer. Klauzule read i write określają sposób przypisywania wartości i jej odczytywania.

Po słowie read występuje nazwa pola FMaxLength, co oznacza, że odczytanie warto­ści właściwości MaxLength jest równoważne odczytaniu tego właśnie pola. Po słowie write występuje jednakże nazwa metody — a to oznacza, że przypisanie właściwości MaxLength no­wej wartości odbywa się przez wykonanie tej metody. Jeżeli więc w tekście programu wystąpią następujące instrukcje:

L := X.MaxLength;

......

X.MaxLength := K

to fizycznie będą one równoważne następującym instrukcjom:

L := X.FMaxLength;

.....

X.SetMaxLength(K);

 

Gdyby w klauzuli read również określić nazwę metody

  Property MaxLength: Integer read GetMaxLength write SetMaxlength default 0;

 

instrukcje

L := X.MaxLength;

......

X.MaxLength := K;

 

byłyby równoważne następującym:

L := X.GetMaxlength();

......

X.SetMaxLength(K);

Pomijając jedną z klauzul, read lub write, możemy uczynić właściwość tylko odczyty­walną lub tylko zapisywalną.

Klauzula default określa wartość domyślną właściwości jako 0 — ma ona znacze­nie przy zapisywaniu komponentu do strumienia; zajmiemy się tym zagadnieniem w na­stępnym rozdziale.

Metody dostępowe właściwości

W poprzednim przykładzie przypisanie nowej wartości właściwości MaxLength realizowane było jako wywołanie metody SetMaxLength. Metody obiektu, których identyfikatory pojawiają się w klauzulach read lub write deklaracji właściwości noszą nazwę metod dostępowych (access methods); nazwa ta dość trafnie określa ich przeznaczenie — zapewniają dostęp do wartości właściwości oraz do samej właściwości (w celu jej modyfikacji).

Metoda dostępowa przypisująca właściwości nową wartość musi być jednoparametrową procedurą, a typ jej parametru — tożsamy z typem właściwości (wyjątkiem są właści...

Zgłoś jeśli naruszono regulamin