Skip to content

Commit 28de4e3

Browse files
committed
Final version
1 parent c17e84a commit 28de4e3

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

79 files changed

+6383
-623
lines changed

.github/workflows/labs.yml

Lines changed: 227 additions & 110 deletions
Large diffs are not rendered by default.

0_intro/01_hello/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# 👋 Intro - Prosty Kalkulator
22

3-
**Poziom**: bardzo łatwy
3+
**Poziom**: N/A
44
**Cel**: Weryfikacja środowiska
55

66
## 🎯 Zadanie

0_intro/01_hello/solution.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
"""
2+
>>> add(2, 3)
3+
5
4+
5+
>>> add(10, 5)
6+
15
7+
8+
>>> multiply(2, 3)
9+
6
10+
11+
>>> multiply(10, 5)
12+
50
13+
14+
>>> add(0, 0)
15+
0
16+
17+
>>> multiply(1, 1)
18+
1
19+
"""
20+
21+
# %% About
22+
# - Name: Intro - Simple Calculator
23+
# - Difficulty: easy
24+
# - Lines: 4
25+
# - Focus: Setup verification
26+
27+
# %% Description
28+
"""
29+
Intro Exercise: Simple Calculator
30+
31+
Zaimplementuj dwie proste funkcje, żeby sprawdzić, że środowisko działa.
32+
"""
33+
34+
# %% Hints
35+
# - Funkcja add() powinna zwrócić sumę dwóch liczb
36+
# - Funkcja multiply() powinna zwrócić iloczyn dwóch liczb
37+
38+
# %% Run
39+
# - Terminal: `python -m doctest -f -v starter.py`
40+
# - Tests: `python -m pytest test_hello.py -v`
41+
42+
43+
def add(a, b):
44+
"""
45+
Add two numbers.
46+
47+
Args:
48+
a: First number
49+
b: Second number
50+
51+
Returns:
52+
Sum of a and b
53+
"""
54+
return a + b
55+
56+
57+
def multiply(a, b):
58+
"""
59+
Multiply two numbers.
60+
61+
Args:
62+
a: First number
63+
b: Second number
64+
65+
Returns:
66+
Product of a and b
67+
"""
68+
return a * b

1_principles/01_grasp/04_low_coupling/README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# 🔗 GRASP Low Coupling - System Gry
22

3-
**Poziom**: łatwy
3+
**Poziom**: Łatwy
44
**Cel**: GRASP Low Coupling
55

66
## 🎯 Zadanie
@@ -44,12 +44,12 @@ class Game:
4444
**Dobrze** (luźne sprzężenie):
4545
```python
4646
class Game:
47-
def __init__(self, score_service): # Pośrednik
47+
def __init__(self, score_service): # 1. wstrzykujemy pośrednika
4848
self.score_service = score_service
4949

5050
def finish_game(self, player, score):
5151
self.score_service.save_score(player, score)
52-
# Game nie zna Database - luźne sprzężenie
52+
# 2. Game nie zna Database - luźne sprzężenie
5353

5454
# ScoreService izoluje Game od Database
5555
```
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
"""
2+
GRASP Low Coupling - Game System
3+
4+
>>> # Test ScoreService
5+
>>> service = ScoreService()
6+
>>> service.save_score("player1", 100)
7+
'Saved score 100 for player1'
8+
9+
>>> # Test Game with ScoreService (low coupling)
10+
>>> game = Game(service)
11+
>>> game.finish_game("Alice", 150)
12+
'Game finished. Saved score 150 for Alice'
13+
"""
14+
15+
16+
class Database:
17+
"""Konkretna implementacja bazy danych"""
18+
19+
def connect(self) -> str:
20+
return "Connected to database"
21+
22+
def save(self, player: str, score: int) -> str:
23+
return f"Saved score {score} for {player}"
24+
25+
26+
# TODO: Zaimplementuj ScoreService
27+
# Klasa pośrednicząca między Game a bazą danych (redukuje sprzężenie)
28+
# Metoda save_score(player, score) zwraca: "Saved score {score} for {player}"
29+
30+
class ScoreService:
31+
def __init__(self):
32+
self.database = Database()
33+
34+
def save_score(self, player: str, score: int) -> str:
35+
"""Pośrednik - izoluje Game od Database"""
36+
self.database.connect()
37+
return self.database.save(player, score)
38+
39+
40+
# TODO: Zaimplementuj Game
41+
# Przyjmuje score_service: ScoreService w konstruktorze (dependency injection)
42+
# Metoda finish_game(player, score):
43+
# - Wywołuje score_service.save_score(player, score)
44+
# - Zwraca "Game finished. {wynik z save_score}"
45+
#
46+
# Low Coupling: Game nie zna Database, tylko ScoreService (pośrednik)
47+
48+
class Game:
49+
def __init__(self, score_service: ScoreService):
50+
"""Dependency Injection - redukuje sprzężenie"""
51+
self.score_service = score_service
52+
53+
def finish_game(self, player: str, score: int) -> str:
54+
"""Game używa tylko ScoreService, nie zna Database"""
55+
result = self.score_service.save_score(player, score)
56+
return f"Game finished. {result}"
57+
58+
59+
# GRASP Low Coupling:
60+
# Minimalizuj zależności między klasami
61+
#
62+
# Silne sprzężenie ❌:
63+
# Game → Database (bezpośrednia zależność)
64+
#
65+
# Luźne sprzężenie ✅:
66+
# Game → ScoreService → Database (pośrednik)
67+
#
68+
# Korzyść: Zmiana Database nie wpływa na Game
69+
70+
71+
# Przykład użycia
72+
if __name__ == "__main__":
73+
service = ScoreService()
74+
game = Game(service)
75+
print(game.finish_game("Alice", 150))

