1/40
Projektowanie programu obiektowego

Wstęp: Od kodu do systemu

Umiemy już pisać klasy, dziedziczyć i obsługiwać błędy. Jednak samo znanie składni nie wystarczy, by zbudować duży, sprawny system. Potrzebujemy planu.

W tej części zajmiemy się procesem projektowania. Odpowiemy na pytania:

  • Jakie klasy stworzyć?
  • Jak te klasy powinny ze sobą współpracować?
  • Jak uniknąć bałaganu w kodzie?

Na końcu omówimy wymagania dotyczące projektu zaliczeniowego.

Dlaczego to jest ważne?
Do tej pory uczyłeś się pisać pojedyncze klasy i metody. Ale prawdziwe programy mają ich setki lub tysiące. Bez planu (tzw. architektury) taki system staje się nieczytelny i trudny do utrzymania. Projektowanie obiektowe to umiejętność planowania struktury programu przed napisaniem pierwszej linijki kodu.
Schemat: Myślenie projektowe vs pisanie kodu
2/40
Po co projektować?

Złożoność to Twój największy wróg

Małe programy można pisać "z głowy". Gdy system rośnie, brak struktury prowadzi do "kodu spaghetti" – sytuacji, w której zmiana jednej rzeczy psuje dziesięć innych.

Dobre projektowanie obiektowe (OOD) zapewnia:

  • Łatwość utrzymania: Szybkie znajdowanie i naprawianie błędów.
  • Rozszerzalność: Dodawanie nowych funkcji bez przepisywania całego programu.
  • Czytelność: Inni programiści (lub Ty za miesiąc) zrozumieją, co kod robi.
Co to jest "kod spaghetti"?
To potoczne określenie na chaotyczny, trudny do zrozumienia kod, w którym wszystko jest ze sobą połączone jak nitki makaronu. Ciężko go naprawić, bo ciągnąc za jedną nitkę (zmieniając jedną rzecz), psuje się dziesięć innych.
Ilustracja: Uporządkowane klocki vs splątane kable
3/40
Zasada KISS

Keep It Simple, Stupid

Najprostsze rozwiązania są zazwyczaj najlepsze. Nie komplikuj systemu na zapas.

  • Nie twórz klas "na wszelki wypadek".
  • Pisz metody, które robią tylko jedną rzecz.
  • Pamiętaj: kod jest częściej czytany niż pisany.
Jeśli nie potrafisz wyjaśnić działania klasy w jednym prostym zdaniu, prawdopodobnie jest ona zbyt skomplikowana.
Co to znaczy "na wszelki wypadek"?
To tworzenie klas lub metod, które mogą kiedyś się przydać, ale na razie nie są potrzebne. Np. klasa "KontoBankowe" ma metodę "zrobPrzelewMiędzyKontami", chociaż program tego nigdy nie wymaga. Takie "na zapas" zaśmieca kod i utrudnia jego zrozumienie.
4/40
Zasada DRY

Don't Repeat Yourself

Unikaj powtarzania tego samego kodu w różnych miejscach. Jeśli kopiujesz i wklejasz kod, robisz to źle.

  • Wspólną logikę wynosimy do metod klasy bazowej.
  • Używamy funkcji pomocniczych.
  • Zmienne konfiguracyjne trzymamy w jednym miejscu.

Dzięki temu zmianę wprowadzasz tylko raz, a nie w dziesięciu plikach.

Przykład złamania zasady DRY:
Wyobraź sobie, że w 5 różnych miejscach programu sprawdzasz, czy hasło jest poprawne (np. czy ma 8 znaków, wielką literę, cyfrę). Jeśli potem będziesz musiał zmienić minimalną długość na 10 znaków, musisz to zrobić w 5 miejscach. Zamiast tego raz napisz funkcję sprawdz_haslo(haslo) i używaj jej wszędzie.
5/40
Zasada YAGNI

You Ain't Gonna Need It

Nie implementuj funkcjonalności, dopóki nie są one faktycznie potrzebne.

  • Przewidywanie przyszłości często się nie sprawdza.
  • Nadmiarowy kod to nadmiarowe błędy do naprawienia.
  • Skup się na dostarczeniu tego, co jest wymagane "tu i teraz".
