Kończąc powoli prace nad systemem GUI (a przynajmniej jakimiś sensownymi podstawami tego systemu), zakańczam jednocześnie prace nad “płaską” częścią silnika. Innymi słowy, już wkrótce nie będzie żadnej wymówki i trzeba będzie zabrać się za dodanie upragnionego, a zarazem niezwykle komplikującego życie trzeciego wymiaru :)
Pomyślałem jednak, że najpierw dobrze byłoby poskładać napisane już w cegiełki w sensowną całość i stworzyć coś w rodzaju frameworka. Chodzi tutaj o tę warstwę pośrednią między kodem silnika a użytkownikiem i systemem, czyli szkielet umożliwiający wygodne tworzenie rzeczywistych aplikacji.
W wielu bibliotekach różnie to rozwiązano. Z jednego strony DirectX czy OpenGL zostawiają to całkowicie w gestii programisty. Musi on samodzielnie przygotować chociażby to okienko, w którym będzie się odbywało rysowanie. Z kolei np. SDL bardzo głęboko ingeruje w kod programu, narzucając nawet określoną formę funkcji main
.
Najlepsze jest oczywiście takie rozwiązanie, które zapewnia zarówno dużą elastyczność, jak i nie zmusza do napisania kilkuset linijek w celu zobaczenia czegokolwiek. Wydaje mi się, że bliski ideałowi jest pomysł zamknięcia funkcjonalności frameworka w klasę w rodzaju Application
, zawierająca metody pozwalające na inicjalizację programu i późniejsze sterowanie nim. Metody tej klasy byłyby wywoływane w funkcji startowej programu, czyli main
lub WinMain
. Tak to wygląda na przykład w Windows Forms czy w VCL (Delphi):
[delphi]program SomeProject;
uses
Forms,
MainFrm in ‘MainFrm.pas’ {Form1};
{$R *.res}
begin
Application.Initialize;
Application.CreateForm(TMainForm, MainForm);
Application.Run;
end.[/delphi]
Według mnie najlepiej jest, gdy obiekt głównej klasy programu jest albo statycznie tworzonym singletonem, albo zostaje wykreowany przez programistę przy rozruchu aplikacji. Najważniejsze, aby nie zmuszać do dziedziczenia po klasie Application
– po to na przykład, by nadpisując metody wirtualne zapewnić możliwość reakcji na zdarzenia (jak wciśnięcia klawiszy czy ruch myszy). Dzięki delegatom, choćby tym z biblioteki FastDelegate, można to zrobić dokładnie tak, jak w “bardziej cywilizowanych” językach programowania.
Ponieważ aplikacja jest jedna, do funkcji zwrotnych nie trzeba używać delegatów. Wystarczą zwykłe wskaźniki do funkcji globalnych.
Ale ktoś (np. ja :)) może być przyzwyczajony, że w programie tworzy sobie jakąś własną klasę aplikacji w rodzaju CApp, która naturalnie jest singletonem, i zamyka całość funkcjonalności danego programu (i tylko korzysta z biblioteki/frameworka).
Po prostu delegaci zwłaszcza tak dobre w FastDelegate (przyjmujące oba rodzaje funkcji) dają większą elastyczność.