Wstęp

Poniższy tekst stanowi zbiór zaleceń do realizacji konstrukcji UML wymaganych do mini-projektu 3 (MP3).

Przed przystąpieniem do implementacji należy przypomnieć sobie składnię i semantykę konstrukcji wymaganych do implementacji:

Wykład PRI (katalog Public)

Wykład PRI (strona)

Wykład PRI po angielsku (katalog Public)

 1. Zgodnie z wymaganiami opisanymi w pkt 3. dokumentu wymagań każda konstrukcja powinna mieć osobny przykład biznesowy (nie należy łączyć konstrukcji wymienionych w wymaganiach mini-projektu).

 2. Każda konstrukcja powinna mieć osobny przykład biznesowy i implementację. Nie należy łączyć ze sobą konstrukcji (np. dziedziczenia overlapping i dynamic).

 3. Klasa abstakcyjna / polimorfizm.

  3.1. Należy zaproponować sensowny przypadek biznesowy. Polimorfizm metod powinien obejmować różne algorytmy dla metody biznesowej, w zależonści od podklasy. Proste rozwiązania, np. zwracanie różnej stałej w zależności od podtypu nie będą akceptowane.

 4. Dziedzieczenie typu overlapping.

  4.1 Obiekt może posiadać wiele ról jednocześnie spośród podtypów wymienionych w dziedziczeniu.

  4.2. W trakcie konstrukcji obiektu możliwe jest ustalenie dowolnej kombinacji ról określonych w dziedziczeniu. Można to zrealizować za pomocą uniwersalnego konstruktora, lub statycznej metody tworzącej obiekt.

  4.3. Przydzielenie zestawu ról jest statyczne - nie jest możliwe dodanie / usunięcie / zmiana roli po utworzeniu obiektu.

  4.4. Istnieją dwa główne sposoby implementacji dziedziczenia typu overlapping:

   4.4.1. Implementacja za pomocą spłaszczenia hiererchii klas.

    4.4.1.1. Wszystkie składowe (atrybuty, metody, asocjacje) zostaną przeniesione do klasy bazowej

    4.4.1.2. Należy umożliwić stworzenie obiektu klasy bazowej - nie może być ona abstrakcyjna.

    4.4.1.3. Należy wprowadzić dyskryminator w celu wskazania ról danego obiektu (np. zbiór wartości enum).

    4.4.1.4. Możliwe jest użycie tylko tych składowych obiektu, które są właściwe dla przydzielonego zestawu ról. Próba użycia niewłaściwej składowej powinna spowodować wyrzucenie wyjątku.

    4.4.1.5. Konstrukcja obiektu powinna uwzględnić walidację składowych dla danego zestawu ról.

    4.4.1.6. Warto wprowadzić interfejsy dla każdej roli zawierające metody obsługujące składowe danej roli. Klasa bazowa będzie wtedy implementować wszystkie te interfejsy. Zwiększy to czytelność kodu przez łatwiejszą identyfikację przynależności składowych do poszczególnych ról.

   4.4.2. Implementacja za pomocą zastąpienia dziedziczenia kompozycją.

    4.4.2.1. Dla każdej roli należy stworzyć osobną klasę zawierającą składowe charakterystyczne dla danej roli.

    4.4.2.2. Klasy te nie powinny dziedziczyć po klasie bazowej, gdyż spowoduje to powielenie składowych klasy bazowej.

    4.4.2.3. Obiekt klasy bazowej będzie skomponowany z obiektów określających jego role.

    4.4.2.4. Należy umożliwić stworzenie obiektu klasy bazowej - nie może być ona abstrakcyjna.

    4.4.2.5. Po inicjalizacji obiektu głównego nie jest możliwe dodanie / usunięcie obiektu reprezentującego rolę.

    4.4.2.6. W przypadku posiadania referencji do obiektu głównego należy umożliwić korzystanie ze składowych umieszczonych w podtypach. Można to zrealizować za pomocą delegacji metod (z zastosowaniem odpowiednich interfejsów), lub metod umożliwiających pobranie obiektów ról.

 5. Dziedziczenie dynamiczne.

  5.1. Obiekt może posiadać jedną rolę spośród podtypów wymienionych w dziedziczeniu.

  5.2. Rola obiektu może być zmieniona na inną w trakcie jego istnienia.

  5.3. Istnieją trzy główne sposoby implementacji dziedziczenia typu dynamic:

   5.3.1. Implementacja za pomocą “zwykłego” dziedziczenia i konstruktora kopiującego.

    5.3.1.1. Klasy reprezentujące podtypy posiadają konstruktor, którego jednym z argumentów jest referencja do obiektu klasy bazowej. Reprezentuje on “starą” rolę obiektu.

    5.3.1.2. Inicjalizacja powinna obejmować przepisanie wartości charakterystycznych dla klasy bazowej do nowego obiektu. Dotyczy to atrybutów i asocjacji klasy bazowej.

    5.3.1.3. W momencie zmiany roli “stary” obiekt przestaje istnieć. Należy zatem po nim “posprzątać” - usunąć go z ekstensji klasy i rozłączyć jego asocjacje.

    5.3.1.4. Miminalna implementacja za pomocą tego sposobu powinna uwzględnić usunięcie “starego” obiektu z ekstensji klasy.

   5.3.2. Implementacja za pomocą spłaszczenia hierarchii klas.

    5.3.2.1. Implementacja podobna do realizacji dziedziczenia overlapping za pomocą spłaszczenia hierarchii klas. Dyskryminatorem jest pojedyncza wartość Enum.

    5.3.2.2. Należy zapewnić metody do zmiany roli obiektu.

    5.3.2.3. Podczas zmiany roli obiektu należy poprawnie zainicjalizować atrybuty i asocjacje wymagane w nowej roli. “Stare” atrybuty i asocjacje nie pasujące do nowej roli powinny zostać usunięte.

   5.3.3. Implementacja za pomocą kompozycji.

    5.3.3.1. Implementacja podobna do realizacji dziedziczenia overlapping za pomocą kompozycji.

    5.3.3.2. W danym momencie tylko jeden obiekt “części” reprezentujący rolę obiektu może mieć przypisaną wartość.

    5.3.3.3. Należy zapewnić metody do zmiany roli obiektu.

 6. Wielodziedziczenie.

  6.1. Niemożliwe do bezpośredniej implementacji w języku Java i podobnych.

  6.2. Należy zastąpić jedną z gałęzi dziedziczenia implementacją interfejsów, jak na diagramie poniżej. Multiinheritance

  6.3. Należy pamiętać, że zarówno klasa Student jak i WorkingStudent z przykładu powinny implementować interfejs IStudent. W przeciwnym razie wymienione klasy nie będą miały części wspólnej, co jest w sprzeczności z zasadą dziedziczenia.

 7. Dziedziczenie wieloaspektowe

  7.1. Standardowo obiekt posiada dokładnie jedną rolę z każdej hierarchii dziedziczenia związanej z tą konstrukcją.

  7.2. Rola ta nie może być zmieniona w trakcie życia obiektu.

  7.3. Jedna gałąź dziedziczenia może być zaimplementowana jako “zwykłe” dziedziczenie, druga jako kompozycja lub spłaszczenie hierarchii w klasie bazowej.