1_principles/01_grasp/04_low_coupling/starter.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,10 @@ class Game:
5353
# Game → ScoreService → Database (pośrednik)
5454
#
5555
# Korzyść: Zmiana Database nie wpływa na Game
56+
57+
58+
# Przykład użycia - odkomentuj gdy zaimplementujesz:
59+
# if __name__ == "__main__":
60+
# service = ScoreService()
61+
# game = Game(service)
62+
# print(game.finish_game("Alice", 150))

1_principles/01_grasp/04_low_coupling/violation.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ def finish_game(self, player: str, score: int) -> str:
6060
# Nie mogę - Game tworzy Database bezpośrednio w finish_game() (ew. patch)
6161
#
6262
# ❌ Chcę zmienić Database na inną implementację?
63-
# Muszę EDYTOWAĆ Game.finish_game()
63+
# Muszę EDYTOWAĆ Game().finish_game()
6464
#
6565
# ❌ Chcę dodać cache między Game a Database?
6666
# Muszę EDYTOWAĆ Game - nie ma miejsca na pośrednika
@@ -90,7 +90,7 @@ def finish_game(self, player: str, score: int) -> str:
9090
- Game odpowiada za komunikację z bazą
9191
- Dwie odpowiedzialności w jednej klasie
9292
93-
5. ❌ Wysokie sprzężenie = niska "współużywalność"
93+
5. ❌ Silne sprzężenie = niska "współużywalność"
9494
- Game nie może działać bez Database
9595
- Nie można użyć Game z innym storage (file, API)
9696
@@ -100,3 +100,9 @@ def finish_game(self, player: str, score: int) -> str:
100100
3. ScoreService izoluje Game od Database
101101
4. Game nie zna szczegółów Database - tylko wywołuje score_service.save_score()
102102
"""
103+
104+
105+
# Przykład użycia
106+
if __name__ == "__main__":
107+
game = Game()
108+
print(game.finish_game("Alice", 150))

1_principles/02_solid/02_ocp/README.md

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,17 +46,28 @@ class AreaCalculator:
4646

4747
**Dobrze** (rozszerzenie bez modyfikacji):
4848
```python
49-
from abc import ABC
49+
from abc import ABC, abstractmethod
5050

5151
class Shape(ABC):
52-
...
52+
@abstractmethod
53+
def calculate_area(self):
54+
pass
5355

5456
class Circle(Shape):
55-
...
57+
def __init__(self, radius):
58+
self.radius = radius
59+
60+
def calculate_area(self):
61+
return 3.14 * self.radius ** 2
5662

5763
# Nowy kształt = nowa klasa, zero zmian w AreaCalculator ✅
5864
class Rectangle(Shape):
59-
...
65+
def __init__(self, width, height):
66+
self.width = width
67+
self.height = height
68+
69+
def calculate_area(self):
70+
return self.width * self.height
6071
```
6172

6273
**Korzyść**: `AreaCalculator` nie zmienia się przy dodaniu Rectangle.
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
"""
2+
Open/Closed Principle - Shape Calculator
3+
4+
>>> circle = Circle(5)
5+
>>> circle.calculate_area()
6+
78.5
7+
8+
>>> square = Square(4)
9+
>>> square.calculate_area()
10+
16
11+
12+
>>> triangle = Triangle(3, 4)
13+
>>> triangle.calculate_area()
14+
6.0
15+
16+
>>> # Test AreaCalculator
17+
>>> shapes = [Circle(5), Square(4), Triangle(3, 4)]
18+
>>> calculator = AreaCalculator()
19+
>>> total = calculator.total_area(shapes)
20+
>>> total
21+
100.5
22+
"""
23+
24+
from abc import ABC, abstractmethod
25+
26+
27+
# TODO: Zaimplementuj interfejs Shape
28+
# Klasa abstrakcyjna z metodą calculate_area()
29+
30+
class Shape(ABC):
31+
@abstractmethod
32+
def calculate_area(self):
33+
"""Metoda abstrakcyjna - każdy kształt musi ją zaimplementować"""
34+
pass
35+
36+
37+
# TODO: Zaimplementuj Circle
38+
# Przyjmuje radius w konstruktorze
39+
# Dziedziczy po Shape
40+
# Pole = π * r² (użyj 3.14 dla π)
41+
42+
class Circle(Shape):
43+
def __init__(self, radius: float):
44+
self.radius = radius
45+
46+
def calculate_area(self) -> float:
47+
return 3.14 * self.radius ** 2
48+
49+
50+
# TODO: Zaimplementuj Square
51+
# Przyjmuje side w konstruktorze
52+
# Dziedziczy po Shape
53+
# Pole = side²
54+
55+
class Square(Shape):
56+
def __init__(self, side: float):
57+
self.side = side
58+
59+
def calculate_area(self) -> float:
60+
return self.side ** 2
61+
62+
63+
# TODO: Zaimplementuj Triangle
64+
# Przyjmuje base i height w konstruktorze
65+
# Dziedziczy po Shape
66+
# Pole = (base * height) / 2
67+
68+
class Triangle(Shape):
69+
def __init__(self, base: float, height: float):
70+
self.base = base
71+
self.height = height
72+
73+
def calculate_area(self) -> float:
74+
return (self.base * self.height) / 2
75+
76+
77+
# TODO: Zaimplementuj AreaCalculator
78+
# Metoda total_area(shapes) przyjmuje listę kształtów
79+
# i zwraca sumę ich pól używając polimorfizmu
80+
81+
class AreaCalculator:
82+
def total_area(self, shapes: list) -> float:
83+
"""Polimorfizm - każdy kształt wie jak obliczyć swoje pole"""
84+
return sum(shape.calculate_area() for shape in shapes)
85+
86+
87+
# OCP: Open for extension, Closed for modification
88+
# Nowy kształt = nowa klasa Shape, zero zmian w AreaCalculator
89+
90+
91+
# Przykład użycia
92+
if __name__ == "__main__":
93+
shapes = [Circle(5), Square(4), Triangle(3, 4)]
94+
calculator = AreaCalculator()
95+
print(f"Total area: {calculator.total_area(shapes)}")

1_principles/02_solid/02_ocp/starter.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,10 @@ class AreaCalculator:
6666

6767
# OCP: Open for extension, Closed for modification
6868
# Nowy kształt = nowa klasa Shape, zero zmian w AreaCalculator
69+
70+
71+
# Przykład użycia - odkomentuj gdy zaimplementujesz:
72+
# if __name__ == "__main__":
73+
# shapes = [Circle(5), Square(4), Triangle(3, 4)]
74+
# calculator = AreaCalculator()
75+
# print(f"Total area: {calculator.total_area(shapes)}")

0 commit comments

Comments
 (0)