Dlaczego nie warto przewidywać?
Często myślisz "kiedyś może przydać się możliwość eksportu do PDF", więc piszesz tę funkcję od razu. Po 3 miesiącach okazuje się, że nikt tego nie potrzebuje, ale Twój kod jest większy, trudniejszy do zrozumienia i zawiera błędy, których nikt nie zgłosił bo funkcja nie jest używana. Lepiej najpierw zrobić to, co jest potrzebne, a "dodatki" dodawać gdy pojawi się realna potrzeba.
Ilustracja: Przeładowany szwajcarski scyzoryk vs konkretne narzędzia
6/40
SOLID: Fundamenty OOD

Pięć zasad czystego kodu

SOLID to akronim pięciu zasad, które pomagają tworzyć elastyczne systemy. Dla początkujących najważniejsza jest pierwsza z nich.

  1. S: Single Responsibility (Jedna odpowiedzialność)
  2. O: Open/Closed (Otwarte-Zamknięte)
  3. L: Liskov Substitution (Zastępowalność Liskov)
  4. I: Interface Segregation (Segregacja interfejsów)
  5. D: Dependency Inversion (Inwersja zależności)

Przyjrzyjmy się im bliżej w uproszczony sposób.

Co to jest akronim?
Akronim to skrót utworzony z pierwszych liter wyrazów. SOLID pochodzi od angielskich nazw zasad: Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation, Dependency Inversion. Te zasady zostały sformułowane przez Roberta Martina (wujka Boba) i są fundamentalnymi regułami dobrego projektowania obiektowego.
7/40
SRP: Jedna odpowiedzialność

Klasa powinna mieć tylko jeden powód do zmiany

Złe podejście: Klasa User, która przechowuje dane użytkownika, wysyła e-maile i zapisuje się do bazy danych.

Dobre podejście:

  • User – tylko dane.
  • EmailService – wysyłka poczty.
  • UserRepository – obsługa bazy danych.
Rozbijaj duże klasy na mniejsze, wyspecjalizowane moduły.
Wyjaśnienie "powód do zmiany":
Wyobraź sobie klasę User, która robi wszystko. Gdy zmienisz system mailingowy (np. z Gmaila na inny serwis), musisz zmienić klasę User. Gdy zmienisz bazę danych (np. z MySQL na PostgreSQL), znowu musisz zmienić klasę User. To oznacza, że klasa ma wiele powodów do zmiany. Zgodnie z SRP powinieneś podzielić te odpowiedzialności.
8/40
OCP: Otwarte-Zamknięte

Otwarte na rozbudowę, zamknięte na modyfikacje

Powinieneś móc dodawać nową funkcjonalność bez zmieniania istniejącego, przetestowanego kodu.

Przykład: Zamiast instrukcji if typ == "kot": ... wewnątrz funkcji, stwórz wspólną klasę bazową Zwierze i pozwól nowym gatunkom nadpisywać ich zachowania.

Jak to działa w praktyce?
Wyobraź sobie funkcję, która rysuje kształty. Zamiast pisać: "jeśli to koło, narysuj koło; jeśli kwadrat, narysuj kwadrat", tworzysz klasę bazową Ksztalt z metodą rysuj(). Potem tworzysz klasy Kolo i Kwadrat, które dziedziczą po Ksztalt i same implementują metodę rysuj(). Gdy dodasz trójkąt, nie musisz zmieniać istniejącego kodu - po prostu dodajesz nową klasę.
Schemat: Dodawanie modułów bez rozbierania bazy
9/40
LSP: Zastępowalność Liskov

Podklasa musi zachowywać się jak klasa bazowa

Jeśli funkcja oczekuje obiektu Ptak, musi działać poprawnie po przekazaniu jej obiektu Wrobel.

Uwaga na pułapkę: Strus jest ptakiem, ale nie lata. Jeśli klasa Ptak ma metodę lec(), to Strus nie powinien z niej dziedziczyć wprost, bo złamie logikę programu.

Wyjaśnienie zasady Liskov:
Zasada mówi, że obiekty klasy potomnej muszą być używane w każdym miejscu, gdzie używamy klasy bazowej, bez naruszenia poprawności programu. Problem ze struścem: jeśli funkcja wywołuje ptak.lec() na obiekcie Strus, to albo dostaniesz błąd (bo struś nie umie latać), albo będziesz musiał jakoś to obejść (np. rzucić wyjątek). To łamie zasadę zastępowalności. Rozwiązanie: nie każdy ptak musi umieć latać - lepiej wydzielić metodę lec() do osobnej klasy lub interfejsu.
10/40
ISP: Segregacja interfejsów

