Temat: Microsoft WebMatrix - kwas czy nowy Access?
Borysław Bobulski:
W WinAPI nie podoba mi się architektura, która utrudnia programowanie zorientowane obiektowo.
Programowanie obiektowe jest świetne, ale nie wszędzie. Im niżej schodzisz w poziomach abstrakcji, tym bardziej obiektówka staję się przeszkodą niż ułatwieniem.
Jak wyobrażasz sobie obiektową implementację takich rzeczy jak np procedurę HeapAlloc?
Na przykład:
Chcę mieć projekt, gdzie całą funkcjonalność zamknę w klasy, tak żeby nic sobie nie wisiało w powietrzu poza klasą. Powiedzmy, że chcę mieć klasę tworzącą mi okienka dla mojej aplikacji. Jak mam się pozbyć np. tego:
LRESULT CALLBACK WindowProcedure(HWND hWnd, UINT message,
WPARAM wParam, LPARAM lParam);
Nie za bardzo jest to jak wrzucić jako część mojej klasy Window. Będzie sobie wisiało poza klasą i będzie ogólnie dostępne. Mało tego, teraz cały interfejs graficzny użytkownika muszę zawrzeć w tej metodzie. (...)
który powinien być w mojej klasie, jest na zewnątrz niej - bo muszę zrobić całą obsługę komunikatów wewnątrz WindowProcedure. I będzie tego sporo poza klasą...
HDC hdc = GetDC(hWnd);
switch(message)
{
case WM_CLOSE: ...
case WM_DESTROY: ... itd
}
No i w którymś momencie muszę "na sztywno" w mojej klasie Window dać wywołanie tej ściśle określonej konkretną nazwą funkcji WindowProcedure(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam), zanim zarejestruję klasę okna
wndclassex.lpfnWndProc = WindowProcedure;
(...)
RegisterClassEx(&wndclassex);
Teoretycznie, to cały kod aplikacji musisz zawrzeć w main(), bo to jest jedyna funkcja którą system operacyjny odpali w Twoim programie, jednak w praktyce jest tak, że w main() masz tylko odwołanie do innych części aplikacji, które robią wszystko.
Tak samo z okienkami. Nieważne ile byś nie robił poziomów abstrakcji, to zawsze musi być miejsce gdzie będziesz zapisywać aktualny komunikat a w innym miejscu stale pętlą sprawdzał czy wartość tamtej zmiennej odpowiada jakiemuś komunikatowi a następnie reagować. Inaczej się po prostu nie da :)
Oczywiście, jak nie chcesz bezpośrednio się bawić w pętle jak te, to możesz użyć innych rzeczy takich jak CWnd z MFC, Window z WinForms, i takich podobnych, ale pamiętaj, że wszystkie i tak będą opierały się właśnie na takiej pętli.
Uważam to jednak za plus, że masz możliwość (a nie jesteś zmuszony) operować w API systemu operacyjnego na tak niskim poziomie jak ręczna manipulacja pętlą okna.
C#-powe eventy, sloty w QT czy wszystkie takie abstrakcje są zbudowane na bazie najprymitywniejszych mechanizmów takich jak tablice, pętle, wskaźniki, itd... Pomyśl w ten sposób: ile musieli Ci architekci nagłowić się nad tym, żeby udostępnić tak niskopoziomowy mechanizm zachowując przy tym bezpieczeństwo.
Gdyby nie dostęp do takich rzeczy jak te pętle, to Windows byłby tak niemodyfikowalny jak Mac ;-)
A jednak, możesz zmienić nie tylko tło, ale i praktycznie zachowanie każdego elementu systemu, nie narażając go na niestabilność.
Przykład 2:
Chcę sobie stworzyć klasę Timer, której będę "podpinał" jakąś określoną akcję, która się będzie powtarzała, co jakiś czas. Piszę sobie i piszę... I oczywiście znów WinAPI irytuje.
Co z tego, że w klasie Timer użyję metod dajmy na to takich:
SetTimer(FWindowHandle, Timer_IDEvent, FInterval, null);
KillTimer(FWindowHandle, Timer_IDEvent);
Skoro potrzebuje durnowatego uchwytu na... okno! I tak, oczywiście - wewnątrz kolejnej, zawieszonej w powietrzu metody WindowProcedure umieszczę mój kod, w którym będę przechwytywał tylko i wyłącznie jeden typ komunikatu.
Zauważcie. Jeśli skorzystam z kodu w przykładzie 1, to już mam 3 miejsca w których przechowuje dane.
1 - Klasa Timer (ustawianie Interval, ustawianie wskaźnika na metodę)
2 - Klasa Window
3 - Metoda WindowProcedure (obsługa komunikatu WM_TIMER od Timera WinAPI)
A jak nie przez uchwyt to jak chcesz się odnieść do okna?
Przez CDialog->GetSafeHwnd() albo CDialog->m_hWnd? (toż to to samo ;-)) możesz, jest ładnie opakowane w MFC, ale dzięki temu, że masz API niższego poziomu, to możesz sobie napisać własne API opakowujące je, w przypadku kiedy masz bardzo specyficzne/niestandardowe wymagania.
Jeśli nie masz na jednym ze stopni abstrakcji czegoś takiego jak uchwyty po których odwołujesz się do okien, to jak chcesz zaimplementować coś, co modyfikowałoby inne okna? (np. wszystkie programy do pracy z wieloma monitorami, które dodają nowe przyciski do paska tytułu).
Przechwytujesz tylko jeden komunikat, ale możesz przechwycić też inne jak zajdzie potrzeba.
Jeśli nie odpowiada Tobie ten poziom abstrakcji, możesz przecież śmiało użyć wyższego API, albo sam przekazać wartość komunikatu do swojej HighLevelHandler::HandleMessage(KOMUNIKAT);
Przykład 3:
Powiedzmy, że mam klasę Timer. Teraz chcę utworzyć 10000 obiektów klasy Timer. I co? Windows mówi, że tyle okien dla jednego programu mi nie da. Porobione.
Nie porobione. Te informacje są przechowywane w innej przestrzeni pamięci - w przestrzeni kernela. Jeśli nie miałbyś takich ograniczeń, albo miałbyś bezpośredni dostęp do tamtej pamięci lub nie byłoby tylu ograniczeń, to co chwilę byś miał bluescreeny. Wtedy dopiero ludzie by jechali po Windows-ie i jego stabilności :)
To właśnie mi się nie podoba w WinAPI.
Nie musisz bezpośrednio używać WinApi, dobrze tylko, żebyś wiedział, że jak kiedyś będziesz chciał zrealizować jakiś egzotyczny fetysz programistyczny, wykraczający po za to co przewidzieli projektanci API wyższego levelu, to masz świadomość, że możesz zejść niżej i to zrobić.
Kolejną radę jaką bym Ci dał, to jest to, żeby zanim coś nie skrytykować w taki sposób, to najpierw zastanowić się nad mechanizmem który tym rządzi, jak to jest zrobione, jak to by było można zrobić lepiej (mając na myśli wszystko - od A do Z).
Takie myślenie, że jedynym słusznym paradygmatem programowania to programowanie obiektowe, wszystko powinno być ładnie opakowane a najlepiej wyklikiwalne świadczy tylko o tym, że myślisz bardzo powierzchownie o tym - a to jest trochę sprzeczne, bo krytykujesz coś o poziomie abstrakcji WinApi argumentami które byłyby słuszne dla Silverlight ;-)
Karim A. edytował(a) ten post dnia 22.01.11 o godzinie 07:07