LEKCJA 46: O PROGRAMACH OBIEKTOWO - ZDARZENIOWYCH. ________________________________________________________________ Po aplikacjach sekwencyjnych, proceduralno-zdarzeniowych, jedno- i dwupoziomowych, pora rozwa�y� dok�adniej stosowanie technik obiektowych. ________________________________________________________________ Programy pracuj�ce w �rodowisku Windows tworzone s� w oparciu o tzw. model tr�jwarstwowy. Pierwsza warstwa to warstwa wizualizacji, druga - interfejs, a trzecia - to w�a�ciwa maszyneria programu. W tej lekcji zajmiemy si� "anatomi�" aplikacji wielowarstwowych a nast�pnie sposobami wykorzystania bogatego instrumentarium oferowanego przez Borlanda wraz z kompilatorami BC++ 3+...4+. Biblioteka OWL w wersjach BORLAND C++ 3, 3.1, 4 i 4.5 zawiera definicje klas potrzebnych do tworzenia aplikacji dla Windows. Fundamentalne znaczenie dla wi�kszo�ci typowych aplikacji maj� nast�puj�ce klasy: TModule (modu� - program lub biblioteka DLL) TApplication (program - aplikacja) TWindow (Okno) Rozpoczn� od kr�tkiego opisu dwu podstawowych klas. KLASA TApplication. Tworz�c obiekt klasy TNaszProgram b�dziemy wykorzystywa� dziedziczenie od tej w�a�nie klasy bazowej: class TNaszProgram : public TApplication Podstawowym celem zastosowania tej w�a�nie klasy bazowej jest odziedziczenie gotowej funkcji - metody virtual InitMainWindow() (zainicjuj g��wne okno programu). Utworzenie obiektu klasy TNaszProgram nast�puje zwykle w czterech etapach: * Windows uruchamiaj� program wywo�uj�c g��wn� funkcj� WinMain() lub OwlMain() wchodz�c� w sk�ad ka�dej aplikacji. * Funkcja WinMain() tworzy przy pomocy operatora new nowy obiekt - aplikacj�. * Obiekt - aplikacja zaczyna funkcjonowa�. Konstruktor obiektu (w�asny, b�d� odziedziczony po klasie TApplication) wywo�uje funkcj� - wirtualn� metod� InitMainWindow(). * Funkcja przy pomocy operatora new tworzy obiekt - okno aplikacji. Wska�nik do utworzonego obiektu zwraca funkcja GetApplication(). Dla zobrazowania mechanizm�w poni�ej przedstawiamy uproszczony "wyci�g" z dwu opisywanych klas. Nie jest to dok�adna kopia kodu �r�d�owego Borlanda, lecz skr�t tego kodu pozwalaj�cy na zrozumienie metod implementacji okienkowych mechanizm�w wewn�trz klas biblioteki OWL i tym samym wewn�trz obiekt�w obiektowo - zdarzeniowych aplikacji. A oto najwa�niejsze elementy implementacji klasy TApplication: - Konstruktor obiektu "Aplikacja": TApplication::TApplication(const char far* name, HINSTANCE Instance, HINSTANCE prevInstance, const char far* CmdLine, int CmdShow, TModule*& gModule) { hPrevInstance = prevInstance; nCmdShow = CmdShow; MainWindow = 0; HAccTable = 0; //Accelerator Keys Table Handle BreakMessageLoop = FALSE; AddApplicationObject(this); //this to wska�nik do w�asnego gModule = this; //obiektu, czyli do bie�. aplikacji } Funkcja - metoda "Zainicjuj Instancj�": void TApplication::InitInstance() { InitMainWindow(); if (MainWindow) { MainWindow->SetFlag(wfMainWindow); MainWindow->Create(); MainWindow->Show(nCmdShow); } Metoda "Zainicjuj g��wne okno aplikacji": void TApplication::InitMainWindow() { SetMainWindow(new TFrameWindow(0, GetName())); } Metoda Run() - "Uruchom program": int TApplication::Run() { int status; { if (!hPrevInstance) InitApplication(); InitInstance(); status = MessageLoop(); } A oto p�tla pobierania komunikat�w w uproszczeniu. "Pump" to po prostu "pompowanie" komunikat�w (message) oczekuj�cych (waiting) w kolejce. PeekMessage() to sprawdzenie, czy w kolejce oczekuje komunikat. PM_REMOWE to "brak komunikatu". BOOL TApplication::PumpWaitingMessages() { MSG msg; BOOL foundOne = FALSE; while (::PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) { foundOne = TRUE; if (msg.message == WM_QUIT) { BreakMessageLoop = TRUE; MessageLoopResult = msg.wParam; ::PostQuitMessage(msg.wParam); break; } if (!ProcessAppMsg(msg)) { ::TranslateMessage(&msg); ::DispatchMessage(&msg); } } return foundOne; } int TApplication::MessageLoop() { long idleCount = 0; MessageLoopResult = 0; while (!BreakMessageLoop) { TRY { if (!IdleAction(idleCount++)) ::WaitMessage(); if (PumpWaitingMessages()) idleCount = 0; } if (MessageLoopResult != 0) { ::PostQuitMessage(MessageLoopResult); break; } }) } BreakMessageLoop = FALSE; return MessageLoopResult; } else if (::IsWindowEnabled(wnd)) { *(info->Wnds++) = wnd; ::EnableWindow(wnd, FALSE); } } return TRUE; } KLASA TWindow. Klasa TWindow (Okno) zawiera implementacj� wielu przydatnych przy tworzeniu aplikacji "cegie�ek". Poni�ej przedstawiono fragment pliku �r�d�owego (patrz \SOURCE\OWL\WINDOW.CPP). �atwo mo�na rozpozna� pewne znane ju� elementy. ... extern LRESULT FAR PASCAL _export InitWndProc(HWND, UINT, WPARAM, LPARAM); ... struct TCurrentEvent //Struktura Bie��ceZdarzenie { TWindow* win; //Wska�nik do okna UINT message; //Komunikat WPARAM wParam; LPARAM lParam; }; ... DEFINE_RESPONSE_TABLE(TWindow) //Makro: Zdefiniuj tablic� odpowiedzi na zdarzenia //EV_WM_SIZE - Zdarzenie (EVent)-nadszed� komunikat WM_SIZE ... EV_WM_SETCURSOR, EV_WM_SIZE, EV_WM_MOVE, EV_WM_PAINT, EV_WM_LBUTTONDOWN, EV_WM_KILLFOCUS, EV_WM_CREATE, EV_WM_CLOSE, EV_WM_DESTROY, EV_COMMAND(CM_EXIT, CmExit), ... END_RESPONSE_TABLE; Funkcje - metody obs�uguj�ce komunikaty zaimplementowane zosta�y wewn�trz klasy TWindow tak: TWindow::EvCreate(CREATESTRUCT far&) { SetupWindow(); return (int)DefaultProcessing(); } void TWindow::EvSize(UINT sizeType, TSize&) { if (Scroller && sizeType != SIZE_MINIMIZED) { Scroller->SetPageSize(); Scroller->SetSBarRange(); } } Metoda GetWindowClass() bardzo przypomina klasyczne zainicjowanie zanej ju� struktury WNDCLASS: void TWindow::GetWindowClass(WNDCLASS& wndClass) { wndClass.cbClsExtra = 0; wndClass.cbWndExtra = 0; wndClass.hInstance = *GetModule(); wndClass.hIcon = 0; wndClass.hCursor = ::LoadCursor(0, IDC_ARROW); wndClass.hbrBackground = HBRUSH(COLOR_WINDOW + 1); wndClass.lpszMenuName = 0; wndClass.lpszClassName = GetClassName(); wndClass.style = CS_DBLCLKS; wndClass.lpfnWndProc = InitWndProc; } Skoro te wszystkie "klocki" zosta�y ju� zaimplementowane wewn�trz definicji klas, nasze programy powinny tylko umiej�tnie z nich korzysta� a teksty �r�d�owe program�w powinny ulec skr�ceniu i uproszczeniu. STADIA TWORZENIA OBIEKTOWEJ APLIKACJI. Poniewa� znakomita wi�kszo�� dzisiejszych u�ytkownik�w pracuje z Windows 3.1, 3.11, i NT - zaczniemy tworzenie aplikacji od umieszczenia na pocz�tku informacji dla OWL, �e nasz docelowy program ma by� przeznaczony w�a�nie dla tego �rodowiska: #define WIN31 Jak ju� wiemy dzi�ki kr�tkiemu przegl�dowi struktury bazowych klas przeprowadzonemu powy�ej - funkcje API Windows s� w istocie klasycznymi funkcjami pos�uguj�cymi si� mechanizmami j�zyka C. C++ jest "pedantem typologicznym" i przeprowadza dodatkowe testowanie typ�w parametr�w przekazywanych do funkcji (patrz "Technika programowania w C++"). Aby u�atwi� wsp�prac�, zwi�kszy� poziom bezpiecze�stwa i "uregulowa�" potencjalne konflikty - dodamy do programu: #define STRICT Chc�c korzysta� z biblioteki OWL wypada do��czy� w�a�ciwy plik nag��wkowy: #include <owl.h> Plik OWL.H zawiera ju� wewn�trz do��czony WINDOWS.H, kt�ry wyst�powa� we wcze�niejszych aplikacjach proceduralno - zdarzeniowych i jeszcze par� innych plik�w. Poniewa� chcemy skorzysta� z gotowych zasob�w - odziedziczymy pewne cechy po klasie bazowej TApplication. Zgodnie z zasadami programowania obiektowego chc�c utworzy� obiekt musimy najpierw zdefiniowa� klas�: class TOkno ... i wskaza� po kt�rej klasie bazowej chcemy dziedziczy�: class TOkno : public TApplication { ... Konstruktor obiektu klasy TOkno powinien tylko przekaza� parametry konstruktorowi klasy bazowej - i ju�. class TOkno : public TApplication { public: TOkno(LPSTR name, HANDLE hInstance, HANDLE hPrevInstance, LPSTR lpCmdLine, int nShow) : TApplication(name, hInstance, hPrevInstance, lpCmdLine, nShow) { return; } virtual void InitMainWindow(); }; Umie�cili�my w definicji klasy jeszcze jedn� funkcj� inicjuj�c� g��wne okno aplikacji. Mo�emy j� zdefiniowa� np. tak: void TOkno::InitMainWindow(void) { MainWindow = new (TWindow(0, "Napis - Tytul Okna")); } Dzia�anie funkcji polega na utworzeniu nowego obiektu (operator new) klasy bazowej TWindow. G��wne okno stanie si� zatem obiektem klasy TWindow (Niekt�re specyficzne aplikacje pos�uguj� si� okienkiem dialogowym jako g��wnym oknem programu. W takiej sytuacji dziedziczenie powinno nast�powa� po klasie TDialog). Konstruktorowi tego obiektu przekazujemy jako parame...
ZAZZY