Lepiej mieć wiele małych interfejsów niż jeden duży

Nie zmuszaj klas do implementowania metod, których nie potrzebują.

Przykład: Zamiast jednego interfejsu Pracownik z metodami programuj() i sprzataj(), stwórz dwa oddzielne. Programista nie musi wiedzieć, jak profesjonalnie myć podłogi.

Co to jest interfejs?
W Pythonie interfejsy nie są tak formalne jak w Javie czy C++, ale idea jest ta sama: interfejs to zbiór metod, które klasa musi zaimplementować. Zasada ISP mówi, żeby nie robić "wielkich" interfejsów z wieloma metodami, z których większość nie jest potrzebna. Lepiej mieć kilka małych, wyspecjalizowanych interfejsów.
11/40
DIP: Inwersja zależności

Zależ od abstrakcji, nie od konkretu

Wysokopoziomowe moduły nie powinny zależeć od niskopoziomowych.

Złe: Laptop bezpośrednio tworzy obiekt DyskSSD.

Dobre: Laptop wymaga dowolnego obiektu, który spełnia wymagania PrzechowywanieDanych. Dzięki temu łatwo podmienisz SSD na dysk w chmurze bez zmiany kodu laptopa.

Co to jest "wysokopoziomowy" i "niskopoziomowy"?
Wysokopoziomowy moduł to taki, który realizuje główne zadania programu (np. Laptop). Niskopoziomowy to taki, który realizuje szczegóły techniczne (np. DyskSSD). Zasada DIP mówi, że Laptop nie powinien "znać" konkretnego dysku - powinien tylko wiedzieć, że może zapisać i odczytać dane. Dzięki temu możesz zmienić dysk na inny (np. HDD, dysk w chmurze) bez zmiany kodu Laptop.
Ilustracja: Gniazdko elektryczne jako abstrakcja dla różnych urządzeń
12/40
SOLID w Pythonie - Podsumowanie

Praktyczna rada

Jako początkujący, nie staraj się stosować wszystkich zasad SOLID na siłę od pierwszego dnia. To przychodzi z doświadczeniem.

Zacznij od SRP (Jedna klasa = Jedno zadanie). To najprostsza droga do czystszego kodu.

# SRP - Dobry przykład
class Logger:
    def log(self, message):
        print(f"LOG: {message}")

class Calculator:
    def add(self, a, b):
        return a + b
Przykład z kodu:
W przykładzie widzisz dwie klasy: Logger (który tylko zapisuje logi) i Calculator (który tylko liczy). Każda robi jedną rzecz. Gdybyś połączył je w jedną klasę, musiałbyś ją zmieniać za każdym razem, gdy zmienisz sposób logowania lub gdy dodasz nowe działania matematyczne. Rozdzielenie ich to właśnie stosowanie SRP.
13/40
Relacje między obiektami

Jak obiekty ze sobą "rozmawiają"?

W systemie obiekty rzadko działają w izolacji. Musimy określić, w jaki sposób są ze sobą połączone.

Główne typy relacji:

  • Dziedziczenie: Relacja typu "jest" (Pies jest zwierzęciem).
  • Kompozycja/Agregacja: Relacja typu "ma" (Samochód ma silnik).
  • Asocjacja: Zwykłe używanie (Lekarz leczy pacjenta).
Jak odróżnić te relacje?
Pytaj siebie: "Czy X jest rodzajem Y?" - to dziedziczenie (Pies jest Zwierzęciem). "Czy X ma w sobie Y?" - to kompozycja/agregacja (Samochód ma Silnik). "Czy X używa Y do czegoś?" - to asocjacja (Lekarz używa Skalpela, ale go nie "ma").
14/40
Kompozycja vs Agregacja

Własność a współpraca

Oba terminy oznaczają, że jeden obiekt ma w sobie drugi, ale różnią się silą związku.

  • Kompozycja (silna): Obiekt podrzędny nie może istnieć bez nadrzędnego. Jeśli zniszczysz Budynek, zniszczysz też jego Pokoje.
  • Agregacja (słaba): Obiekty mogą istnieć niezależnie. Jeśli rozwiążesz Zespol, jego Czlonkowie nadal istnieją i mogą dołączyć do innego zespołu.
