Wymagania formalne:
Sprawozdanie z Modułu 3 powinno kłaść szczególny nacisk na poprawne wykorzystanie polimorfizmu oraz dziedziczenia. Należy wykazać, że kody są odporne na błędy (np. sprawdzanie zakresu indeksatora). Wymagana jest strona tytułowa, treść zadania, komentarze w kodzie, zrzuty ekranu oraz sekcja z wnioskami, w której porównasz działanie nadpisywania (override) z przesłanianiem (new).
D3.1
Inteligentny Katalog Produktów (Indeksator)
Cel

Utworzenie klasy katalogu wykorzystującej indeksatory do alternatywnego dostępu do danych (nie tylko po numerze porządkowym). Student uczy się projektowania wygodnych interfejsów dostępu do danych wewnątrz własnych obiektów.

Scenariusz

Zaprojektuj klasę o nazwie "Katalog", która przechowuje listę produktów jako prostą strukturę wewnętrzną (możesz użyć tablicy stringów dla uproszczenia). Klasa musi oferować dwa sposoby dostępu do danych za pomocą indeksatorów. Pierwszy z nich pozwala na pobranie lub zmianę opisu produktu za pomocą standardowego numeru indeksu (int). Drugi indeksator, będący wersją rozszerzoną, powinien pozwalać na sprawdzenie ceny produktu na podstawie jego nazwy (string jako indeks). Twoim celem jest zapewnienie, aby próba dostępu do nieistniejącego indeksu lub nazwy nie powodowała awarii programu, lecz zwracała stosowną informację, np. "Brak w bazie". Program w metodzie Main powinien wypełnić katalog kilkoma przykładowymi danymi i zademonstrować ich odczyt za pomocą obu rodzajów indeksatorów. Dzięki takiemu rozwiązaniu Twój obiekt zachowuje się bardzo naturalnie, przypominając w działaniu słownik lub bazę klucz-wartość. Jest to świetny przykład na to, jak C# pozwala na budowanie bardzo eleganckich struktur danych przyjaznych dla innych programistów. Pamiętaj, aby opisać w dokumentacji sposób przeszukiwania tablicy wewnątrz indeksatora tekstowego.

Sugerowane kroki do wykonania
  1. Zdefiniuj klasę o nazwie Katalog.
  2. Dodaj prywatną tablicę stringów przechowującą nazwy 10 produktów.
  3. Dodaj oddzielną tablicę decimal przechowującą ich ceny (pod tymi samymi indeksami).
  4. Zaimplementuj standardowy indeksator: public string this[int i].
  5. Wewnątrz akcesora get dodaj warunek sprawdzający, czy indeks i mieści się w zakresie 0-9.
  6. Zaimplementuj drugi indeksator: public decimal this[string nazwa].
  7. Wewnątrz indeksatora tekstowego użyj pętli for, aby znaleźć pozycję produktu o danej nazwie.
  8. Jeśli produkt zostanie znaleziony, zwróć jego cenę z tablicy cen.
  9. W przeciwnym wypadku zwróć wartość 0 lub zgłoś specjalny sygnał błędu.
  10. W Main utwórz katalog i przetestuj oba sposoby odczytywania danych.
D3.2
Analizator Matematyczny (Metody z out)
Cel

Praktyczne wykorzystanie modyfikatora out do zwracania wielu wyników cząstkowych z jednej metody statycznej. Student uczy się optymalizować przepływ danych w programie poprzez grupowanie wyników obliczeń.

Scenariusz

Pracujesz nad modułem statystycznym dla aplikacji przetwarzającej wyniki egzaminów studenckich. Twoim zadaniem jest napisanie metody, która przyjmuje jako wejście tablicę liczb całkowitych (oceny lub punkty) i zwraca za jednym razem trzy ważne informacje: wartość minimalną, wartość maksymalną oraz łączną sumę punktów. Aby zrealizować to wymaganie, musisz skorzystać z parametrów wyjściowych typu out w sygnaturze metody. Takie podejście jest znacznie wygodniejsze niż pisanie trzech osobnych metod, które trzy razy musiałyby przeglądać tę samą tablicę. Twoja metoda powinna być statyczna i umieszczona w klasie narzędziowej o nazwie Statystyka. Program w konsoli powinien pobrać od użytkownika ciąg liczb rozdzielonych spacją, przetworzyć go i wyświetlić kompletny raport statystyczny. Zwróć uwagę na inicjalizację zmiennych out przed wyjściem z metody – jest to wymóg kompilatora języka C#. Dodatkowo zapewnij obsługę przypadku, gdy tablica wejściowa jest pusta lub null. Taki moduł jest bazą dla wielu systemów analitycznych typu Business Intelligence. Finalnie zaprezentuj wyniki w czytelnej formie liczbowej na ekranie terminala.

