Prosty menedżer zasobów?…Silnikologia (przynajmniej ta warsztatowa) ma swoje dziwnostki. Za jedną z nich uważam przykładanie zbyt dużego znaczenia do tych podsystemów engine'u gry, które są zdecydowanie mniej ważne niż silnik graficzny czy fizyczny. Podpadają pod to (między innymi): mechanizmy logowania, VFS-y (wirtualne systemy plików), kod odpowiedzialny za wczytywanie i zapisywanie opcji konfiguracyjnych, itp. Blisko szczytu tej listy znajduje się też podsystem, o którym chcę napisać dzisiaj kilka słów. Chodzi mianowicie o menedżer zasobów (resource manager).
Pod tą nazwą kryję się obiekt, którego zadaniem jest - jak nazwa zresztą wskazuje - zarządzanie różnego rodzaju zasobami gry, gdzie pod pojęciem 'zasobu' kryje się zwykle jakiś element jej contentu: model, tekstura, próbka dźwiękowa, czcionka, shader... Menedżer ma za zadanie udostępniać odwołania do tych zasobów elementom silnika/gry, które ich potrzebują. To jest jego główny, nadrzędny cel.
Tu jednak dochodzimy do małego paradoksu. Jeśli bowiem jest to jedyne przeznaczenie modułu od zarządzania zasobami, to tak naprawdę mogłoby go w ogóle nie być!... W praktyce jego istnienie usprawiedliwia przynajmniej jeden z poniższych powodów:
Tego rodzaju wymagania rzeczywiście można rozsądnie zrealizować dopiero wówczas, gdy w logiczny sposób wydzielimy z kodu część, która za kontrolę zasobów będzie odpowiadać. Jeśli jednak żadna z powyższych sytuacji nie aplikuje się do nas, nie ma żadnego powodu, by zabierać się za tworzenie modułu zarządzania zasobami tylko dlatego, że "przecież zasoby gdzieś muszą być". Coś takiego jak 'prosty menedżer zasobów' to oksymoron: jeśli faktycznie może być prosty, to najpewniej znaczy, iż jest zbędny. No, chyba że pod tym pojęciem rozumiemy też coś, co w praktyce da się zredukować do:
W takim przypadku powinniśmy zadbać przede wszystkich o to, by nasz Menedżer Zasobów (caps intended) dobrze współpracował z Modułem Wyświetlania Życia Gracza, że o Silniku Od PauzyTM nie wspomnę ;P
Standardy kodowania jako choroba dziedzicznaPonieważ programowaniem zajmuję się już od dość długiego czasu, mam w tym nieco doświadczenia praktycznego. Przez ten czas zdążyłem też zmienić część swoich poglądów na niektóre rzeczy z tym związane. Mówiąc trochę górnolotnie: stałem się bogatszy o wiedzę z życia wziętą :)
Jedną z takich nauczek życiowych jest to, by nie ufać zbytnio standardom, zaleceniom i innym tzw. dobrym praktykom podpowiadających (a czasem wręcz nakazujących), jak powinien wyglądać pisany przez nas kod. Dotyczy to na przykład instrukcji rzekomo złych "z natury" i innych tego rodzaju przesądów. Tak, uważam określenie 'przesądy' za uprawnione w wielu przypadkach.
Nie jestem ponadto odosobniony w tych poglądach - ba, wśród swoich kolegów po fachu mógłbym wskazać wielu kilku, którzy są pod tym względem nawet bardziej... radykalni :) Jak już jednak wspomniałem na początku, nie zawsze tak było. Kiedyś miałem bardziej rygorystyczne i pryncypialne podejście do kwestii tego, jak kodować należy lub nie należy. Skąd brałem dla niego uzasadnienie, jeśli nie z praktyki, której wtedy zbyt wiele jeszcze nie miałem?...
Ano właśnie - tu dochodzimy do sedna. Przekonanie to brało się głównie - jeśli nie wyłącznie - ze wszelkiego typu materiałów pomocniczych do nauki programowania: książek, kursów, artykułów, czasem dokumentacji. Choćby nie wiem jak techniczne i merytoryczne były te teksty, w każdym - co do jednego, z moim skromnym dziełem włącznie - znajdą się rady, zalecenia, wskazówki i inne "metainformacje" odnośnie tego, jak programować należy - a nie tylko o tym, jak można. Czy ktoś widział w kursie programowania opis instrukcji goto (że pozwolę sobie użyć najbardziej typowego przykładu), który nie zawierał chociaż śladowej wzmianki o tym, iż.. no, w zasadzie to nie należy jej używać?... ...Tak myślałem ;-)
Nasuwającą się od razu repliką w obronie autorów jest sugestia, że w sumie to przecież całkiem dobrze, iż uczą oni nowicjuszy tego, co sami musieli wcześniej wypracować przez lata. Nie wątpię wręcz, że taka intencja - oszczędzenia początkującym trudności - rzeczywiście autorom przyświeca. Dodają oni do pisanych przez siebie tekstów, kursów, tutoriali, itp. liczne wskazówki, te wszystkie dos and don'ts właśnie dlatego, iż wierzą w to, że w ten sposób pomogą swoim czytelnikom od razu, już na starcie kodować w sposób przejrzysty, efektywny, elastyczny, elegancki, "łatwo konserwowalny", i tak dalej.
Chwalebne intencje - nie da się ukryć. Ale nietrudno jest wskazać przynajmniej dwa błędy takiego rozumowania. Pierwszym jest założenie, że każdą wiedzę i każdą umiejętność da się przekazać innym - czyli że w ogóle istnieje "droga na skróty", niejako omijająca zdobywanie doświadczenia praktycznego w danej dziedzinie (w tym przypadku w programowaniu). Żeby stwierdzić, że to jednak nieprawda, wystarczy zastanowić się, czy ukończenie podstawowego kursu szachów u arcymistrza da nam od razu ELO 2500. Niestety w rzeczywistości dojście do takiego poziomu wymaga dziesiątków tysięcy rozegranych partii - i nic się na to nie poradzi.
Drugim błędem jest przyjmowanie, że przekazywane mądrości są faktycznie odpowiednie dla odbiorców. Nie, nie chcę wcale sugerować, że ktoś może mieć traumę z powodu wczesnych doświadczeń z hermetyzacją czy wzorcem fabryki ;) Raczej sugeruję, że przywiązując zbyt wielką wagę do przekazywanych zaleceń, początkujący mogą stracić wiele czasu na dopasowywanie swoich pierwszych programów do ich wysoko zawieszonej poprzeczki - najczęściej zupełnie niepotrzebnie. Gra w zgadywanie liczby, kółko i krzyżyk czy tetris nie potrzebują zwykle skomplikowanej, wewnętrznej architektury z dokładnie zaprojektowanymi relacjami między klasami i starannym oddzieleniem interfejsu od implementacji. Prosi się to bowiem o przytoczenie znanego powiedzenia na temat artylerii i pewnego gatunku insekta :)
Tak oto okazuje się więc, że zalecenia i rady tudzież - szumnie nazywane - standardy kodowania są "dziedziczone" przez początkujących programistów od autorów książek, kursów i tutoriali, którzy je tam umieszczają pomiędzy objaśnieniami kolejnych konstrukcji językowych czy elementów API. A że ponadto, jak opisałem wyżej, standardy te zbyt wcześnie poznane mogą być nie tyle nawet zbędne, co wręcz szkodliwe, pozwalam sobie nazwać je 'chorobami dziedzicznymi'. Nic tak bowiem nie trafia do wyobraźni jak obrazowe porównanie ;>
Zakończyć chcę jednak optymistycznym akcentem. Otóż choroby te są jak najbardziej uleczalne. Terapia jest też nieskomplikowana i - jak sądzę - skuteczna. Trzeba po prostu kodować, kodować i jeszcze raz kodować :)
ma kau se jinvi mi la lojban....czyli co sądzę o lojbanie.
Powracam do tematu z notki sprzed kilku tygodni, mimo swoich przeczuć, że nie jest to wcale coś, na co wszyscy z napięciem oczekiwali ;-) Spróbuję jednak przekazać porcję swoich nieco głębszych przemyśleń na temat lojbanu po tym, jak poświęciłem trochę więcej czasu na przyjrzenie się temu językowi. Witam więc tych nielicznych, którzy chcą się z nimi zapoznać :]
Dla osoby zupełnie niewtajemniczonej najbardziej rzucającą się w oczy cechą tego języka jest to, że zdecydowana większość słów jest zupełnie niepodobna do swoich odpowiedników w innych językach - mimo że zostały one skonstruowane właśnie z nich. Chociaż na początku może to się wydawać minusem, w ostatecznym rozrachunku ma chyba więcej zalet niż wad. To dlatego, że - jak pisałem już wcześniej - w lojbanie nie ma tradycyjnych części mowy ani konstrukcji gramatycznych. Nie ma więc uzasadnienia dla tego, żeby słownictwo było w nim podobne do innych języków; to mogłoby wręcz być mylące. Zgadza się to też z założeniem kulturowej bezstronności ("znajome" słowa byłyby faworyzowaniem języków europejskich, jak to jest chociażby w esperanto).
Drugim widocznym atrybutem jest wszechobecność krótkich, zwykle dwu- lub trzyliterowych słów, często z apostrofem w środku (czytanym jako 'h'). To cmavo (wym. szmawo), słowa strukturalne. W nich zawarta jest zdecydowana większość gramatyki lojbanu. Pełnią one mnóstwo różnorodnych funkcji, będąc odpowiednikami zarówno przyimków, spójników czy zaimków, jak i rodzajników, znaków przestankowych, a nawet podkreśleń czy cudzysłowów (w lojbanie wszystko to ma postać słów). Istnieje więc całe multum różnych cmavo, z których wiele jest logicznie zorganizowanych w grupy mające części wspólne, jak np. ze'i/ze'a/ze'u - krótki/średni/długi odcinek czasu. Nie dotyczy to jednak wszystkich, zatem nierzadko pomyłka o jedną literę może skutkować zupełnie innym znaczeniem wypowiedzi lub jej niepoprawnością.
Jednak cmavo są na szczęście w dużej mierze nieobowiązkowe, bo w wielu przypadkach służą uszczegółowieniu tego, co wynika z kontekstu. Jedną z takich kontekstowych informacji jest często czas (teraźniejszy, przeszły, itp.), który w lojbanie - w przeciwieństwie do wielu innych języków - nieczęsto trzeba podawać wprost, bo reguły gramatyki tego nie wymuszają. Jeśli jednak ktoś sobie życzy sobie, aby go doprecyzować, to ma do dyspozycji bardzo ciekawe rozwiązanie, rozdzielające problem na dwie części. Pierwsza odpowiada relacji czasowej między aktualnym punktem a zdarzeniem - czyli temu, czy mówimy o przeszłości (pu), teraźniejszości (ca) czy przyszłości (ba). Druga (zwana niekiedy aspektem) określa, o którym punkcie osi czasu wokół zdarzenia mówimy, np.: przed jego początkiem (pu'o), dokładnie na początku (co'a), w środku jego trwania (ca'o), dokładnie na końcu (mo'u) lub po jego zakończeniu (ba'o). W sumie daje to kilkanaście kombinacji, z których większość da się odnieść na przykład do czasów w języku angielskim (tenses), ale które jednocześnie są o wiele łatwiejsze do zrozumienia.
Trzecia istotną właściwością lojbanu jest fakt, że znacznie częściej niż w innych językach mówimy w nim o "nie-rzeczach", takich jak zdarzenia, informacje, wypowiedzi, właściwości, ilości, koncepcje, procesy, stany, itp. Nazywa się je tu ogólnie abstrakcjami i regularnie występują one jako argumenty do predykatów (czyli selbri). Istnieją oczywiście ich odpowiedniki w innych językach, lecz zwykle przyjmują one postać zdań podrzędnych ('to, że...'), jako że rzeczownikami nazywa się w większości rzeczy lub osoby. W lojbanie tak nie jest (no bo przecież rzeczowników nie ma :)) i w sumie wychodzi mu to na dobre. Można rzecz jasna wskazać dziwne "skutki uboczne" - nie można na przykład chcieć (djica) rzeczy jako takich, a jedynie zdarzeń z nimi związanych - ale ogólnie obecność abstrakcji korzystnie wpływa na specyfikowanie tego, o czym tak naprawdę mówimy.
Aby nie śmiecićJako moderator forum Warsztatu mam wątpliwą przyjemność kontaktu z różnymi przejawami - nazwijmy to eufemistycznie - niepożądanych zachowań. A to mamy jakieś pytanie, na które odpowiedzią jest pierwszy rezultat, jaki wyszukiwarka wyrzuca w reakcji na prostą kwerendę. Innym razem może to być klasyczne "No i co tu jest źle?!", opatrzone wysokim na kilka ekranów kawałkiem kodu, dla którego właściwym miejscem jest /dev/null. Kiedy indziej będzie to pojawiający się po raz 2512. problem w rodzaju unresolved external... I tak dalej, i tak dalej. Ot, zwyczajne przypadki, gdy napisanie posta poprzedza (lub zastępuje) myślenie.