Praktyczna różnica:
W kodzie kompozycja wygląda tak: "samochód.tworz_silnik()" - silnik powstaje wraz z samochodem i umiera z nim. Agregacja wygląda tak: "do_klubu.dodaj(pilkarz)" - pilkarz istniał przed dołączeniem i będzie istniał po opuszczeniu klubu.
Schemat: Pokoje w domu vs Studenci na uczelni
15/40
Przykład Kompozycji w Python

Silnik w samochodzie

class Silnik:
    def start(self): print("Wrrrum!")

class Samochod:
    def __init__(self):
        self.silnik = Silnik() # Kompozycja

    def jedz(self):
        self.silnik.start()
        print("Jedziemy!")

auto = Samochod()
auto.jedz()

Zauważ: Silnik jest tworzony wewnątrz Samochodu.

Jak to działa w kodzie:
W metodzie __init__ klasy Samochod tworzymy nowy obiekt Silnik(). Od tego momentu Samochód "posiada" ten konkretny silnik. Jeśli usuniemy obiekt Samochod, automatycznie zniknie też jego silnik (chyba że ktoś inny też go przechowuje). To jest kompozycja - silnik jest nieodłączną częścią samochodu.
16/40
Przykład Agregacji w Python

Piłkarz w klubie

class Pilkarz:
    def __init__(self, nazwisko):
        self.nazwisko = nazwisko

class Klub:
    def __init__(self, nazwa):
        self.nazwa = nazwa
        self.zawodnicy = []

    def dodaj_zawodnika(self, p):
        self.zawodnicy.append(p)

lewy = Pilkarz("Lewandowski")
fcb = Klub("FC Barcelona")
fcb.dodaj_zawodnika(lewy) # Agregacja
Kluczowa różnica:
W tym przypadku najpierw tworzysz obiekt Pilkarz ("Lewandowski" istnieje jako osoba), a potem dodajesz go do listy w klubie. Ale piłkarz nie przestaje istnieć gdy klub przestanie istnieć - może przejść do innego klubu. Dlatego to agregacja, a nie kompozycja.
17/40
Preferuj kompozycję nad dziedziczeniem

Złota zasada OOD

Dziedziczenie jest bardzo silnym związkiem i często prowadzi do problemów przy zmianach. Kompozycja jest bardziej elastyczna.

Zamiast tworzyć PracownikProgramista przez dziedziczenie z Pracownik i Programista, lepiej stworzyć obiekt Pracownik, który ma pole rola przechowujące obiekt typu Programista.

Dlaczego kompozycja jest lepsza?
Wyobraź sobie, że PracownikProgramista dziedziczy po Pracownik i Programista. A co jeśli ten sam człowiek jest jednocześnie Programistą i Menadżerem? W wielokrotnym dziedziczeniu łatwo o konflikt. Kompozycja pozwala na łatwe zmiany: pracownik.rola = Menadzer() - i już mamy inną rolę. To jak zmiana ubrania - nie zmieniasz siebie, tylko to co na sobie masz.
18/40
Asocjacja (Powiązanie)

Luźna współpraca

Relacja typu: "Obiekt A używa obiektu B".

Przykład: Klient składa Zamowienie. Klient nie "ma w sobie" zamówienia jako stałego elementu swojej budowy, a zamówienie nie jest klientem. Po prostu w pewnym momencie dochodzi do interakcji.

Asocjacja vs Kompozycja:
Różnica: w kompozycji obiekt jest częścią drugiego (Samochód ma Silnik). W asocjacji obiekty wchodzą w interakcję, ale nie są częściami siebie nawzajem. Klient może złożyć milion zamówień, ale nie "zawiera" ich w sobie jak samochód zawiera silnik.
19/40
Wizualizacja projektu: UML

Unified Modeling Language

Zanim zaczniesz pisać kod, warto narysować schemat klas. UML to standard rysowania takich "map drogowych" programu.

Klasa w UML: Prostokąt podzielony na trzy części:

  1. Nazwa klasy.
  2. Atrybuty (pola).
  3. Metody.
Co to jest UML?
UML (Unified Modeling Language) to zestandaryzowany język do wizualizacji projektowania oprogramowania. Nie musisz znać wszystkich jego symboli - wystarczy umieć narysować prosty diagram klas. To jak mapa przed podróżą - pomaga zobaczyć całość zanim zaczniesz kodzić.
Przykład prostego diagramu klasy UML
20/40
Znaki w diagramach klas

