@shadow_0771 a demandé un retour sur son code dans le cadre de l'apprentissage de la POO.

Alors... #on_grille_le_code!

Un RPG en console, c'est la base. Le code remplit sa tâche, mais il est écrit de telle manière que toute tentative de le faire évoluer provoquera une ÉÉÉÉÉÉÉÉNORME douleur. Analysons les problèmes les plus importants 👇

1️⃣ Héritage par Ctrl+C, Ctrl+V
Regardons la hiérarchie des armures :
class Armor(Item):
def __init__(self, name, category, strength=None, value_strength=None...): # et encore 100500 arguments
super().__init__(name, category)
# ...

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

class Chestplate(Armor):
# Copie de Helmet

class Greaves(Armor):
# Copie de Chestplate

Toutes ces classes (Helmet, Chestplate, Greaves, Boots) sont absolument identiques. Elles n'ajoutent ni nouveaux attributs, ni nouveau comportement. Elles ne font rien d'autre qu'appeler super().__init__.

La POO n'a pas été créée pour décrire chaque objet physique du monde avec une classe séparée. Si les entités ne diffèrent que par le nom de la catégorie, il devrait y avoir une seule classe Armor avec un attribut slot_type (idéalement via une Enum).

2️⃣ Le constructeur Frankenstein
Regardez comment un objet est créé :
crown = Helmet('Шлем Господства', 'Шлем', 'Сила', 5, 'Ловкость', 7, 'Интеллект', 3)

Ne codez jamais en dur les noms des statistiques dans la signature de la méthode. Utilisez des dictionnaires.
Au lieu de cette liste interminable de paramètres, un objet devrait accepter stats={'strength': 5, 'agility': 7, 'intellect': 3}.

3️⃣ Inceste de classes
La classe Characteristic prend un hero, puis fait ceci :
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

Cela s'appelle "Couplage fort" (Tight Coupling). La classe des caractéristiques fouille avec ses mains sales dans l'inventaire du héros, vérifie s'il y a des objets, puis via hasattr (qui est déjà une béquille dans 99% des cas) essaie d'en extraire les statistiques.

Le héros devrait lui-même interroger son équipement et transmettre les modificateurs finaux au système de caractéristiques. Actuellement, c'est la queue qui remue le chien.

4️⃣ Utilisation des exceptions pour la logique
Dans la méthode equip_armor, on voit ceci :
try:
if key not in self.slots_equipment:
print('Нет такого слота.')
# ... logique ...
except KeyError:
print(f'Предмет не найден')

Premièrement, intercepter une KeyError large masquera de véritables bugs dans le code (par exemple, une faute de frappe dans un dictionnaire à l'intérieur du try). Deuxièmement, les exceptions sont pour les situations exceptionnelles, pas pour vérifier la présence d'un objet dans l'inventaire. Pour cela, il existe la méthode .get() des dictionnaires.

En résumé, pour commencer, au lieu de 10 classes d'armure inutiles et de constructeurs monstrueux, on pourrait faire au moins ceci :

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]

# Création d'un objet :
crown = Equipment(
name='Шлем Господства',
slot=EquipmentSlot.HEAD,
stats_bonus={'strength': 5, 'agility': 7, 'intellect': 3}
)

Et voilà. Pas de duplication de code, protection contre les fautes de frappe dans les slots via Enum, et système de statistiques extensible auquel on pourrait ajouter demain "Chance" sans réécrire __init__ dans une dizaine de classes.

La POO, ce n'est pas quand vous avez une classe séparée pour chaque entité de l'univers. La POO, c'est gérer la complexité et l'état.


Mais pour un mois d'apprentissage, c'est une étape d'évolution tout à fait normale.

📖 À lire :
- Quand une classe en Python est un mal : 6 cas où vous vous compliquez la vie
- Principes SOLID en POO avec des exemples en Python