Może nie jestem w stanie tego zrozumieć, ale przynajmniej potrafię się z tym pogodzić - po części pewnie dlatego, że sprzątanie po tego rodzaju twórczości nie jest specjalnie kłopotliwe. Jest jednak przynajmniej jedna rzecz, która skłania mnie do poważnych rozważań na temat intelektualnej kondycji rodzaju ludzkiego - a przynajmniej tej jego części, która się na forum pojawia - połączonych z co najmniej jednokrotnym wykonaniem znanego i lubianego gestu kapitana Picarda.
O co chodzi?... O dołączanie nowych pytań do istniejących wątków, z uzasadnieniem, że czyni się tak po to, aby nie zaśmiecać forum nowymi tematami. Czy jest to dobra praktyka? Czy ma to jakikolwiek sens? I wreszcie, czy skutkiem jej stosowania jest faktycznie większy porządek forum?
Otóż nie - i to po trzykroć nie. Paradoks tu występujący polega właśnie na tym, że - dokładnie przeciwnie do intencji osób tak postępujących - podczepianie się pod istniejące tematy skutkuje tylko i wyłącznie jeszcze większym bałaganem! Dzieje się tak co najmniej z kilku powodów:
Widać zatem wyraźnie, że skutek doczepiania się do istniejących wątków jest dokładnie odwrotny do zamierzonego. Nie ma to najmniejszego sensu i jest wybitnie niepożądane. Wciąż jednak nie mogę się nadziwić, jakaż to dziwna logika podsuwa ludziom pomysły, że może być inaczej...
O obiektowościKiedy programiści się nudzą, to często spierają się o terminologię. Przedmiotem takich sporów są zwykle nieco mgliste określenia, które czasami - zwykle niesłusznie - używane są jako atrybuty oceniające. Przykład? Choćby "obiektowość" - w zależności od punktu widzenia stwierdzenie, że dany kod/biblioteka/moduł/itp. są bardziej lub mniej obiektowe może stanowić albo zaletę, albo wadę. Zależy to głównie od 'poglądów' danej osoby na temat użyteczności podejścia OO w programowaniu.
Co to jednak znaczy, że dany kod jest obiektowy?... Nie uważam wcale, że należy go w tym celu napisać go w języku obiektowym. Twierdzę na przykład, że Windows API jest całkiem dobrym przykładem biblioteki realizującej obiektowy paradygmat, mimo tego że została ona stworzona w jak najbardziej strukturalnym C. Praktyczna różnica między poniższymi wywołaniami:
jest bowiem właściwie żadna. Dodajmy do tego fakt, że z punktu widzenia programisty-użytkownika w WinAPI występuje zarówno dziedziczenie (rodzajów uchwytów), jak i polimorfizm (funkcje niezależne od typu uchwytów, np. CloseHandle), a więc bardzo obiektowe feature'y.
Jeśli komuś wydaje się to naciągane i twierdzi, że w ten sposób pod obiektowość można podciągnąć właściwie każdy kod, to już spieszę z przykładem ukazującym, że tak nie jest. Otóż większa część biblioteki OpenGL obiektowa na pewno nie jest, zaś w tych nielicznych miejscach gdzie OOP jest niemal konieczny (jak zarządzanie teksturami czy buforami wierzchołków) pojawia się nieco dziwna koncepcja 'indeksów' używanych do identyfikowania poszczególnych obiektów.
Dla niektórych (łącznie ze mną) taki interfejs nie jest szczytem marzeń, a preferowane jest wyraźnie obiektowe podejście DirectX. Absolutnie jednak nie zgadzam się z twierdzeniem, że DirectX jest lepszy, bo bardziej obiektowy - to trochę tak jak powiedzenie, że ten obrazek jest ładniejszy, bo bardziej zielony. W obu przypadkach jest to kwestia gustu i nie powinniśmy zakładać, że cechy pozytywne dla nas są tak samo dobrze odbierane przez innych.
A wyższość DirectX nad OpenGL da się przecież uargumentować na wiele innych, lepszych sposobów :)
10 lat w sieciTegoroczny koniec grudnia to czas podsumowań tego, co działo się nie tylko w ciągu ostatnich 12 miesięcy, ale i całej ostatniej dekady. A przypadkiem właśnie dekadę temu zyskałem dostęp do globalnej sieci zwanej Internetem... Jest to więc ciekawa okazja na podzielenie się paroma moimi obserwacjami tego, jak na przestrzeni ostatnich 10 lat Internet się zmieniał.
Rok 1999 to nie były jakieś zamierzchłe czasy sieci w Polsce (nie wspominając już o sieci w ogóle), lecz mimo to podejrzewam, że niezbyt duża część obecnych jej użytkowników dobrze je pamięta. Cechą wyróżniającą Internetu w tamtym okresie była przede wszystkim oszczędność. Szerokopasmowe łącza nikomu się wtedy nie śniło, a część internautów musiała płacić za sam czas spędzony w sieci (sławetny numer 0202122). Dlatego też podstawową zasadą netykiety było możliwie najskuteczniejsze zmniejszanie rozmiarów przesyłanych danych. Objawiało się to dość spartańskim wyglądem ówczesnych stron, z rzadka składającym się z większej ilości obrazków, a często wykorzystującym takie niefortunne wynalazki HTML jak choćby ramki.
Jak nietrudno zauważyć dzisiaj jest zupełnie inaczej. Główna strona typowego portalu może spokojnie ważyć kilka megabajtów i być okraszona kilkoma reklamowymi animacjami typu Flash, często zresztą niczym nie różniących się od spotów telewizyjnych. Wymaga ona więc znacznie większej przepustowości łącza, a także o wiele lepszej i efektywniejszej przeglądarki.
To też zresztą znak rozpoznawczy Internetu końca dekady: jest on niemal tożsamy z WWW, a inne protokoły komunikacji są w głębokim odwrocie. Kilka lat wcześniej było pod tym względem inaczej. Przeglądanie stron było tylko jedną z wielu sieciowych aktywności, do których należało też korzystanie z e-maila, ściąganie plików przez FTP, czytanie grup dyskusyjnych (Usenet) czy pogaduszki za pośrednictwem IRC. Dzisiaj prawie każdy z tych kanałów komunikacji został pochłonięty lub zastąpiony przez tzw. aplikacje webowe, czyli po prostu trochę bardziej interaktywne wersje stron WWW.
I wreszcie trzeci aspekt zmian, jakie w sieci się dokonały: jej umasowienie. Nie chodzi tu tylko o sam wzrost liczby osób korzystających z sieci, który w ciągu ostatnich 10 lat był prawie czterokrotny. Chodzi o pewne zmiany w samej "strukturze" sieci, które są przynajmniej częściowo jego wynikiem.
Kiedyś w Przekroju czytałem artykuł reklamowany na okładce wiele mówiącą frazą: "Jak idioci zepsuli nam Internet". Dotyczy ona oczywiście całego sieciowego trendu znanego pod ogólnym określeniem Web 2.0, nad którym zresztą pastwiłem się już przy innej okazji. W skrócie chodzi mniej więcej o to, że połączenie masowości dostępu do Internetu (w krajach rozwiniętych to już przynajmniej połowa populacji) z łatwością umieszczania w nim nowych treści (coraz mniejsza konieczność znajomości takich "technikaliów" jak choćby HTML) powoduje zalanie sieci contentem bez żadnej wartości, czyli jej zwyczajne zaśmiecenie.
W sumie jednak jest to nic nowego; już przed 10 laty mówiło się, że zawartością Internetu są w większości bzdury. Różnica między stanem z tego czasu a dniem dzisiejszym jest jednak taka, że mimo bezwzględnego (a pewnie i względnego) wzrostu ilości treściowych odpadów znalezienie poszukiwanych informacji jest paradoksalnie łatwiejsze niż kiedykolwiek wcześniej. Zawdzięczamy to jednak tylko i wyłącznie gwałtownemu rozwojowi internetowych wyszukiwarek.
Ogólny bilans tej dekady w sieci nie jest więc jednoznaczny. Z jednej strony stoją rzecz jasna MySpace, Facebooka, 4chana czy inne sieciowe szkodniki, ale z drugiej jest przecież Google, Wikipedia i całe mnóstwo wartościowych serwisów tematycznych, z których część nie powstałaby pewnie w warunkach "elitarnego" Internetu sprzed dekady.
Jesteśmy więc w innym miejscu - lecz niekoniecznie gorszym. Trzeba się do niego po prostu przyzwyczaić :)
Dlaczego nie lubię typów referencyjnychJeśli chodzi o C++, to nietrudno zauważyć, że często pozwalam sobie na sporo uwag krytycznych pod adresem tego języka. Oczywiście zawsze jest to krytyka konstruktywna :) Tym niemniej wiele jest tu rzeczy, o których można powiedzieć, że w innych językach zostały pomyślane lepiej (łącznie z takimi, których w C++ nie ma, a przydałyby się).
Dlatego dzisiaj będzie trochę nietypowo. Chcę bowiem wspomnieć o problemie, który w językach pokroju C# czy Javy potrafi doprowadzić do powstawania trudnych do wykrycia błędów - i który jednocześnie w C++ w zasadzie nie występuje wcale.
Mam tu na myśli semantykę referencji, czyli pewien szczególny sposób odwoływania się do szeroko rozumianych obiektów w kodzie. Klasy, a właściwie to prawie wszystkie typy poza podstawowymi (jak liczby czy znaki), są w C# i Javie obsługiwane w ten właśnie sposób; dlatego czasami nazywa się je typami referencyjnymi.
Najważniejszą cechą takich typów jest fakt, że należące do nich zmienne nie zawierają bezpośrednio instancji obiektów. Jeśli na przykład Foo jest klasą, to deklaracja:
nie sprawi, że pod nazwą x będzie siedział obiekt typu Foo. x będzie tutaj zaledwie odwołaniem do takiego obiektu - w tym przypadku zresztą odwołaniem pustym, niepokazującym na nic.
Jest to zachowanie diametralnie różne od typów podstawowych, jak choćby int. Ale idźmy dalej - skoro mamy zmienną mogącą trzymać odwołanie (czyli referencję) do obiektu, to pokażmy nią na jakiś obiekt, na przykład taki zupełnie nowy:
A że w prawdziwym programie zmiennych i obiektów jest zawsze mnóstwo, to wprowadźmy na scenę jeszcze parę:
No i zonk, można powiedzieć... Nikt aczkolwiek tego nie powie, bo dla każdego programisty C#, Javy itp. istnienie wielu referencji do tego samego obiektu jest rzeczą całkowicie naturalną. Jednak wiem, że podobny kod dla dowolnego typu liczbowego (zastąpiwszy ostatnią linijkę przez y += 4; lub coś tym w guście) zachowałby się zupełnie inaczej. Wiem też, że kiedyś byłem zmuszony wykonać kilka empirycznych testów, by się o tym naocznie przekonać; było to jeszcze w Delphi, a powodem były oczywiście jakieś "dziwne" błędy, na które natrafiłem w jednym ze swoich programów. Źle użyte typy referencyjne łatwo mogą być bowiem przyczyną takich błędów, które zresztą bywają potem trudne do wykrycia.
Bez jakiegoś rodzaju referencji nie da się rzecz jasna wyobrazić sobie użytecznego języka programowania. Sęk w tym, że w C# czy Javie używanie ich nie jest opcją do stosowania w tych przypadkach, które tego wymagają - jest koniecznością wymuszoną przez sam fakt programowania z użyciem klas i obiektów. To całkiem inaczej niż w C++, gdzie w tym celu trzeba wyraźnie zaznaczyć swoje intencje (najczęściej poprzez użycie typów wskaźnikowych).
W tworzeniu oprogramowania istnieje tzw. zasada najmniejszego zdziwienia (principle of least astonishment). Mówi ona, że przy alternatywie równoważnych przypadków powinno się wybrać ten, który u użytkownika końcowego będzie powodował mniejsze zdziwienie. Czy typy referencyjne zachowujące się zupełnie inaczej niż typy podstawowe i "same" zmieniające swoją zawartość nie są przypadkiem złamaniem tej reguły?...