Menu
Coddy logo textTech

Étendre le constructeur

Fait partie de la section Programmation orientée objet du Journey Lua de Coddy. Leçon 33 sur 70.

Lorsqu’une classe enfant hérite d’une classe parente, vous souhaitez souvent que le constructeur de l’enfant initialise tout ce que ferait le parent, ainsi que tout champ supplémentaire propre à l’enfant. Au lieu de dupliquer le code, vous pouvez appeler directement le constructeur du parent.

La technique consiste à utiliser le nom de la classe parente pour appeler sa méthode :new(), en passant self comme premier argument. Cela crée l’objet de base avec toute l’initialisation de la classe parente, que vous personnalisez ensuite pour la classe enfant.

local Vehicle = {}
Vehicle.__index = Vehicle

function Vehicle:new(speed)
    local obj = {speed = speed}
    setmetatable(obj, Vehicle)
    return obj
end

-- Car hérite de Vehicle
local Car = {}
Car.__index = Car
setmetatable(Car, {__index = Vehicle})

function Car:new(speed, brand)
    local obj = Vehicle.new(Vehicle, speed)  -- Appeler le constructeur parent
    obj.brand = brand                         -- Ajouter le champ spécifique à l'enfant
    setmetatable(obj, Car)                    -- Relier à nouveau à Car
    return obj
end

Note les étapes clés dans le constructeur enfant : d’abord, nous appelons Vehicle.new(Vehicle, speed) pour obtenir un objet dont le champ speed est déjà défini. Ensuite, nous ajoutons le champ brand propre à l’enfant. Enfin, nous modifions la métatable de Vehicle à Car afin que l’instance recherche d’abord les méthodes dans la classe enfant.

Vous pouvez également utiliser la syntaxe avec deux-points lors de l’appel du constructeur parent : Vehicle:new(speed) est équivalent à Vehicle.new(Vehicle, speed), puisque les deux transmettent Vehicle comme premier argument. Les deux formes fonctionnent correctement ici, alors n’hésitez pas à utiliser celle que vous trouvez la plus lisible.

local myCar = Car:new(120, "Toyota")
print(myCar.speed)  -- Sortie : 120
print(myCar.brand)  -- Sortie : Toyota

Ce modèle permet de garder votre code DRY : la logique d’initialisation partagée réside dans le parent, tandis que chaque enfant ne gère que ses attributs uniques.

challenge icon

Défi

Facile

Construisons une hiérarchie de créatures où une classe enfant étend le constructeur de sa classe parente ! Vous allez créer une classe parente Animal et une classe enfant Bird, dont le constructeur appelle celui de Animal pour gérer l'initialisation commune avant d'ajouter son propre attribut unique.

Vous organiserez votre code sur trois fichiers :

  • Animal.lua : définissez votre classe parente avec un constructeur :new(name) qui stocke le nom de l'animal. Incluez une méthode :describe() qui affiche le nom de l'animal. Mettez en place le modèle de classe standard avec __index et retournez la classe.
  • Bird.lua : définissez votre classe enfant qui hérite de Animal. Le constructeur :new(name, wingspan) de votre Bird doit appeler le constructeur parent en utilisant Animal.new(Animal, name) pour initialiser le nom, puis ajouter le champ wingspan à l'objet, et enfin relier la métatable à Bird. Incluez une méthode :info() qui affiche à la fois le nom et l'envergure dans un format précis.
  • main.lua : exigez votre module Bird, lisez deux entrées (le nom et l'envergure), créez une instance de Bird et appelez les deux méthodes pour démontrer que les fonctionnalités héritées et spécifiques à la classe enfant fonctionnent ensemble.

Vous recevrez deux entrées :

  1. Le nom de l'oiseau
  2. L'envergure de l'oiseau (un nombre)

Dans votre fichier principal, créez un Bird avec le nom et l'envergure donnés. Appelez ensuite :describe(), puis :info(), sur des lignes séparées.

La méthode :describe() (héritée de Animal) doit afficher :

Animal: {name}

La méthode :info() (définie dans Bird) doit afficher :

{name} has a wingspan of {wingspan}

Par exemple, si les entrées sont Eagle et 200, la sortie doit être :

Animal: Eagle
Eagle has a wingspan of 200

L'idée essentielle est que votre constructeur Bird ne duplique pas l'initialisation du nom. Il délègue cette tâche au constructeur de Animal, puis n'ajoute que ce qui est propre aux oiseaux. Votre code reste ainsi DRY tout en construisant une véritable chaîne d'héritage !

Essayez vous-même

-- main.lua: Point d'entrée

local Bird = require('Bird')

-- Lire les entrées
local name = io.read()
local wingspan = tonumber(io.read())

-- TODO: Créer une instance de Bird avec le name et le wingspan donnés

-- TODO: Appeler :describe() sur le bird (hérité de Animal)

-- TODO: Appeler :info() sur le bird (défini dans Bird)
quiz iconTestez-vous

Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.

Toutes les leçons de Programmation orientée objet

Entraînez-vous par vous-même : Compilateur Lua en ligne