@shadow_0771 bat um Feedback zu seinem Code im Rahmen des OOP-Studiums.

Na dann... #wir_braten_code!

RPG in der Konsole – das ist die Basis. Der Code erfüllt seine Aufgabe, ist aber so geschrieben, dass jeder Versuch, ihn zu skalieren, SCHMEEEEEEERZ verursacht. Lassen Sie uns die wichtigsten Probleme analysieren 👇

1️⃣ Strg+C, Strg+V Vererbung
Schauen wir uns die Hierarchie der Rüstungen an:
class Armor(Item):
def __init__(self, name, category, strength=None, value_strength=None...): # und noch 100500 Argumente
super().__init__(name, category)
# ...

class Helmet(Armor):
def __init__(self, ...):
super().__init__(...)

class Chestplate(Armor):
# Kopie von Helmet

class Greaves(Armor):
# Kopie von Chestplate

Alle diese Klassen (Helmet, Chestplate, Greaves, Boots) sind absolut identisch. Sie fügen weder neue Attribute noch neues Verhalten hinzu. Sie tun nichts außer super().__init__ aufzurufen.

OOP wurde nicht geschaffen, um jedes physische Objekt in der Welt mit einer eigenen Klasse zu beschreiben. Wenn sich Entitäten nur durch den Namen der Kategorie unterscheiden, sollte es eine einzige Klasse Armor geben, die ein Attribut slot_type hat (idealerweise über Enum).

2️⃣ Frankenstein-Konstruktor
Sehen Sie, wie ein Gegenstand erstellt wird:
crown = Helmet('Шлем Господства', 'Шлем', 'Сила', 5, 'Ловкость', 7, 'Интеллект', 3)

Härten Sie niemals die Namen von Stats in der Methodensignatur fest. Verwenden Sie Wörterbücher.
Statt dieser langen Parameterliste sollte ein Gegenstand stats={'strength': 5, 'agility': 7, 'intellect': 3} akzeptieren.

3️⃣ Klassen-Inzest
Die Klasse Characteristic nimmt hero entgegen und macht dann Folgendes:
for item in self._hero.slots_equipment.values():
if item:
if hasattr(item, 'value_strength') and item.value_strength:
self.attributes['strength'] += item.value_strength

Das nennt man "Tight Coupling" (starke Kopplung). Die Klasse der Eigenschaften greift mit schmutzigen Händen in das Inventar des Helden, prüft, ob dort Gegenstände sind, und versucht dann über hasattr (was an sich in 99% der Fälle ein Workaround ist), die Stats daraus zu extrahieren.

Der Held sollte selbst seine Ausrüstung abfragen und die endgültigen Modifikatoren an das Eigenschaftssystem übergeben. Aber hier wedelt der Schwanz mit dem Hund.

4️⃣ Verwendung von Exceptions für die Logik
In der Methode equip_armor sehen wir Folgendes:
try:
if key not in self.slots_equipment:
print('Нет такого слота.')
# ... Logik ...
except KeyError:
print(f'Предмет не найден')

Erstens maskiert das Abfangen eines breiten KeyError echte Bugs im Code (z.B. einen Tippfehler im Dictionary innerhalb des try-Blocks). Zweitens sind Ausnahmen für außergewöhnliche Situationen gedacht, nicht um zu prüfen, ob ein Gegenstand im Inventar vorhanden ist. Dafür gibt es die Dictionary-Methode .get().

Insgesamt könnte man für den Anfang statt 10 nutzloser Rüstungsklassen und monströser Konstruktoren zumindest Folgendes machen:

from dataclasses import dataclass
from enum import Enum

class EquipmentSlot(Enum):
HEAD = "Шлем"
CHEST = "Нагрудник"
WEAPON = "Оружие"

@dataclass
class Equipment:
name: str
slot: EquipmentSlot
stats_bonus: dict[str, int]

# Erstellen eines Gegenstands:
crown = Equipment(
name='Шлем Господства',
slot=EquipmentSlot.HEAD,
stats_bonus={'strength': 5, 'agility': 7, 'intellect': 3}
)

Und das war's. Keine Code-Duplikate, Schutz vor Tippfehlern bei Slots durch Enum und ein erweiterbares Stats-System, dem man morgen problemlos "Glück" hinzufügen kann, ohne den __init__ von Dutzenden Klassen umschreiben zu müssen.

OOP bedeutet nicht, dass man für jede Entität im Universum eine separate Klasse hat. OOP geht um die Verwaltung von Komplexität und Zustand.


Für einen Monat Lernzeit ist das jedoch eine absolut normale Entwicklungsstufe.

📖 Lesen:
- Wann eine Klasse in Python böse ist: 6 Fälle, in denen Sie sich das Leben erschweren
- SOLID-Prinzipien in OOP mit Beispielen in Python