Sugerowane kroki do wykonania
  1. Zdefiniuj statyczną klasę o nazwie NarzedziaAnalizy.
  2. Zaprojektuj metodę ProcesujDane przyjmującą tablicę int[] dane.
  3. Dodaj do metody trzy parametry wyjściowe oznaczone jako out (min, max, suma).
  4. Zapewnij walidację: jeśli tablica jest pusta, ustaw wszystkie parametry out na 0 i przerwij.
  5. Zainicjalizuj lokalne zmienne pomocnicze min i max na pierwszą wartość z tablicy.
  6. Użyj pętli foreach do obliczenia wszystkich trzech statystyk w jednym przejściu po tablicy.
  7. Na samym końcu metody przypisz obliczone wartości do parametrów zdefiniowanych jako out.
  8. W metodzie Main utwórz przykładową tablicę ocen.
  9. Wywołaj metodę ProcesujDane, przekazując nowo zadeklarowane zmienne pomocnicze z kodem out.
  10. Wyświetl uzyskane wyniki min/max/suma z odpowiednim opisem słownym.
D3.3
Hierarchia ZOO: Dziedziczenie w praktyce
Cel

Opracowanie hierarchii klas opisującej królestwo zwierząt z wykorzystaniem dziedziczenia oraz słowa kluczowego base. Student uczy się rozszerzać funkcjonalność klas bazowych bez konieczności modyfikowania ich oryginalnego kodu.

Scenariusz

Twoje zadanie polega na stworzeniu systemu ewidencji mieszkańców ogrodu zoologicznego. Zacznij od zdefiniowania klasy bazowej o nazwie "Zwierze", która posiada wspólne cechy, takie jak nazwa gatunkowa oraz wiek. Następnie stwórz dwie klasy pochodne, na przykład "Ptak" (dodające pole rozpiętość skrzydeł) oraz "Ssak" (dodające informację o rodzaju futra). Bardzo ważne jest, aby każda z klas pochodnych posiadała konstruktor, który poprawnie deleguje część obowiązków inicjalizacyjnych do konstruktora klasy bazowej przy użyciu : base(...). Dzięki temu unikniesz powielania kodu przypisującego nazwę i wiek w każdej klasie z osobna. Dodaj do każdej z klas metodę o nazwie WydajGlos, która będzie wirtualna w klasie bazowej i nadpisana w klasach szczegółowych (np. ptak może świergotać, a ssak wydawać inny dźwięk). Program ma umożliwić utworzenie małej kolekcji zwierząt i wyświetlenie ich pełnej charakterystyki na podstawie danych zawartych w hierarchii. Taka struktura ułatwia późniejsze dodawanie nowych gatunków, np. gadów czy ryb, poprzez proste rozszerzenie istniejących definicji. Zadanie to uczy poprawnego myślenia obiektowego w oparciu o wspólne cechy obiektów świata rzeczywistego. Napisz program tak, aby na końcu wyświetlił on listę wszystkich zarejestrowanych zwierząt z ich unikalnymi parametrami.

Sugerowane kroki do wykonania
  1. Utwórz klasę bazową Zwierze z publicznymi właściwościami i konstruktorem.
  2. Zdefiniuj w niej wirtualną metodę public virtual void PokazDane() wpisującą podstawowe informacje.
  3. Stwórz klasę Ptak dziedziczącą po Zwierze.
  4. Dodaj w klasie Ptak pole Specyficzne (np. RozpietoscSkrzydel).
  5. Zaimplementuj konstruktor dla klasy Ptak wywołujący : base(...) dla danych podstawowych.
  6. Nadpisz metodę PokazDane w klasie Ptak używając override.
  7. Użyj base.PokazDane() wewnątrz nowej metody, aby wyświetlić najpierw dane ogólne.
  8. Stwórz analogicznie klasę Ssak z jej unikalnymi cechami.
  9. W Main utwórz po jednym obiekcie każdej klasy.
  10. Wywołaj metodę PokazDane na wszystkich egzemplarzach i sprawdź czy wyniki są kompletne.
D3.4
Wielopoziomowy System Biletowy (Polimorfizm)
Cel

Zastosowanie polimorfizmu do dynamicznego wyliczania cen usług w zależności od typu klienta. Student uczy się projektować elastyczne cenniki biletowe oparte na nadpisywaniu metod wirtualnych.

Scenariusz

