Zajęcia 1#
Paradygmaty programowania#
W programowaniu wyróżnia się różne paradygmaty, czyli sposoby myślenia o strukturze programu. Trzy bardzo popularne to:
- programowanie strukturalne
- programowanie obiektowe (OOP)
- programowanie funkcyjne
Najłatwiej zrozumieć je przez porównanie sposobu organizowania kodu.
Programowanie strukturalne#
To najstarszy z tych trzech stylów. Program jest zorganizowany jako ciąg instrukcji i funkcji, które wykonują się krok po kroku.
Podstawowe elementy: zmienne, instrukcje (if, for, while), funkcje — brak obiektów.
Program wygląda jak przepis kulinarny: wykonuj instrukcje po kolei.
Przykład#
def calculate_area(width, height):
return width * height
w = 5
h = 3
area = calculate_area(w, h)
print(area)
Typowe języki#
C, Pascal, w dużej części Python.
Programowanie obiektowe (OOP)#
W OOP program organizuje się wokół obiektów, które reprezentują rzeczy ze świata rzeczywistego.
Obiekt: - ma dane (atrybuty) - ma zachowanie (metody)
Zamiast funkcji operujących na danych, mamy obiekty, które coś robią.
Przykład#
class Dog:
def __init__(self, name):
self.name = name
def bark(self):
print(self.name + " says woof!")
dog = Dog("Torvi")
dog.bark()
Tutaj: Dog → klasa, dog → obiekt, name → atrybut, bark() → metoda.
Cechy OOP#
- encapsulation – ukrywanie danych
- inheritance – dziedziczenie
- polymorphism – różne zachowanie tej samej metody
Typowe języki#
Java, C#, C++, Python, Ruby.
Programowanie funkcyjne#
Program składa się głównie z funkcji, które: - nie zmieniają stanu - zawsze dla tych samych danych dają ten sam wynik
Najważniejsza zasada — funkcje są "czyste" (pure functions):
i nic poza tym.
Przykład — map#
def add(a, b):
return a + b
numbers = [1, 2, 3, 4]
result = list(map(lambda x: x * 2, numbers))
print(result)
Przykład — filter#
Kluczowe idee#
- immutability (dane się nie zmieniają)
- functions as first-class objects
- map / filter / reduce
Typowe języki#
Haskell, Lisp, Clojure, Scala, częściowo Python i JavaScript.
Porównanie paradygmatów#
| Paradygmat | Myślenie o programie | Pytanie, które zadajesz |
|---|---|---|
| strukturalny | lista instrukcji | Jakie kroki mam wykonać? |
| obiektowy | współpracujące obiekty | Jakie obiekty istnieją i co potrafią robić? |
| funkcyjny | transformacje danych przez funkcje | Jak przepływają dane i jakimi funkcjami je przekształcam? |
Analogia — restauracja#
Strukturalne — szef kuchni ma listę kroków:
Obiektowe — masz obiekty: Chef, Oven, Pan, Dish, każdy coś robi:
Funkcyjne — masz transformacje danych:
Kiedy który styl ma sens?#
Strukturalne — proste skrypty, małe programy, nauka podstaw, zadania typu „wejście → przetworzenie → wynik". Najprostsze na start, ale przy dużym projekcie może się zrobić bałagan.
Obiektowe — większe aplikacje, systemy z wieloma bytami (User, Order, Product, Invoice), aplikacje webowe, kod rozwijany długo.
Funkcyjne — praca na danych, pipeline'y, analiza danych, logika biznesowa, którą chcesz mieć czystą i łatwą do testowania.
Przykład: Aplikacja do liczenia wartości zamówienia#
- mamy produkty (nazwa, cena, liczba sztuk)
- chcemy policzyć łączną wartość zamówienia
- chcemy wypisać podsumowanie
Podejście strukturalne#
Wszystko opiera się na danych w prostych strukturach, funkcjach i instrukcjach krok po kroku.
products = [
{"name": "Laptop", "price": 3000, "quantity": 1},
{"name": "Mouse", "price": 100, "quantity": 2},
{"name": "Keyboard", "price": 200, "quantity": 1},
]
def calculate_item_total(product):
return product["price"] * product["quantity"]
def calculate_order_total(products):
total = 0
for product in products:
total += calculate_item_total(product)
return total
def print_order_summary(products):
print("ORDER SUMMARY")
for product in products:
item_total = calculate_item_total(product)
print(f'{product["name"]}: {product["quantity"]} x {product["price"]} = {item_total}')
print(f"TOTAL: {calculate_order_total(products)}")
print_order_summary(products)
Myślenie: „weź dane i je przetwórz" — mam listę danych i funkcje, które coś z nimi robią.
Podejście obiektowe#
Dane i zachowanie łączymy w obiekty: Product i Order.
class Product:
def __init__(self, name, price, quantity):
self.name = name
self.price = price
self.quantity = quantity
def total_price(self):
return self.price * self.quantity
class Order:
def __init__(self):
self.products = []
def add_product(self, product):
self.products.append(product)
def total_amount(self):
return sum(product.total_price() for product in self.products)
def print_summary(self):
print("ORDER SUMMARY")
for product in self.products:
print(f"{product.name}: {product.quantity} x {product.price} = {product.total_price()}")
print(f"TOTAL: {self.total_amount()}")
order = Order()
order.add_product(Product("Laptop", 3000, 1))
order.add_product(Product("Mouse", 100, 2))
order.add_product(Product("Keyboard", 200, 1))
order.print_summary()
Myślenie: „stwórz rzeczy, które umieją coś robić" — produkt to obiekt, zamówienie to obiekt, każdy ma swoje dane i metody.
Podejście funkcyjne#
Skupiamy się na funkcjach i przekształcaniu danych — bez zmiany stanu.
products = [
{"name": "Laptop", "price": 3000, "quantity": 1},
{"name": "Mouse", "price": 100, "quantity": 2},
{"name": "Keyboard", "price": 200, "quantity": 1},
]
def item_total(product):
return product["price"] * product["quantity"]
def order_total(products):
return sum(map(item_total, products))
def order_lines(products):
return list(
map(
lambda p: f'{p["name"]}: {p["quantity"]} x {p["price"]} = {item_total(p)}',
products
)
)
def print_summary(products):
print("ORDER SUMMARY")
for line in order_lines(products):
print(line)
print(f"TOTAL: {order_total(products)}")
print_summary(products)
Myślenie: „przekształcaj dane przez funkcje" — dane wpływają do funkcji, funkcje zwracają wynik.
Python w praktyce — mix paradygmatów#
W praktyce większość języków jest mieszana. Python to doskonały przykład — w jednym pliku możesz mieć wszystkie trzy style:
- modele i klasy → obiektowo
- operacje na danych → funkcyjnie
- prosty flow programu → strukturalnie
Jako Pythonowiec często już używasz elementów funkcyjnych, nawet jeśli o tym nie myślisz: map, filter, sorted(key=...), list comprehensions, funkcje przekazywane jako argumenty.
Normalny kod produkcyjny to zazwyczaj mix — i to jest w porządku.
Praca projektowa#
Część 1 — Programowanie strukturalne (20 pkt)#
Temat: Biblioteka — wersja podstawowa
Napisz konsolową aplikację do obsługi biblioteki wykorzystując wyłącznie programowanie strukturalne (funkcje, pętle, instrukcje warunkowe, kolekcje — bez klas).
Dane przechowuj w strukturach takich jak listy, słowniki i listy słowników. Zdefiniuj na sztywno w kodzie co najmniej 5 książek (tytuł, autor, liczba sztuk) oraz 3 użytkowników z hasłami — wszyscy o roli „czytelnik".
Wymagania funkcjonalne:
- Logowanie — użytkownik podaje login i hasło; po 3 nieudanych próbach program się kończy.
- Menu główne z opcjami do wyboru (pętla aż do wybrania „Wyloguj").
- Przeglądanie katalogu — wyświetlenie wszystkich książek z autorem i liczbą dostępnych sztuk.
- Wypożyczenie książki — użytkownik podaje tytuł; jeśli są dostępne sztuki, liczba się zmniejsza, a książka trafia na listę wypożyczeń tego użytkownika. Jeśli brak sztuk — komunikat.
- Moje wypożyczenia — wyświetlenie listy książek aktualnie wypożyczonych przez zalogowanego użytkownika.
Wymagania techniczne:
- Każda operacja (logowanie, wyświetlanie katalogu, wypożyczanie itd.) musi być wyodrębniona jako osobna funkcja.
- Żadnych klas ani importów zewnętrznych bibliotek.
- Kod powinien być czytelny — sensowne nazwy zmiennych i funkcji, komentarze przy kluczowych fragmentach.
Punktacja (20 pkt):
| Element | Punkty |
|---|---|
| Poprawna struktura danych (książki, użytkownicy, wypożyczenia) | 3 |
| Logowanie z walidacją i limitem prób | 3 |
| Menu w pętli z obsługą błędnych wyborów | 2 |
| Przeglądanie katalogu z liczbą dostępnych sztuk | 3 |
| Wypożyczanie z aktualizacją stanu i obsługą braku sztuk | 4 |
| Wyświetlanie wypożyczeń zalogowanego użytkownika | 3 |
| Podział na funkcje i czytelność kodu | 2 |