Programowanie obiektowe w C
Różnie można określić to, czym jest programowanie obiektowe (a raczej zorientowane obiektowo), jednym z podejść jest określenie możliwych typów bytów w naszej aplikacji, utworzenie ich obiektów i zmuszenie ich do wykonania zadania poprzez wysyłanie doń komunikatów (tzn. wywołanie ich procedur).
Programowanie obiektowe, jak i imperatywne są metodami do osiągnięcia celu i w zależności od problemu czasem może być lesze jedno a czasem drugie. Dodatkowo paradygmatów prócz dwójki wymienionych jest jeszcze sporo więcej, np. SQL i Prolog jest przykładem programowania deklaratywnego. BTW użyłem w tym kontekście zamiennie słów programowanie i paradygmat, miałem racje? Zainteresowanych odsyłam do wyszukiwarki pod hasłem programming paradigm.
Wydawać by się mogło (przynajmniej ja bardzo długo tak sądziłem) iż obiektowo pisać można tylko w języku obiektowym, jednak to nie prawda - przykładem jest chociażby Perl, kiedyś w necie natknąłem się na tutorial o pr. obiektowym w asmie ale nie pamiętam linka.
Przykładem obiektowo napisanego projektu w C jest redland, jako, że jest to projekt open source, możemy ściągnąć jego kod ku bliższej analizie (a ja mogę tu pokazać przykład example3.c z katalogu examples):
1 librdf_world* world; 2 librdf_storage *storage; 3 librdf_model* model; 4 librdf_statement* statement; 5 6 world=librdf_new_world(); 7 librdf_world_open(world); 8 9 model=librdf_new_model(world, storage=librdf_new_storage(world, "hashes", "test", "hash-type='bdb',dir='.'"), NULL); 10 11 librdf_model_add_statement(model, 12 statement=librdf_new_statement_from_nodes(world, librdf_new_node_from_uri_string(world, (const unsigned char*)"http://www.dajobe.org/"), 13 librdf_new_node_from_uri_string(world, (const unsigned char*)"http://purl.org/dc/elements/1.1/creator"), 14 librdf_new_node_from_literal(world, (const unsigned char*)"Dave Beckett", NULL, 0) 15 ) 16 ); 17 18 librdf_free_statement(statement); 19 20 librdf_model_print(model, stdout); 21 22 librdf_free_model(model); 23 librdf_free_storage(storage); 24 25 librdf_free_world(world); 26 27 #ifdef LIBRDF_MEMORY_DEBUG 28 librdf_memory_report(stderr); 29 #endif 30
Tworzymy obiekt (danej struktury, która posiada w sobie zaszyte pola, będące polami obiektu) i inicjujemy go (odpowiednik konstruktora). Od tego momentu możemy wykonywać operacje na tym obiekcie (funkcja przyjmująca jako pierwszy argument obiekt na który wykona swoją operację).
Oczywiście możemy mieć dużo różnych obiektów, które będą między na siebie oddziaływać i coś wykonywać wspólnie, jak w zwykłym programie obiektowym. Na końcu niszczymy obiekt (zwalniamy pamięć etc).
Żeby przyjrzeć się bliżej jak to działa napisałem sam (całkiem osobny) program/kod/jakkolwiek zwać - DAO ale zabrakło mi motywacji i jest tylko moduł do obsługi postgresa =(
Możesz ominąć ten akapit jeśli wiesz, po co jest DAO. Powyżej znajduje się schemat jak zbudowane jest takie coś, jest biblioteka,API umożliwiające wykonywanie zapytań SQL i zwracanie wyników niezależnie od zastosowanej bazy danych (przynajmniej w teorii). Plusem jest to, iż interfejs (w znaczeniu: to jakie mamy funkcje, ich parametry etc) jest wspólny dla wszystkich baz danych, nie trzeba się przekopywać przez dokumentację za każdym razem gdy korzystamy z nowej bazy.
Co do tego, jak zaimplementowana jest obiektowość w kodzie polecam książkę, która jest podana w bibliografii - nie będę jej tu przepisywał :) Dokumentację napisałem w kodzie i później wygenerowałem doxygenem do formatu HTML (nienawidzę LaTeXa) - jest dostępna online (link na końcu posta)
Całość składa się z klasy DAO, która jest interfejsem tego przybytku, pobiera ona zapytania z kodu klienckiego (tu prosty main.c). Następie wykonuje to zapytanie przesyła już do modułu konkretnej bazy danych. I tu zonk bo nie ma żadnej logiki, jest na sztywno jeden jedyny moduł bo nie chciało mi się już tego dalej rozwijać =(
Wynik zwracany jest w osobnym obiekcie (to jak jest trzymam woła o pomstę do nieba :P). W kodzie aplikacji mając obiekt w którym jest zapisany wynik możemy się do niego dobrać korzystając z iteratora.
Na koniec jeszcze plik main.c i przykład użycia:
1 #include <stdio.h> 2 #include "query-result.h" 3 #include "dao.h" 4 5 int main(){ 6 // tworze obiekt klasy dao i inicjuje go 7 void * dao = new(Dao, "psql", "testrollback","rgawron", "", ""); 8 // tworze obiekt bedacy wynikiem zapytania i inicjuje go 9 void * result = new(QueryResult); 10 11 // na obiekcie dao wykonuje zapytaie do bazy, a wynik umieszczam 12 // w obiekcie klsay wynik zapytania 13 query(dao, "select nazwisko from Osoba", result); 14 15 int isEmpty; 16 for(isEmpty = 0; isEmpty!=1; ) { 17 char answer[1000]; 18 // iteruje po krotkach ktore zawiera wynik zapytania 19 // by wysietlic wszytkie wyniki ktore zwrocilo zapytanie 20 QueryResult_iterator(result, &answer[0], &isEmpty); 21 printf("%s\n", answer); 22 } 23 24 // nie ptrzeba mi juz polaczenia z baza danych ani wynikow 25 // zaptania usuwam je wiec by nie bylo wycieku pameci i by 26 // nie obciazac bazy danych zbednym polaczeniem 27 delete(result); 28 delete(dao); 29 30 printf("La fin de mon monde..\n"); 31 } 32
rgawron@foo:~/objects/handler$ ./handler_test Kowalski Michnik La fin de mon monde..
Gdyby ktoś chciał rozwinąć w/w DAO by przyjrzeć się pisaniu obiektowo w C nie zaczynając od kodu od zera to można dodać np:
- napisanie własnego skryptu do generowania zbędnych rzeczy, by podać informacje o tym, jakie pola ma mieć klasa i jakie metody, a reszta by wygenerowała się automatycznie (i by zostało nam "tylko" napisanie ciał tych metod)
- obsługę innych baz
- cacheowanie danych, np do pliku
- wycieki pamięci :P
Bibliografia
Materiały ode mnie
- Kod źródłowy DAO, dump z bazy postgresa wykorzystanej w main.c
- Dokumentacja do w/w kodziku dostępna online i do ściągnięcia.