Menu
Coddy logo textTech

Interface commune

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

Le typage canard nous a montré que des classes sans lien peuvent fonctionner ensemble si elles partagent les mêmes noms de méthodes. Cette idée mène à un concept de conception puissant : l’interface commune. Lorsque vous concevez intentionnellement plusieurs classes pour qu’elles aient les mêmes signatures de méthodes, vous créez des composants interchangeables.

Imaginez que vous construisiez un système de journalisation. Vous pourriez vouloir envoyer des messages vers différentes destinations : un fichier, l’écran ou même un réseau. Au lieu d’écrire du code distinct pour chacune, vous concevez toutes vos classes de sortie afin qu’elles implémentent la même méthode :write() :

local LogFile = {}
LogFile.__index = LogFile

function LogFile:new(filename)
    local obj = {filename = filename}
    setmetatable(obj, LogFile)
    return obj
end

function LogFile:write(message)
    print("[FILE:" .. self.filename .. "] " .. message)
end

local Screen = {}
Screen.__index = Screen

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

function Screen:write(message)
    print("[SCREEN] " .. message)
end

Désormais, tout code qui doit afficher des messages peut accepter l’un ou l’autre type :

local function logMessage(output, msg)
    output:write(msg)
end

local file = LogFile:new("app.log")
local screen = Screen:new()

logMessage(file, "Starting up")    -- [FILE:app.log] Démarrage
logMessage(screen, "Starting up")  -- [SCREEN] Démarrage

La fonction logMessage ne sait pas, et n’a pas besoin de savoir, quelle classe elle reçoit. Elle attend seulement une méthode :write(). Cela rend votre code flexible. Vous pouvez ajouter ultérieurement de nouveaux types de sortie sans modifier les fonctions existantes.

challenge icon

Défi

Facile

Construisons un système de traitement des paiements où différents modes de paiement peuvent être utilisés de manière interchangeable ! Vous allez créer deux classes de paiement complètement indépendantes qui partagent une interface commune, une méthode :process(amount), permettant à tout code qui gère les paiements de fonctionner avec l’un ou l’autre type sans connaître la différence.

Vous allez organiser votre code sur trois fichiers :

  • CreditCard.lua : une class de paiement avec un constructeur :new(cardNumber) qui stocke le numéro de carte. Sa méthode :process(amount) doit afficher Charging {amount} to card {cardNumber}.
  • BankTransfer.lua : une class de paiement complètement distincte avec un constructeur :new(accountId). Sa méthode :process(amount) doit afficher Transferring {amount} from account {accountId}.
  • main.lua : créez une function appelée handlePayment(paymentMethod, amount) qui accepte tout object de paiement et un montant, puis appelle :process(amount) sur celui-ci. Cette function n’a pas besoin de savoir si elle traite une carte de crédit ou un virement bancaire. Elle s’attend simplement à ce que l’object possède une méthode :process().

Vous recevrez trois entrées :

  1. Un numéro de carte de crédit (par exemple, 4532-1234-5678)
  2. Un identifiant de compte bancaire (par exemple, ACC-9876)
  3. Un montant de paiement (par exemple, 150)

Dans votre fichier principal, créez une instance de chaque type de paiement à l’aide des deux premières entrées. Utilisez ensuite votre handlePayment pour traiter le montant indiqué avec les deux modes de paiement : d’abord la carte de crédit, puis le virement bancaire.

Par exemple, si les entrées sont 1111-2222-3333, ACC-5555 et 75, la sortie doit être :

Charging 75 to card 1111-2222-3333
Transferring 75 from account ACC-5555

La beauté de cette conception est que votre handlePayment fonctionne avec tout object qui implémente :process(). Vous pourriez ajouter une class PayPal demain et, tant qu’elle possède une méthode :process(amount), elle fonctionnerait avec votre function existante sans aucune modification !

Essayez vous-même

-- Importer les classes de paiement
local CreditCard = require('CreditCard')
local BankTransfer = require('BankTransfer')

-- Lire les entrées
local cardNumber = io.read()
local accountId = io.read()
local amount = tonumber(io.read())

-- TODO: Créer la fonction handlePayment qui accepte n'importe quel objet de paiement
-- et un montant, puis appelle :process(amount) sur celui-ci


-- TODO: Créer des instances de CreditCard et BankTransfer en utilisant les entrées


-- TODO: Utiliser handlePayment pour traiter le montant via les deux méthodes de paiement
-- D'abord la carte de crédit, puis le virement bancaire
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