C++ w Objectivity Marcin Michalak s1744
Pomocne pakiety: Data Definition Language (DDL). Standard Template Library (STL). Active Schema.
Data Definition Language (DDL) W pierwszym kroku rozwijania obiektowo-orientowanej aplikacji definiujemy klasy, które mają zamodelować budowę i zachowania aplikacji. Takie definicje klas powstają z logicznego modelu problemu. W logicznym modelu określamy klasy których instancje będą trwałe i zachodzące pomiędzy nimi relacje. W Objectivity opisywane jest to poprzez schematy (schema). Język definiowania danych (Data Definition Language (DDL)) jest narzędziem które ma to ułatwić. Za jego pomocą możemy: - Określać klasy z trwałością (persistence-capable classes). - Definiować powiązania (asocjacje). - Modyfikować już istniejące klasy. -Dodawać nowe klasy. Zaletą DDL jest współdziałanie z c++. Definicje klas zapisujemy w jednym lub wielu plikach z rozszerzeniem ddl. Schemat tworzymy przetwarzając te pliki za pomocą procesora DDL.
Procesor DDL (1) Procesor DDL dodaje informacje do bazy danych o naszym schemacie. Dodatkowo dla każdego przetwarzanego pliku file.ddl generuje pliki źródłowe z kodem w c++: - Główny plik nagłówkowy: file.h – zawiera deklaracje i definicje z oryginalnego ddl niezbędne dla istnienia opisywanego obiektu. - Plik nagłówkowy referencji: file_ref.h – zawiera dodatkowe definicje i klasy niezbędne do obsługi referencji (asocjacji). - Plik z implementacją metod: file_ddl.cpp dla Windowsa file_ddl.c dla UNIXa Zawiera: - Definicje nie włączanych bezpośrednio non-inline funkcji deklarowanych w plikach nagłówkowych. - Kod pozwalający łączyć informacje przetrzymywane jako schemat z obiektami w bazie danych. - Kod odpowiedzialny za rejestrowanie trwałych obiektów.
Procesor DDL (2) Pliki generowane przez DDL:Pliki użytkownika:
Przykładowe możliwości DDL (1) Można wykorzystywać klasy nie obsługujące trwałości w plikach ddl. Klasa z trwałością. Klasa bez trwałości.
Przykładowe możliwości DDL (2) Możliwe jest dziedziczenie z klas obsługujących trwałość. Pliki użytkownika.Pliki generowane przez DDL.
Przykład aplikacji Biblioteka (1) Na podstawie poniższego diagramu została utworzona prosta aplikacja przechowująca obiekty wraz z powiązaniami:
Przykład aplikacji Biblioteka (2) Tworzenie schematu poprzez przenoszenie logicznego modelu do plików ddl: class Book : public ooObj { private: ooVString author; ooVString title; ooVString subject; char isbn[12]; // isbn is a fixed size ~Book() {}; // books are never deleted public: Book( const char* title, const char* subject, const char* author, const char* isbn ); ooHandle(Loan) inLoan hasBook; ooHandle(Library) fromLibrary allBooks[]; // access fns for Book fields const char* get_author() const; const char* get_title() const; const char* get_subject() const; const char* get_isbn() const; // print fns for Book ostream& print( ostream& = cout ) const; ostream& printInfo( ostream& = cout ) const; ostream& printCheckout( ostream& = cout ) const; }; // access fns for Book fields inline const char* Book::get_author() const { return author; } inline const char* Book::get_title() const { return title; } inline const char* Book::get_subject() const { return subject; } inline const char* Book::get_isbn() const { return isbn; } // ostream operator for Book inline ostream& operator << ( ostream& os, ooHandle(Book)& book) { return book->print( os ); }
Przykład aplikacji Biblioteka (3) lib.ddl lib.cpp lib.h lib_ref.h lib_ddl.cpp Book.ddl Book.cpp book.h book_ref.h book_ddl.cpp Loan.ddl Loan.cpp loan.h loan_ref.h loan_ddl.cpp lib.ddl lib.cpp Book.ddl Book.cpp Loan.ddl Loan.cpp ptrn.ddl ptrn.cpp lm.h lmMain.cpp makefile ptrn.ddl ptrn.cpp ptrn.h ptrn_ref.h ptrn_ddl.cpp lmMain.cpp lm.h makefile Pliki powstałe po przetworzeniu plików ddl: Przed:Po: Przed przetwarzaniem: -Pliki ddl składają się na opis schematu. -Pliki cpp zawierają implementacje metod. -Pliki lm.h i lmMain.cpp składają się na aplikacje. -Plik makefile zawiera reguły kompilacji.
Przykład aplikacji Biblioteka (4) ooHandle(Library) library; ooHandle(Book) book; ooHandle(Patron) patron; // tworzenie nowej biblioteki cout << "Starting Library Information System. New Library:\n"; library = new Library("Campus", "1 Campus Way", "x123"); cout printInfo(cout) << endl; // dodawanie ksiazki do biblioteki book = new Book("Moby Dick", "Fiction", "Melville", " "); cout << "Adding Book record to library:\n" << book; library->add_allBooks(book); // add to allBooks relation book = new Book("The Galloping Gourmet", "Cooking", "Kerr", " "); cout << "Adding Book record to library:\n" << book; library->add_allBooks(book); // add to allBooks relation Przykładowe operacje na obiektach: // dodawanie osoby do biblioteki patron = new Patron("Joe Cool", "Main Dorm #32", "x340"); cout << "Adding Patron record to library:\n" << patron; library->add_hasPatrons(patron); // add to hasPatrons relation patron = new Patron("Jane Doe", "Campus Apartments #232", " "); cout << "Adding Patron record to library:\n" << patron; library->add_hasPatrons(patron); // add to hasPatrons relation // wyszukiwanie książki po ISBN book = library->findBookByISBN(" "); if (book != 0) { // wypożyczenie library->checkoutBook(book, patron, "10/30/93"); cout << "Book checked out:\n" << book; }else cout << "Book: ISBN# not found\n\n"; // wydruk stanu biblioteki cout << "Exiting Library Information System:\n" << library;
Przykład aplikacji Biblioteka (5) Starting Library Information System. New Library: Name: Campus Address: 1 Campus Way Phone: x123 Adding Book record to library: [Book] Title: Moby Dick Subject: Fiction Author: Melville ISBN: Not checked out. Adding Book record to library: [Book] Title: The Galloping Gourmet Subject: Cooking Author: Kerr ISBN: Not checked out. Adding Patron record to library: [Patron] Name: Joe Cool Address: Main Dorm #32 Phone: x340 No Loans Adding Patron record to library: [Patron] Name: Jane Doe Address: Campus Apartments #232 Phone: No Loans Book checked out: [Book] Title: The Galloping Gourmet Subject: Cooking Author: Kerr ISBN: Due Date: 10/30/93 Exiting Library Information System: [Library] Name: Campus Address: 1 Campus Way Phone: x123 [Patrons] Name: Joe Cool Address: Main Dorm #32 Phone: x340 Name: Jane Doe Address: Campus Apartments #232 Phone: [Holdings] Title: Moby Dick Subject: Fiction Author: Melville ISBN: Title: The Galloping Gourmet Subject: Cooking Author: Kerr ISBN: [Loans] Name: Jane Doe Address: Campus Apartments #232 Phone: Title: The Galloping Gourmet Subject: Cooking Author: Kerr ISBN: Due Date: 10/30/93 Wynik wygenerowany przez aplikację:
Standard Template Library (STL) (1) Objectivity/C++ Standard Template Library jest rozszerzeniem elementu ObjectSpace Standards, wchodzącego w skład pakietu ANSI/ISO C++ Standard Template Library, o trwałość. Umożliwia to przechowywanie pojemników (containers) i stringów STL w Objectivity/DB. Plik lista.ddl #include template class d_list_node ; class SpliceList : public ooObj { public: // Konstruuje _list aby przechowywala wszystkie elementy // z zakresu [first, last) SpliceList(const int* first, const int* last) : _list(first, last) {} // atrybut d_list > _list; … }; Przykład wykorzystania:
Standard Template Library (STL) (1) cd. #include #include lista.h" // Wygenerowane z lista.ddl { typedef d_list > int_list; const int myArray1[] = { 2, 8 }; const int myArray2[] = { 4, 9 }; ooHandle(ooContObj) contH; ooHandle(SpliceList) sourceList, targetList; … // Otwarcie bazy danych i stworzenie nowego kontenera // contH jest uchwytem do kontenera targetList = new ( contH ) SpliceList(myArray1, myArray1+2); sourceList = new ( contH ) SpliceList(myArray2, myArray2+2); … // Sprawdzenie czy obiekty zostały utworzone Kod źródłowy do przykładu użycia STL:
Standard Template Library (STL) (1) cd. // Wyjęcie elementów z sourceList z zakresu // [sourceList->_list.begin(), sourceList->_list.end()) // i wstawienie ich na pozycji targetList->_list,begin() targetList->_list.splice( targetList->_list.begin(), sourceList->_list, sourceList->_list.begin(), sourceList->_list.end() ); // Wypisywanie zawartości targetList, za pomocą iteratora int_list::const_iterator i; for ( i = targetList->_list.begin(); i != targetList->_list.end(); ++i){ cout << *i << " "; } cout << endl; } Wynik:
Active Schema (1) Pakiet Active Schema umożliwia tworzenie narzędzi takich jak przeglądarki klas i obiektów. Aplikacje korzystające Active Schema mogą: -Uzyskać opis klas zapisanych w schemacie. -Pobrać i modyfikować trwałe obiekty używając opisów ich klas uzyskiwanych dynamicznie. -Modyfikować schema poprzez dodawanie nowych klas i zmianę opisów istniejących. -Dodawać trwałe obiekty do bazy danych (federated database), włączając instancje nowo dodanych klas. -Dokonywać konwersji trwałych obiektów, aby były zgodne ze zmienionym opisem (evolved schema) ich klas.
Active Schema (2) void showInheritance(const d_Class &aClass) { // Print parent classes, if any inheritance_iterator itr = aClass.base_class_list_begin(); if (itr != aClass.base_class_list_end()) { cout << "Parent classes of " << aClass.name(); cout << ":" << endl; while (itr != aClass.base_class_list_end()) { const d_Inheritance &curInh = *itr; const d_Class &curParent = curInh.derives_from(); cout << curParent.name(); d_Access_Kind access = curInh.access_kind(); if (access == d_PROTECTED) { cout << " (protected)"; }else if (access == d_PRIVATE) { cout << " (private)"; } cout << endl; ++itr; } // End while more parents cout << endl; } else { // End if any parents Przykład wykorzystania Active Schema do przeglądania dziedziczenia:
Active Schema (2) cd. cout << aClass.name() << " has no parent classes"; cout << endl << endl; } // End else no parents // Print child classes, if any itr = aClass.sub_class_list_begin(); if (itr != aClass.sub_class_list_end()) { cout << "Child Classes of " << aClass.name(); cout << ":" << endl; while (itr != aClass.sub_class_list_end()) { const d_Inheritance &curInh = *itr; const d_Class &curChild = curInh.inherits_to(); cout << curChild.name() << endl; ++itr; } // End while more child classes cout << endl; } else { // End if any child classes cout << aClass.name() << " has no subclasses"; cout << endl << endl; } // End else no child classes } // End showInheritance
Podsumowanie: Pisanie programów dla Objectivity w C++ jest bardzo pracochłonne. Tworzy się znaczne ilości kodu, który bardzo często się powtarza. Dlatego potrzebne są narzędzia aby ukryć przed użytkownikiem proces generacji niskopoziomowego kodu i pozwolić mu zająć się tylko kodowaniem samego modelu.