Zaprojektuj system do obsługi biletów dla kina lub teatru. Klasa bazowa o nazwie "Bilet" powinna zawierać podstawową cenę wejściówki oraz wirtualną metodę o nazwie ObliczCene(). Musisz stworzyć kilka klas pochodnych, reprezentujących różne typy zniżek, np. "BiletUlgowy" (zniżka 50% ważna dla uczniów) oraz "BiletSeniora" (zniżka stała, np. -10 zł od ceny bazowej). W każdej z tych klas nadpisz metodę obliczeniową tak, aby zwracała odpowiednio obniżoną kwotę. To jednak nie wszystko – stwórz również klasę "BiletVIP", w której cena jest wyższa o opłatę za rezerwację miejsca premium. Kluczowym fragmentem zadania jest demonstracja polimorfizmu: stwórz tablicę typu Bilet[], wypełnij ją obiektami różnych klas (ulgowy, VIP, normalny) i w jednej pętli foreach zsumuj całkowity przychód ze sprzedanych biletów. Program ma wyświetlić szczegółowe zestawienie, pokazując typ sprzedanego biletu oraz jego kwotę po rabacie. Rozwiązanie to pokazuje, dlaczego polimorfizm jest tak ceniony przy budowaniu systemów sprzedażowych – pozwala on na dodawanie nowych rodzajów ulg bez dotykania głównej logiki sumującej. Program musi poprawnie zaokrąglać kwoty końcowe i wyświetlać je w formacie walutowym.

Sugerowane kroki do wykonania
  1. Zdefiniuj klasę bazową Bilet z ceną bazową w konstruktorze.
  2. Dodaj publiczną metodę public virtual decimal ObliczCene(), która zwraca wartość pola cena.
  3. Stwórz klasę BiletUlgowy nadpisującą ObliczCene i zwracającą 50% ceny bazowej.
  4. Stwórz klasę BiletSeniora z nadpisaniem metody (np. cena - 10 zł).
  5. Stwórz klasę BiletVIP z nadpisaniem metody (np. cena + 30 zł).
  6. W metodzie Main utwórz tablicę obiektów typu Bilet o rozmiarze 5.
  7. Zainicjalizuj tablicę mieszając różne typy biletów (normalny, ulgowy, VIP).
  8. Wykorzystaj pętlę foreach i zmienną sumatora (decimal) do podliczenia zamówienia.
  9. Wyświetl listę biletów z użyciem polimorficznego wywołania ObliczCene().
  10. Zademonstruj na ekranie końcową kwotę do zapłaty sformatowaną walutowo.
D3.5
System Generowania Dokumentów (Override vs New)
Cel

Demonstracja różnicy między nadpisywaniem metod wirtualnych a ich przesłanianiem przy użyciu słowa kluczowego new. Student zgłębia tajniki wiązania dynamicznego i statycznego w hierarchii klas C#.

Scenariusz

Zaprojektuj system do drukowania różnych typów dokumentów biurowych. Klasa bazowa o nazwie "Dokument" posiada metodę public virtual void Print(), która wypisuje ogólny opis strony. Stwórz klasę "Raport", która nadpisuje tę metodę (używając override), oraz klasę "DokumentTajny", która jedynie przesłania metodę bazową (używając słowa kluczowego new). Twoim celem jest przeprowadzenie testu polegającego na przypisaniu obiektów obu klas pochodnych do zmiennych typu bazowego Dokument. Po wywołaniu metody Print() na obu tych zmiennych, zaobserwujesz, że jeden obiekt wywoła nową wersję metody, a drugi – wersję z klasy bazowej. Jest to krytyczny test zrozumienia mechanizmów OOP, który uczy świadomego projektowania API i unikania tzw. "method hiding" tam, gdzie oczekiwany jest polimorfizm. Program musi wygenerować zestawienie logów, które wyjaśniają użytkownikowi, co się stało przy każdym wywołaniu. Zadanie to jest świetną okazją do dyskusji nad bezpieczeństwem i czytelnością kodu w dużych systemach klasy Enterprise. Opisz w sprawozdaniu wynik tego porównania, wskazując który mechanizm (new czy override) zachowuje własność polimorficzną. Program powinien na końcu wywołać metody po rzutowaniu na typy szczegółowe, aby pokazać, że "ukryta" metoda nadal istnieje w pamięci.

Sugerowane kroki do wykonania
  1. Stwórz klasę Dokument z wirtualną metodą Print().
  2. Stwórz klasę Raport dziedziczącą po Dokument z nadpisaniem (override) Print().
  3. Stwórz klasę DokumentTajny dziedziczącą po Dokument z przesłonięciem (new) Print().
  4. W Main utwórz zmienną: Dokument d1 = new Raport().
  5. W Main utwórz zmienną: Dokument d2 = new DokumentTajny().
  6. Wywołaj d1.Print() i d2.Print() – zanotuj wyniki w sprawozdaniu.
  7. Utwórz jawny obiekt Raport r1 = new Raport() i wywołaj r1.Print().
  8. Utwórz jawny obiekt DokumentTajny t1 = new DokumentTajny() i wywołaj t1.Print().
  9. Porównaj wyniki uzyskane w punktach 6 i 8.
  10. Dodaj komentarz wyjaśniający, dlaczego DokumentTajny przy typie bazowym zachowuje się inaczej niż Raport.