Oznaczenia widoczności

W UML używamy symboli, by określić, kto ma dostęp do pól i metod:

  • + (plus): Publiczne (dostępne dla wszystkich).
  • - (minus): Prywatne (tylko wewnątrz klasy).
  • # (hasz): Chronione (dla klasy i potomków).

Pamiętaj, że w Pythonie to tylko konwencja (np. _atrybut), ale na diagramie warto to zaznaczyć.

Widoczność w Pythonie:
Python nie ma formalnych modyfikatorów dostępu jak Java (public, private, protected). Zamiast tego stosujemy konwencje: atrybut = publiczny, _atrybut = "chroniony" (nie używaj poza klasą i podklasami), __atrybut = "prywatny" (mocniej ukryty przez name mangling). Te oznaczenia w UML pomagają innym programistom wiedzieć, które elementy są do użytku wewnętrznego.
21/40
Symbole relacji w UML

Jak rysować połączenia?

  • Dziedziczenie: Linia ciągła z pustym trójkątem.
  • Kompozycja: Linia z zamalowanym rombem po stronie "właściciela".
  • Agregacja: Linia z pustym rombem.
  • Asocjacja: Zwykła linia, ew. ze strzałką kierunku.
Jak zapamiętać różnicę między kompozycją a agregacją?
Zamalowany (pełny) romb = silniejsza więź = kompozycja (część nie istnieje bez całości). Pusty (otwarty) romb = słabsza więź = agregacja (część może istnieć bez całości). Narysuj sobie: Budynek z pełnym rombem przy "pokoje", a Uczelnia z pustym rombem przy "studenci".
Zestawienie ikon relacji UML
22/40
Narzędzia do projektowania

Gdzie rysować diagramy?

Nie musisz instalować drogiego oprogramowania:

  • draw.io (diagrams.net): Darmowe, online, proste.
  • PlantUML / Mermaid: Piszesz tekst, a generator robi z tego obrazek.
  • Kartka i ołówek: Często najszybszy sposób na pierwszy szkic.
Którego narzędzia użyć?
draw.io jest najprostsze - wchodzisz na stronę, rysujesz myszką, zapisujesz jako obrazek. PlantUML i Mermaid to dla lubiących pisać kod zamiast rysować - opisujesz diagram tekstem, a program generuje obrazek. Na początek kartka i ołówek są najlepsze - szybko zrobisz szkic, zanim zapomnisz o co chodziło.
23/40
Modelowanie domeny

Zrozum problem, zanim zaproponujesz rozwiązanie

Projektowanie zaczyna się od rozmowy z "klientem" lub analizy tekstu zadania. Szukamy:

  • Rzeczowników: To zazwyczaj kandydaci na klasy (Student, Książka, Kurs).
  • Czasowników: To zazwyczaj kandydaci na metody (zapisz_na_kurs, oddaj_ksiazke).
Przykład analizy tekstu:
Weźmy zadanie: "System ma pozwalać studentom na wypożyczanie książek z biblioteki. Studenci mogą mieć wypożyczone maksymalnie 3 książki." Rzeczowniki: Student, Książka, Biblioteka (klasy).
Czasowniki: wypożycz, oddaj (metody).
Liczby i ograniczenia: "maksymalnie 3" to reguła biznesowa do implementacji.
24/40
Wzorce projektowe (Design Patterns)

Gotowe rozwiązania częstych problemów

Programiści przed Tobą rozwiązali już tysiące podobnych problemów. Wzorce projektowe to sprawdzone przepisy na architekturę.

Dzielimy je na:

  • Kreacyjne: Jak mądrze tworzyć obiekty (np. Singleton, Fabryka).
  • Strukturalne: Jak składać obiekty w większe całości (np. Dekorator, Adapter).
  • Behawioralne: Jak obiekty mają się komunikować (np. Obserwator, Strategia).
Czy musisz znać wszystkie wzorce?
Nie! Na początku wystarczy, że wiesz, że istnieją. Wzorce to narzędzia - używasz ich gdy pasują do problemu, nie na siłę. Najpopularniejsze to: Singleton (jedna instancja klasy), Fabryka (tworzenie obiektów zależnie od typu), Strategia (wymiana algorytmów), Obserwator (powiadomienia o zmianach).
25/40
Proces budowy programu

