Dans Verse, vous pouvez créer une classe qui étend la définition d'une autre classe en ajoutant ou en modifiant les champs et les méthodes de cette dernière. Cette méthode est également appelée sous-classement ou héritage, car une classe hérite des définitions de l'autre classe.
Prenons l'exemple de l'appareil Concepteur de classe pour illustrer le sous-classement. Le concepteur de classe vous permet de créer des classes pour les joueurs et de définir les attributs et les inventaires propres à une classe de personnage, par exemple un personnage de type tank ou DPS (dégâts par seconde).
![]() |
![]() |
| Classe de personnage DPS créée avec le concepteur de classe | Classe de personnage tank créée avec le concepteur de classe |
Dans Verse, vous pouvez créer une classe tank et une classe dps comme suit :
tank := class():
StartingShields : int
MaxShields : int
AllowOvershield : logic
DamageReduction : int
dps := class():
StartingShields : int
MaxShields : int
AllowOvershield : logic
MovementMultiplier : float
Étant donné que certains des champs des deux classes sont identiques, vous pouvez réduire la duplication en créant une superclasse contenant les propriétés et les comportements partagés des deux classes. Appelez cette superclasse player_character, et créez les sous-classes tank et dps de player_character :
player_character := class():
StartingShields : int
MaxShields : int
AllowOvershield : logic
dps := class(player_character):
MovementMultiplier : float
tank := class(player_character):
DamageReduction : int
Dans la mesure où les classes tank et dps sont des sous-classes de player_character, elles héritent automatiquement des champs et des méthodes de la classe player_character. Par conséquent, vous devez uniquement spécifier les différences entre cette classe et la superclasse.
Par exemple, la classe dps ajoute seulement le champ Movement Multiplier, et la classe tank seulement le champ DamageReduction. Cette configuration est utile si vous modifiez ultérieurement le comportement partagé des deux classes, car vous n'aurez à le modifier que dans la super-classe.
Grâce à Verse, vous pouvez ajouter d'autres modifications pour différencier les classes tank et dps, en ajoutant des méthodes aux sous-classes.
Un effet utile du sous-classement est que vous pouvez utiliser la relation entre une superclasse et ses sous-classes. En raison de l'héritage, une instance de tank est une classe player_character spécialisée, et une instance de dps est une classe player_character spécialisée. Ce type de relation est appelé relation is-a. Dans la mesure où tank et dps sont toutes deux des sous-classes de la même superclasse et qu'elles divergent de leur superclasse commune, tank n'a pas de relation avec dps.
Spécificateur de remplacement
Pour créer des instances de classes avec des valeurs initiales, une pratique courante consiste à créer une fonction qui génère les instances. Par exemple :
CreateDPSPlayerCharacter() : dps =
return dps{StartingShields := 0, MaxShields := 0, AllowOvershield := false, MovementMultiplier := 1.9}
CreateTankPlayerCharacter() : tank =
return tank{StartingShields := 100, MaxShields := 200, AllowOvershield := true, DamageReduction := 50}
Les fonctions CreateTankPlayerCharacter() et CreateDPSPlayerCharacter() créent les instances avec les valeurs initiales appropriées. Vous pouvez également remplacer les champs de la superclasse et leur attribuer des valeurs initiales, afin de ne pas avoir à fournir autant de valeurs initiales lors de la création d'une instance.
Par exemple, la classe tank de la section précédente pourrait ressembler à celle-ci, avec des remplacements de champs :
tank := class(player_character):
StartingShields<override> : int = 100
MaxShields<override> : int = 200
AllowOvershield<override> : logic = true
DamageReduction : int = 50
CreateTankPlayerCharacter() : tank =
return tank{}
Vous pouvez également remplacer des méthodes dans la sous-classe, c'est-à-dire utiliser la méthode de substitution partout où elle est compatible. En d'autres termes :
- La méthode doit accepter au moins tout argument pris en charge par la méthode substituée, de sorte que le type de paramètre doit être un super-type du type de paramètre de la fonction substituée.
- La méthode ne doit pas renvoyer une valeur que la méthode substituée ne pouvait pas renvoyer, de sorte que le type de retour doit être un sous-type du type de retour de la méthode substituée.
- La méthode ne doit pas avoir plus d'effets que la méthode substituée ; le spécificateur d'effet doit donc être un sous-type du spécificateur d'effet de la méthode substituée.
Super
À la manière de Self, (super:) permet d'accéder aux implémentations de champs et de méthodes de la super-classe. Pour pouvoir utiliser (super:), il est nécessaire d'implémenter le champ ou la méthode dans la définition de la superclasse.
pet := class():
Sound : string
Speak() : void =
Log(Sound)
cat := class(pet):
Sound<override> : string = "Meow"
Speak<override>() : void =
(super:)Speak() # "Meow" apparaît dans le journal de sortie
Log("Purr") # "Purr" apparaît dans le journal de sortie
Expressions de bloc dans le corps d'une sous-classe
Toutes les expressions block dans le corps d'une sous-classe sont exécutées après les expressions block spécifiées dans le corps de la superclasse. Par exemple, dans le code suivant, lorsque l'instance de la classe cat nommée MrSnuffles est créée, Speak() est exécutée en premier, puis Purr().
pet := class():
Speak() : void =
...
block:
Speak()
cat := class(pet):
Purr() : void =
...
block:
Purr()
MrSnuffles := cat{}
Spécificateur d'abstraction
Lorsqu'une classe ou une méthode de classe possède le spécificateur abstract, vous ne pouvez pas créer d'instance de la classe. Les classes abstraites sont destinées à être utilisées comme une superclasse avec implémentation partielle ou comme une interface commune. Elles sont utiles lorsque vous n'avez pas besoin des instances d'une superclasse, mais que vous ne souhaitez pas dupliquer les propriétés et les comportements pour l'ensemble des classes semblables.
Dans l'exemple suivant, dans la mesure où le concept d'animal de compagnie est abstrait, une instance de la classe animal de compagnie n'est pas assez précise. Pour que ces sous-classes ne soient pas abstraites, il convient donc de spécifier chat ou chien.
pet := class<abstract>():
Speak() : void
cat := class(pet):
Speak() : void =
...
dog := class(pet):
Speak() : void =
...

