1. Myślenie systemowe
Programowanie to nie tylko pisanie kodu, ale przede wszystkim projektowanie struktury. Dobre projektowanie obiektowe (OOD) sprawia, że system jest łatwy w utrzymaniu i rozbudowie. Zanim napiszesz pierwszą linię kodu class, powinieneś wiedzieć, jak obiekty będą ze sobą rozmawiać.
2. Relacje: "Jest" vs "Ma"
Dziedziczenie (Is-A) jest bardzo silnym związkiem. Często lepiej zastosować kompozycję lub agregację (Has-A), co daje większą elastyczność. Przykład: Zamiast robić PracownikProgramista przez dziedziczenie, lepiej mieć Pracownik, który "ma" przypisaną Rola.
01
Planowanie architektury (Diagram UML)
Czego student się nauczy
Wizualizacji struktury klas, planowania pól i metod oraz określania widoczności (publiczne/prywatne) przed przystąpieniem do kodowania.
Scenariusz
Przed przystąpieniem do pisania jakiejkolwiek linii kodu w dużym projekcie informatycznym, kluczowe jest stworzenie precyzyjnego planu interakcji między poszczególnymi komponentami systemu. Twoim zadaniem jest wcielenie się w rolę architekta oprogramowania i zaprojektowanie kompleksowego systemu obsługi Biblioteki Narodowej. Musisz zidentyfikować kluczowe obiekty, takie jak książki, autorzy oraz czytelnicy, a następnie precyzyjnie opisać relacje zachodzące między nimi. Wykorzystaj standard Mermaid do stworzenia diagramu klas, który uwzględni nie tylko nazwy obiektów, ale także ich prywatne atrybuty i publiczne metody operacyjne. Takie podejście pozwala na wczesne wykrycie błędów projektowych i znacząco ułatwia późniejszą implementację w języku Python. Pamiętaj o oznaczeniu widoczności pól za pomocą symboli plus i minus, co jest fundamentem poprawnej hermetyzacji danych. Dobrze przygotowany diagram UML staje się uniwersalnym językiem komunikacji w zespole programistycznym, eliminując nieporozumienia dotyczące funkcjonalności systemu. Poprawna realizacja tego zadania pokaże Twoje umiejętności analitycznego myślenia i profesjonalnego podejścia do inżynierii oprogramowania.
Wymagania techniczne
- Zaprojektuj strukturę systemu bibliotecznego, identyfikując klasy:
Biblioteka, Ksiazka, Autor oraz Czytelnik.
- Stwórz diagram klas wykorzystując notację Mermaid, uwzględniając trzy sekcje każdej klasy (nazwa, pola, metody).
- Zastosuj symbole
+ (publiczne) oraz - (prywatne) do określenia widoczności atrybutów i metod.
- Zdefiniuj typy danych dla wszystkich pól (np.
String, Boolean, List).
- Zaznacz na diagramie relacje kompozycji (silne powiązanie) oraz agregacji (luźne powiązanie) między obiektami.
- Określ metody kluczowe, takie jak
wypozycz(), zwroc() czy szukaj_autora().
- Uwzględnij relację dziedziczenia, jeśli planujesz różne typy książek (np.
Ebook, Audiobook).
- Wyjaśnij, jak poprawnie zaplanowana architektura UML zapobiega powstawaniu "kodu spaghetti" w późniejszym etapie.
Wskazówki wykonania
- Użyj notacji Mermaid lub prostego rysunku, aby narysować strukturę Twojego systemu.
- Klasy oznaczaj za pomocą słowa kluczowego
class, a ich treść zamknij w klamrach { }.
- Zastosuj relacje:
*-- to kompozycja, o-- to agregacja, a <|-- to dziedziczenie.
- Pamiętaj o znaku minusa przed polami, które mają być prywatne, np.
-String _tytul.
- Metody publiczne, dostępne dla innych obiektów, oznaczaj znakiem plusa:
+wypozycz().
- Zastanów się, czy każda książka musi posiadać autora (agregacja) czy jest on jej integralną częścią.
- To zadanie uczy, jak "zobaczyć" cały program i jego problemy jeszcze przed napisaniem pierwszej linii kodu.
- Sprawdź, czy Twój diagram jest czytelny dla osoby, która zupełnie nie zna Twojego pomysłu na aplikację.
- Wyjaśnij, dlaczego precyzyjny diagram UML ułatwia podział pracy w większym zespole programistycznym.
- Zauważ, że poprawka na etapie diagramu jest "tańsza" niż refaktoryzacja gotowego, działającego kodu.
Przykładowy ekran
classDiagram
Biblioteka *-- Ksiazka
Ksiazka o-- Autor
class Ksiazka {
-String tytul
-Boolean dostepna
+wypozycz()
}
Wnioski do opracowania
- Wyjaśnij szczegółowo, dlaczego warto poświęcić cenny czas na przygotowanie diagramu UML przed rozpoczęciem pisania kodu w Pythonie.
- Opisz fundamentalną różnicę między kompozycją (silne powiązanie) a agregacją (luźne powiązanie) na przykładzie systemu bibliotecznego.
- Omów znaczenie rygorystycznego stosowania symboli widoczności (
+, -) dla poprawnej hermetyzacji danych w Twoim projekcie.
- Przeanalizuj, w jaki sposób diagramy klas ułatwiają szybką identyfikację nadmiarowych zależności między poszczególnymi komponentami systemu.
- Zastanów się nad strategiczną rolą diagramu UML jako uniwersalnego języka komunikacji technicznej w profesjonalnych zespołach wieloosobowych.
- Wnioskuj o znacznie niższych "kosztach" wprowadzania zmian na etapie projektu w porównaniu do kosztownej refaktoryzacji gotowego kodu.
- Porównaj nowoczesną notację Mermaid do tradycyjnych narzędzi graficznych do rysowania diagramów technicznych (np. Enterprise Architect).
- Opisz precyzyjny sposób odwzorowania relacji dziedziczenia z diagramu na konkretną składnię klas języka Python.
- Sprawdź doświadczalnie, czy Twój diagram uwzględnia wszystkie niezbędne metody operacyjne, takie jak
wypozycz() czy zwroc().
Rozwiązanie
3. Zasady SOLID
SOLID to zbiór 5 zasad, które pomagają uniknąć "kodu spaghetti". Najważniejsza to SRP (Single Responsibility Principle) – każda klasa powinna odpowiadać tylko za jedną rzecz. Jeśli klasa User zapisuje się do bazy i wysyła e-maile, to robi za dużo.
02
Relacje między klasami (Kompozycja i Agregacja)
Czego student się nauczy
Implementacji praktycznych relacji "posiadania" oraz rozumienia cyklu życia obiektów zależnych.
Scenariusz
W profesjonalnym programowaniu obiektowym wybór odpowiedniego rodzaju powiązania między klasami ma ogromny wpływ na elastyczność i łatwość utrzymania całego systemu. Twoim celem jest stworzenie zaawansowanego modelu komputera, który demonstruje praktyczne różnice między silną kompozycją a luźniejszą agregacją obiektów. Musisz zaprojektować klasę Komputer w taki sposób, aby jej procesor był nierozerwalnie związany z cyklem życia jednostki centralnej, powstając i ginąc wraz z nią. Jednocześnie opracuj system obsługi urządzeń peryferyjnych podłączanych przez USB, które mogą istnieć niezależnie od głównego urządzenia i być swobodnie przenoszone między różnymi stacjami roboczymi. Takie rozróżnienie uczy, jak poprawnie zarządzać pamięcią i cyklem życia obiektów w skomplikowanych hierarchiach sprzętowych. Twoim zadaniem jest zweryfikowanie zachowania programu po usunięciu obiektu nadrzędnego i upewnienie się, że urządzenia agregowane nadal pozostają dostępne w pamięci. Jest to kluczowa lekcja projektowania systemów modułowych, które są odporne na gwałtowne zmiany strukturalne. Gotowy moduł będzie stanowił doskonałą ilustrację tego, jak relacje "jest" i "ma" definiują architekturę nowoczesnego oprogramowania.
Wymagania techniczne
- Skonstruuj klasę
Komputer, która zarządza cyklem życia obiektu Procesor poprzez silną kompozycję.
- Zaimplementuj tworzenie instancji procesora bezpośrednio w konstruktorze
__init__ klasy nadrzędnej.
- Opracuj mechanizm agregacji dla urządzeń peryferyjnych (np.
Mysz, Klawiatura) poprzez listę podlaczone_urzadzenia.
- Stwórz metodę
podlacz_usb(), umożliwiającą dodawanie zewnętrznych obiektów do systemu.
- Przeprowadź test usuwania obiektu komputera za pomocą instrukcji
del i zaobserwuj zachowanie procesora.
- Udowodnij, że urządzenia peryferyjne istnieją w pamięci niezależnie od komputera po jego skasowaniu.
- Dodaj do klas metody
__del__ (destruktory), aby wizualizować moment usuwania obiektów z pamięci.
- Wyjaśnij różnice w odpowiedzialności za pamięć między kompozycją a agregacją w dużych systemach.
Wskazówki wykonania
- W metodzie
__init__ klasy Komputer utwórz instancję procesora: self.cpu = Procesor().
- Metoda
podlacz_usb(self, urzadzenie) powinna jedynie dodawać gotowy obiekt do listy agregatów.
- Wypisz komunikat diagnostyczny w metodzie magicznej
__del__ każdej klasy (tzw. destruktor).
- Stwórz obiekt
pc = Komputer() oraz mysz = Mysz(), a następnie połącz je ze sobą.
- Wywołaj jawnie
del pc i zaobserwuj w konsoli, które destruktory uruchomiły się automatycznie.
- Sprawdź, czy obiekt
mysz nadal istnieje i działa poprawnie po usunięciu obiektu komputera.
- To zadanie uczy kluczowej w systemach koncepcji zarządzania "własnością" obiektów (Ownership).
- Wyjaśnij, dlaczego kompozycja jest znacznie silniejszym typem powiązania niż zwykła agregacja.
- Zauważ, że
__del__ nie zawsze uruchamia się natychmiast – zależy to od mechanizmu Garbage Collector.
- Zastanów się, jak unikać błędów przy próbie użycia obiektu, którego logiczny "właściciel" już nie istnieje.
Przykładowy ekran
>>> mysz = UrzadzenieUSB("Mysz")
>>> pc = Komputer()
>>> pc.podlacz_usb(mysz)
>>> del pc
>>> print(mysz.nazwa)
Mysz # Urządzenie nadal istnieje!
Wnioski do opracowania
- Wyjaśnij kluczową różnicę technologiczną między kompozycją a agregacją w kontekście całkowitego usuwania obiektów nadrzędnych z pamięci.
- Opisz techniczne aspekty zarządzania cyklem życia obiektu
Procesor, który jest integralną i nierozerwalną częścią komputera.
- Omów korzyści projektowe płynące z możliwości swobodnego przenoszenia agregowanych urządzeń (np. USB) między różnymi systemami IT.
- Przeanalizuj zachowanie metody magicznej
__del__ i jej bezpośrednią zależność od mechanizmu Garbage Collector w Pythonie.
- Zastanów się nad problemem "wiszących odwołań" (dangling references) przy błędnie zaprojektowanych relacjach między obiektami.
- Wnioskuj o modularności systemów, które skutecznie separują stałe komponenty sprzętowe od wymiennych peryferiów zewnętrznych.
- Porównaj odpowiedzialność za pamięć operacyjną w silnie powiązanej kompozycji do luźno powiązanej agregacji obiektów klasy.
- Opisz proces debugowania cyklu życia obiektów przy użyciu pomocniczych komunikatów diagnostycznych umieszczonych w destruktorach.
- Sprawdź, czy usunięcie obiektu głównego faktycznie zwalnia zasoby procesora, ale jednocześnie zachowuje pełny stan urządzeń zewnętrznych.
Rozwiązanie
4. Wzorce projektowe
To gotowe rozwiązania powtarzających się problemów architektonicznych. Przykładowo, Singleton gwarantuje, że w całym programie istnieje tylko jedna instancja danej klasy (np. dla ustawień systemowych).
03
Refaktoryzacja do SRP (Single Responsibility Principle)
Czego student się nauczy
Identyfikowania nadmiarowych odpowiedzialności w klasach i dzielenia kodu na mniejsze, wyspecjalizowane komponenty.
Scenariusz
Jednym z najczęstszych błędów popełnianych przez początkujących programistów jest tworzenie tzw. "boskich klas", które biorą na siebie zbyt wiele odpowiedzialności jednocześnie. Wyobraź sobie, że dysponujesz przestarzałym modułem raportowym, który samodzielnie pobiera dane, formatuje je do standardu HTML i zajmuje się zapisem plików na dysku. Twoim wyzwaniem jest przeprowadzenie gruntownej refaktoryzacji tego kodu zgodnie z zasadą Single Responsibility Principle (SRP) ze standardu SOLID. Musisz rozbić ten monolityczny obiekt na trzy niezależne, wyspecjalizowane komponenty, z których każdy będzie odpowiadał za jeden, ściśle określony etap procesu. Dzięki takiemu podejściu, Twój kod stanie się znacznie łatwiejszy do testowania, ponieważ będziesz mógł weryfikować każdą funkcjonalność w całkowitej izolacji. Klasa nadrzędna powinna jedynie koordynować współpracę tych małych modułów, przekazując dane między nimi w sposób uporządkowany. Taka modularność pozwala na błyskawiczną wymianę np. formatera HTML na PDF bez konieczności modyfikowania logiki pobierania informacji. Twoje rozwiązanie pokaże, jak za pomocą prostych zmian strukturalnych można drastycznie podnieść jakość i elastyczność profesjonalnych systemów produkcyjnych.
Wymagania techniczne
- Zidentyfikuj nadmiarowe odpowiedzialności w monolitycznej klasie raportowej i zaplanuj jej podział.
- Skonstruuj klasę
DanePobieracz odpowiedzialną wyłącznie za ekstrakcję informacji ze źródeł danych.
- Opracuj klasę
HTMLFormater, której jedynym zadaniem jest konwersja surowych danych na format znaczników.
- Zaimplementuj klasę
PlikZapisywacz, zajmującą się fizycznym utrwalaniem wyników na dysku.
- Stwórz klasę koordynującą
SystemRaportowy, wykorzystując wstrzykiwanie zależności (Dependency Injection) w konstruktorze.
- Zapewnij, aby każda z klas wyspecjalizowanych posiadała tylko jedną, jasną odpowiedzialność biznesową.
- Przetestuj działanie całego potoku przetwarzania, wywołując metodę
uruchom() w klasie nadrzędnej.
- Wyjaśnij korzyści płynące z takiej architektury przy próbie dodania nowego formatu (np.
CSVFormater).
Wskazówki wykonania
- Wyodrębnij logikę pobierania danych do całkowicie nowej, wyspecjalizowanej klasy
DaneFetcher.
- Stwórz klasę
Formater, która przyjmuje surowe dane i zwraca str w formacie HTML lub JSON.
- Klasa
Zapisywacz powinna zajmować się wyłącznie obsługą plików (metody open i write).
- Główna klasa zarządcza powinna otrzymywać instancje tych klas pomocniczych w swoim konstruktorze.
- Przetestuj działanie całego systemu, wywołując jedną, główną metodę startową w klasie koordynującej.
- Spróbuj podmienić
HTMLFormater na JSONFormater bez zmieniania kodu w głównej klasie systemu.
- To zadanie uczy, jak budować profesjonalne systemy z wymiennych, modularnych "klocków".
- Wyjaśnij, dlaczego małe, wyspecjalizowane klasy są znacznie łatwiejsze do zrozumienia i testowania.
- Zauważ, że zasada SRP skutecznie zapobiega sytuacjom, w których zmiana w jednym module psuje inny.
- Zastanów się, czy Twoje klasy nie są "zbyt małe" – unikaj tworzenia tzw. anemicznych modeli danych.
Przykładowy ekran
>>> sys = SystemRaportowy(Generator(), Formater(), Zapisywacz())
>>> sys.uruchom()
Dane pobrane.
Formatowanie do HTML...
Zapisano do raport.html
Wnioski do opracowania
- Wyjaśnij szczegółowo, dlaczego kod podzielony na małe klasy jest znacznie łatwiejszy do profesjonalnego testowania (np. testami jednostkowymi).
- Opisz zasadę Single Responsibility Principle (SRP) i jej bezpośredni wpływ na uniknięcie powstania trudnych w utrzymaniu "boskich klas".
- Omów zalety stosowania zaawansowanej techniki wstrzykiwania zależności (Dependency Injection) w budowaniu modularnych systemów.
- Przeanalizuj proces rozbijania monolitycznego modułu na mniejsze, wyspecjalizowane komponenty takie jak Pobieracz, Formater i Zapisywacz.
- Zastanów się nad ryzykiem tworzenia "zbyt małych" klas (tzw. anemiczne modele danych) i jak zachować zdrowy balans w architekturze.
- Wnioskuj o elastyczności systemu, który pozwala na błyskawiczną wymianę formatu danych bez żadnej ingerencji w logikę biznesową.
- Porównaj czytelność i czystość kodu przed i po refaktoryzacji pod kątem łatwości wprowadzania zupełnie nowych funkcjonalności.
- Opisz rolę klasy koordynującej (tzw. Controller) w precyzyjnym zarządzaniu przepływem danych między wszystkimi modułami pomocniczymi.
- Sprawdź doświadczalnie, czy każda z Twoich nowych klas ma dokładnie jedną, jasno określoną odpowiedzialność techniczną w systemie.
Rozwiązanie
5. Projekt Zaliczeniowy
Czas na podsumowanie kursu. Twoim zadaniem jest stworzenie kompletnej aplikacji. Wykorzystaj w niej: 1. Minimum 3 współpracujące klasy, 2. Dziedziczenie, 3. Obsługę wyjątków, 4. Zapis do JSON/CSV, 5. Proste menu w konsoli.
04
Wzorce projektowe (Singleton Konfiguracyjny)
Czego student się nauczy
Implementacji wzorca Singleton w Pythonie przy użyciu metody magicznej __new__ oraz zrozumienia globalnego stanu aplikacji.
Scenariusz
Podczas tworzenia rozbudowanych aplikacji, często zachodzi potrzeba zapewnienia, aby pewne kluczowe komponenty, takie jak ustawienia systemowe czy połączenia z bazą danych, istniały tylko w jednej instancji. Twoim zadaniem jest zaimplementowanie wzorca projektowego Singleton dla centralnej klasy konfiguracyjnej Twojej aplikacji. Musisz wykorzystać zaawansowaną metodę magiczną __new__, aby przejąć kontrolę nad procesem tworzenia obiektów i zagwarantować, że każde wywołanie konstruktora zwróci ten sam unikalny obiekt z pamięci. Takie rozwiązanie eliminuje ryzyko powstawania niespójnych ustawień w różnych częściach programu i ułatwia globalne zarządzanie parametrami pracy systemu. Pamiętaj jednak o odpowiedzialnym korzystaniu z tego wzorca, ponieważ nadmiarowe stosowanie stanu globalnego może utrudnić późniejsze testowanie i debugowanie kodu. Twoja klasa powinna przechowywać swoją jedyną instancję w atrybucie klasowym, chroniąc go przed przypadkowym nadpisaniem. Przetestuj swoje rozwiązanie, sprawdzając identyfikatory id() kilku rzekomo różnych obiektów, aby potwierdzić ich tożsamość fizyczną. Poprawne wykonanie tego zadania wprowadzi Cię w świat profesjonalnych wzorców projektowych, które są fundamentem stabilnych systemów korporacyjnych.
Wymagania techniczne
- Zaimplementuj klasę
UstawieniaAplikacji, stosując wzorzec projektowy Singleton.
- Nadpisz metodę magiczną
__new__, aby kontrolować proces alokacji pamięci dla nowej instancji.
- Zdefiniuj atrybut klasowy
_instance, służący do przechowywania jedynego dopuszczalnego obiektu klasy.
- Dodaj do klasy atrybuty konfiguracyjne, takie jak
wersja_systemu czy sciezka_bazy_danych.
- Przeprowadź test porównawczy za pomocą operatora
is, sprawdzając czy różne zmienne wskazują na ten sam obiekt.
- Udowodnij spójność stanu globalnego, zmieniając wartość atrybutu w jednej zmiennej i odczytując ją w drugiej.
- Zabezpiecz proces inicjalizacji tak, aby
__init__ nie nadpisywał danych przy ponownym "tworzeniu" instancji.
- Wskaż potencjalne pułapki wzorca Singleton w kontekście testowania kodu i wielowątkowości.
Wskazówki wykonania
- W metodzie magicznej
__new__(cls) sprawdź najpierw if not hasattr(cls, '_instance'):.
- Jeśli instancja jeszcze nie istnieje, utwórz ją:
cls._instance = super().__new__(cls).
- Zawsze zwracaj zapisaną instancję
cls._instance niezależnie od liczby wywołań konstruktora.
- Uważaj na metodę
__init__: w Pythonie wywołuje się ona po każdym wywołaniu nazwy klasy.
- Możesz zastosować flagę
self._initialized = True, aby uniknąć wielokrotnego nadpisywania danych.
- Porównaj dwa nowo utworzone obiekty za pomocą operatora tożsamości
is lub funkcji id().
- To zadanie uczy pełnej kontroli nad sposobem powstawania i alokacji obiektów w pamięci operacyjnej.
- Wyjaśnij scenariusze, w których Singleton jest naprawdę przydatny (np. system logowania, pula połączeń).
- Zauważ, że wzorzec Singleton utrudnia testy jednostkowe, ponieważ stan obiektu jest współdzielony globalnie.
- Zastanów się, czy w danym przypadku prosty moduł z funkcjami nie byłby lepszą alternatywą dla Singletona.
Przykładowy ekran
>>> u1 = Ustawienia()
>>> u2 = Ustawienia()
>>> print(u1 is u2)
True
>>> u1.db = "test.db"
>>> print(u2.db)
test.db
Wnioski do opracowania
- Wyjaśnij szczegółowo, jakie poważne zagrożenia niesie ze sobą nadużywanie wzorca Singleton (który jest de facto formą zmiennej globalnej).
- Opisz techniczne działanie metody magicznej
__new__ i jej kluczową rolę w kontrolowaniu procesu alokacji pamięci dla obiektów.
- Omów skuteczny sposób ochrony atrybutu klasowego
_instance przed jego przypadkowym nadpisaniem z zewnątrz klasy.
- Przeanalizuj problematykę wielokrotnego wywoływania metody
__init__ przy każdej kolejnej próbie dostępu do obiektu typu Singleton.
- Zastanów się nad nowoczesnymi alternatywami dla Singletona, takimi jak proste moduły Pythona zawierające funkcje i zmienne stanowe.
- Wnioskuj o spójności stanu globalnego aplikacji (np. ustawienia, logowanie) dzięki gwarantowanej unikalności obiektu konfiguracyjnego.
- Porównaj obiektywne trudności w testowaniu Singletonów do testowania klas wykorzystujących elastyczne wstrzykiwanie zależności.
- Opisz zachowanie operatora tożsamości
is oraz funkcji id() przy porównywaniu wielu różnych referencji do tego samego Singletona.
- Sprawdź i opisz, czy Twoja implementacja Singletona jest bezpieczna w środowiskach wielowątkowych i jak można by ją ulepszyć.
Rozwiązanie
6. Separacja logiki od UI
Nigdy nie mieszaj instrukcji input() i print() wewnątrz metod logicznych klasy. Klasa powinna zwracać dane lub rzucać wyjątki, a to interfejs użytkownika decyduje, jak je wyświetlić.
7. Dokumentowanie kodu
Używaj Docstringów (potrójne cudzysłowy) pod każdą klasą i metodą. Dobry kod dokumentuje się sam poprzez czytelne nazwy, ale wyjaśnienie "dlaczego" coś robimy jest bezcenne.
8. Pakowanie i moduły
Podziel swój projekt na kilka plików: models.py (klasy), app.py (logika główna), main.py (punkt startowy). Używaj import do łączenia ich w całość.
9. Refaktoryzacja końcowa
Zanim oddasz projekt, przeczytaj swój kod jeszcze raz. Usuń nieużywane zmienne, popraw wcięcia (zgodność z PEP 8) i upewnij się, że nazwy metod są czasownikami, a klas rzeczownikami.
10. Gratulacje!
Ukończyłeś kurs Programowania Obiektowego w Pythonie. Posiadasz wiedzę, która pozwoli Ci budować profesjonalne, stabilne i skalowalne aplikacje. Powodzenia w realizacji własnych projektów!
05
Projekt Zaliczeniowy - Szkielet Aplikacji
Czego student się nauczy
Integrowania wszystkich poznanych technik (klasy, dziedziczenie, wyjątki, serializacja) w jeden działający produkt końcowy.
Scenariusz
Nadszedł moment kulminacyjny kursu, w którym musisz połączyć wszystkie zdobyte dotychczas umiejętności w jeden spójny i funkcjonalny projekt końcowy. Twoim zadaniem jest stworzenie solidnego szkieletu aplikacji biznesowej, która demonstruje praktyczne zastosowanie zaawansowanych mechanizmów programowania obiektowego w rzeczywistym scenariuszu. Zaprojektuj system zarządzania, taki jak menedżer wydatków lub rezerwacja biletów, dbając o poprawną hierarchię klas oraz rygorystyczną obsługę sytuacji wyjątkowych. Aplikacja musi posiadać intuicyjne menu konsolowe, umożliwiające interakcję z danymi, ich trwały zapis do formatu JSON oraz bezbłędne odczytywanie stanu przy ponownym uruchomieniu. Kluczowym elementem oceny będzie czystość Twojego kodu, zgodność z normami PEP 8 oraz umiejętna separacja logiki operacyjnej od warstwy prezentacji. Zadbaj o to, aby każda klasa miała jasno określoną odpowiedzialność, a wszelkie błędy użytkownika były przechwytywane i profesjonalnie komunikowane. Pamiętaj, że diabeł tkwi w szczegółach, dlatego poświęć czas na refaktoryzację i usunięcie wszelkich zbędnych fragmentów kodu przed oddaniem pracy. Twój projekt będzie ostatecznym dowodem na to, że jesteś gotowy do budowania samodzielnych i skalowalnych rozwiązań w języku Python. Powodzenia w realizacji Twojej autorskiej wizji systemowej!
Wymagania techniczne
- Zaprojektuj i zaimplementuj szkielet autorskiej aplikacji (np. system rezerwacji, katalog gier lub menedżer finansowy).
- Zastosuj minimum 3 współpracujące ze sobą klasy, dbając o poprawne relacje między nimi (dziedziczenie/kompozycja).
- Opracuj rozbudowane menu konsolowe działające w pętli
while True, z czytelnym podziałem na moduły.
- Wprowadź rygorystyczną obsługę wyjątków dla wszystkich danych wprowadzanych przez użytkownika (np.
ValueError).
- Zintegruj system trwałego zapisu i odczytu danych do formatu JSON przy użyciu modułu
json.
- Zapewnij pełną separację logiki biznesowej (metody klas) od warstwy interakcji z użytkownikiem (print/input).
- Udokumentuj każdą klasę i metodę za pomocą profesjonalnych Docstringów zgodnych ze standardem Pythona.
- Wykonaj refaktoryzację końcową, zapewniając pełną zgodność kodu z wytycznymi stylu PEP 8.
- Wybierz konkretny temat biznesowy, który Cię interesuje (np. system RPG, inwentarz, rezerwacja miejsc).
- Zacznij od zdefiniowania czystych klas danych (np.
Produkt, Uzytkownik, Zdarzenie).
- Dodaj jedną, silną klasę zarządzającą, która będzie sterować całą logiką aplikacji (Controller).
- Zaimplementuj mechanizm zapisu i odczytu JSON na samym początku – to bardzo ułatwi Ci dalsze testy.
- Pamiętaj o walidacji funkcji
input(): zawsze sprawdzaj, czy użytkownik wpisał poprawny typ danych.
- Użyj bloków
try-except do bezpiecznego łapania błędów podczas operacji na plikach i danych.
- Zastosuj profesjonalne Docstringi:
"""To jest klasa opisująca...""" dla każdego komponentu.
- To zadanie jest ostatecznym sprawdzianem Twoich umiejętności architektonicznych i programistycznych.
- Przed oddaniem pracy sprawdź kod pod kątem zgodności z PEP 8 (odpowiednie wcięcia, spacje i nazewnictwo).
- Wyjaśnij w krótkiej dokumentacji dołączonej do projektu, jakie wzorce projektowe zdecydowałeś się zastosować.
Przykładowy ekran
--- SYSTEM ZARZĄDZANIA KINEM ---
1. Dodaj film
2. Pokaż repertuar
3. Zapisz i wyjdź
Wybór: 1
Podaj tytuł: Matrix
[OK] Film dodany do bazy.
Wnioski do opracowania
- Wyjaśnij szczegółowo, która część programowania obiektowego była dla Ciebie najtrudniejsza do opanowania i z jakiego powodu.
- Opisz proces integracji minimum 3 współpracujących klas w jeden spójny, profesjonalny i w pełni działający produkt końcowy.
- Omów znaczenie całkowitej separacji logiki biznesowej od warstwy interakcji z użytkownikiem (mechanizm input/print).
- Przeanalizuj skuteczność zastosowanej obsługi wyjątków w zabezpieczaniu aplikacji przed błędami użytkownika i awariami systemu.
- Zastanów się nad zaletami stosowania formatu JSON dla trwałego przechowywania i łatwego odczytu aktualnego stanu Twojego systemu.
- Wnioskuj o jakości kodu na podstawie pełnej zgodności z normami PEP 8 oraz przejrzystości stosowanych przez Ciebie Docstringów.
- Porównaj początkową wizję projektu z jego ostateczną implementacją pod kątem wszystkich napotkanych trudności technicznych.
- Opisz, w jaki sposób rzetelne planowanie architektury (zadanie 1) pomogło Ci w sprawnej realizacji Twojego projektu końcowego.
- Sprawdź stabilność aplikacji przy wielokrotnym uruchamianiu i przeprowadzaniu skomplikowanych, wieloetapowych operacji biznesowych.
Rozwiązanie