Krok po kroku

  1. Analiza: Co program ma robić?
  2. Projekt: Jakie klasy i jak połączone? (UML)
  3. Implementacja: Pisanie kodu (Python).
  4. Testowanie: Czy działa zgodnie z założeniami?
  5. Refaktoryzacja: Poprawianie struktury kodu bez zmiany jego działania.
Czy zawsze trzeba przejść przez wszystkie kroki?
W projektach zaliczeniowych na uczelni często wystarczy analiza -> projekt -> implementacja. Ale w pracy zawodowej testowanie i refaktoryzacja są kluczowe. Pamiętaj: wbrew pozorom najwięcej czasu zajmuje analiza i projekt (nawet 40-50% całego projektu). Wielu początkujących programistów pomija te kroki i potem przepisuje kod kilka razy.
Cykl życia oprogramowania
26/40
Refaktoryzacja - Higiena kodu

Sprzątanie po sobie

Pierwsza wersja kodu rzadko jest idealna. Refaktoryzacja to proces ulepszania napisanego już kodu.

  • Zmienianie nazw zmiennych na czytelniejsze.
  • Wyciąganie powtarzających się fragmentów do metod.
  • Upraszczanie skomplikowanych pętli i warunków.
Refaktoryzację robimy tylko wtedy, gdy mamy pewność, że kod działa (mamy testy!).
Przykład refaktoryzacji:
Było:
def policz(a, b, c): return a*2 + b*2 + c*2
Po refaktoryzacji:
def pomnoz_przez_dwa(x): return x * 2
def policz(a, b, c): return pomnoz_przez_dwa(a) + pomnoz_przez_dwa(b) + pomnoz_przez_dwa(c)
Kod robi to samo, ale jest czytelniejszy i łatwiej go zmienić (np. gdybyśmy chcieli mnożyć przez 3).
27/40
Dokumentacja kodu

Pomóż innym zrozumieć Twój kod

Używaj docstringów (potrójne cudzysłowy) pod definicją klasy i metody.

class Robot:
    """Klasa reprezentująca robota sprzątającego."""
    
    def sprzataj(self):
        """Uruchamia sekwencję sprzątania pokoju."""
        print("Sprzątam...")
Co to jest docstring?
Docstring to wieloliniowy komentarz wewnątrz potrójnych cudzysłowów ("""), umieszczany bezpośrednio pod definicją funkcji, metody lub klasy. Służy do automatycznego generowania dokumentacji (np. przez narzędzie pydoc). Dzięki temu pisząc w konsoli help(Robot) zobaczysz opis klasy.
28/40
Modularność: Pakiety i Moduły

Podział na pliki

Nie trzymaj wszystkiego w jednym pliku main.py. Podziel klasy tematycznie.

  • models.py – definicje klas.
  • utils.py – funkcje pomocnicze.
  • gui.py / cli.py – interfejs użytkownika.
Jak to działa w Pythonie?
Każdy plik .py to moduł. Możesz importować klasy z innego pliku pisząc: from models import Samochod. Tworząc folder z plikiem __init__.py tworzysz pakiet. Dzięki temuTwój kod jest zorganizowany i łatwiej go utrzymywać - gdy chcesz zmienić wygląd interfejsu, edytujesz gui.py, nie szukając w morzu kodu w main.py.
29/40
Podsumowanie teorii projektowania

Trzy filary sukcesu

  1. Zasady (SOLID): Wyznaczają kierunek.
  2. Relacje (Kompozycja): Budują strukturę.
  3. Wzorce: Dostarczają gotowe plany.

Praktyka czyni mistrza – najlepiej nauczysz się projektować, realizując projekt zaliczeniowy.

Jak to zapamiętać?
SOLID to jak zasady ruchu drogownego - musisz je znać. Relacje to jak budowanie z klocków - musisz wiedzieć co z czego się składa. Wzorce to jak przepisy kulinarne - gotowe rozwiązania na typowe sytuacje. Razem dają Ci solidny fundament do tworzenia dobrych programów.
30/40
Przerwa na pytania

Czy wszystko jasne?

Zanim przejdziemy do szczegółów projektu zaliczeniowego, zastanów się, czy rozumiesz różnicę między kompozycją a dziedziczeniem oraz dlaczego SRP jest tak ważne.

Sprawdź siebie:
Czy potrafisz wytłumaczyć własnymi słowami:
1. Czym kompozycja różni się od agregacji?
2. Dlaczego jedna klasa powinna mieć jeden powód do zmiany?
3. Jak narysowałbyś w UML związek między Laptopem a DyskSSD?
Jeśli tak - jesteś gotowy do projektu zaliczeniowego!
Grafika z pytajnikiem
31/40
Projekt Zaliczeniowy: Cel

Zastosowanie wiedzy w praktyce

Celem projektu jest stworzenie kompletnej aplikacji obiektowej w Pythonie, która rozwiązuje konkretny problem.

Czego będę szukał w Waszym kodzie?

  • Poprawnego użycia klas i obiektów.
  • Zastosowania dziedziczenia i polimorfizmu tam, gdzie ma to sens.
  • Czystego kodu (zgodnego z PEP 8).
  • Obsługi wyjątków i walidacji danych.
  • Zapisywania stanu do pliku (serializacja).
Co to jest serializacja?
Serializacja to zamiana obiektu Python na format, który można zapisać do pliku lub przesłać przez sieć. Najpopularniejsze formaty: JSON (tekstowy, czytelny dla ludzi), Pickle (binarny, tylko dla Pythona). Dzięki temu po zamknięciu programu dane nie znikają - możesz je wczytać przy ponownym uruchomieniu.
32/40
Projekt: Wymagania techniczne

Co musi się znaleźć w kodzie?

  1. Minimum 3-4 współpracujące ze sobą klasy.
  2. Użycie dziedziczenia (przynajmniej jeden poziom).
  3. Wykorzystanie metod magicznych (np. __str__).
  4. Obsługa błędów (bloki try...except).
  5. Trwałość danych: JSON, Pickle lub plik CSV.
  6. Prosty interfejs tekstowy (menu w konsoli).
Wyjaśnienie poszczególnych wymagań:
- 3-4 klasy: np. Ksiazka, Uzytkownik, Biblioteka, Wypozyczenie
- Dziedziczenie: np. klasa Bazowa "Zwierze", a od niej "Pies" i "Kot"
- Metody magiczne: __str__ (do print), __init__ (konstruktor), __eq__ (porównanie)
- try...except: obsługa błędów, np. gdy użytkownik wpisze literę zamiast liczby
- Trwałość: zapis do pliku, żeby dane nie znikały po zamknięciu programu
- Menu w konsoli: proste wybieranie opcji z klawiatury (1-2-3-...)
33/40
Struktura projektu

Jak spakować zadanie?

Projekt powinien składać się z:

  • Plików .py z kodem źródłowym.
  • Krótkiej dokumentacji (plik README.md lub PDF) opisującej:
    • Co program robi?
    • Diagram klas (obrazek lub opis).
    • Instrukcja uruchomienia.
Co to jest README?
README.md to plik tekstowy (w formacie Markdown), który jest "wizytówką" projektu. Powinien zawierać: tytuł projektu, krótki opis co program robi, jak go uruchomić, jakie są wymagania (np. Python 3.x). To pierwsze co ludzie widzą gdy otwierają Twój projekt na GitHubie.
34/40
Pomysł na projekt: System Zarządzania Biblioteką

Inspiracja 1

Klasy: Ksiazka, Uzytkownik, Biblioteka.

Funkcje: Wypożyczanie, oddawanie, wyszukiwanie po tytule, zapisywanie bazy czytelników do JSON.

Dziedziczenie: KsiazkaPapierowa i Ebook jako potomkowie Pozycja.

Jak to rozwinąć?
Możesz dodać: klasę Pracownik (do zarządzania), Kategoria (dla książek), Kary (za przetrzymanie). Dziedziczenie: Pozycja może mieć wspólne cechy (tytuł, autor, rok), a KsiazkaPapierowa i Ebook różnią się (np. Ebook ma rozmiar pliku, Ksiazka - liczbę stron).
35/40
Pomysł na projekt: Symulator RPG

Inspiracja 2

Klasy: Bohater, Bron, Przeciwnik, Walka.

Funkcje: Atakowanie, zdobywanie doświadczenia, ekwipowanie przedmiotów.

Dziedziczenie: Klasy postaci (Mag, Wojownik) dziedziczące po Bohater.

Jak to rozwinąć?
Bohater może mieć atrybuty (HP, siła, zręczność). Bron może mieć typy (miecz, łuk, różdżka) z różnymi statystykami. Przeciwnik - różne potwory z różnymi poziomami trudności. Walka - mechanika turowa: tura gracza, tura przeciwnika. Możesz zapisać stan gry do pliku i kontynuować później.
36/40
Pomysł na projekt: System Finansów Osobistych

Inspiracja 3

Klasy: Transakcja, Kategoria, Budzet.

Funkcje: Dodawanie wydatków/przychodów, generowanie raportów miesięcznych, eksport do CSV.

Jak to rozwinąć?
Transakcja ma: kwotę, datę, opis, kategorię. Kategoria: jedzenie, transport, rozrywka. Budzet: ile możesz wydać w danej kategorii. Program może ostrzegać gdy przekroczysz limit. CSV to plik tekstowy z danymi oddzielonymi przecinkami - możesz go otworzyć w Excelu.
37/40
Kryteria oceny

Za co można dostać 5?

  • Poprawność: Wszystko działa "od strzału".
  • Architektura: Logiczny podział na klasy.
  • Wyjątki: Program nie "wywala się" po wpisaniu litery zamiast cyfry.
  • Kreatywność: Własny, ciekawy pomysł wykraczający poza standardowe przykłady.
  • Estetyka kodu: Czytelne nazwy, docstringi, brak nieużywanego kodu.
Co oznacza "od strzału"?
Program uruchamiasz i działa bez błędów - nie musisz niczego naprawiać. "Wyjątki" oznaczają obsługę błędów: gdy użytkownik wpisze "abc" zamiast liczby, program powinien wyświetlić komunikat "Podaj liczbę" zamiast wyrzucić błąd i zamknąć się.
38/40
Najczęstsze błędy

Czego unikać?

  • Klasa-Bóg: Jeden obiekt, który robi absolutnie wszystko.
  • Globalne zmienne: Używaj atrybutów klasy.
  • Brak walidacji: Przyjmowanie każdych danych od użytkownika bez sprawdzenia.
  • Ignorowanie PEP 8: Niechlujne formatowanie (brak wcięć, dziwne nazwy).
Wyjaśnienie pojęć:
- Klasa-Bóg: klasa z tysiącami linii, wszystko w niej - to łamie SRP.
- Globalne zmienne: zmienne dostępne wszędzie w programie - to zła praktyka, lepiej przekazywać dane jako argumenty.
- Walidacja: sprawdzanie czy dane są poprawne (np. czy wiek > 0).
- PEP 8: oficjalny przewodnik stylu w Pythonie - mówi m.in. że nazwy zmiennych piszemy małymi_literami, klasy WielkimiLiterami.
39/40
Harmonogram

Kiedy oddać projekt?

Szczegółowe terminy zostaną podane na platformie e-learningowej.

Rada: Nie zostawiajcie pisania na ostatnią noc. Projektowanie obiektowe wymaga czasu na przemyślenie struktury. Najpierw narysuj, potem koduj!

Plan działania:
Dzień 1-2: Wybierz temat i narysuj diagram klas na kartce.
Dzień 3-4: Stwórz podstawowe klasy i uruchom program.
Dzień 5-6: Dodaj dziedziczenie i metody.
Dzień 7: Dodaj obsługę błędów i zapis do pliku.
Dzień 8: Testuj i popraw błędy.
Dzień 9: Napisz dokumentację i README.
Tak rozłożony projekt jest mniej stresujący i daje czas na poprawki.
40/40
Powodzenia!

Koniec kursu

Gratulacje! Przeszliśmy razem przez podstawy i zaawansowane aspekty programowania obiektowego w Pythonie.

Pamiętajcie, że programowanie to rzemiosło. Kluczem do sukcesu jest pisanie jak największej ilości kodu i ciągłe szukanie sposobów na jego ulepszenie.

Do zobaczenia na prezentacji projektów!

Co dalej?
Po tym kursie warto uczyć się dalej: testy automatyczne (unittest/pytest), bazy danych (SQL), frameworki webowe (Django/Flask). Ale najważniejsze to praktyka - im więcej piszesz, tym lepszy się stajesz. Powodzenia w projektach!
Gratulacje!