Menu
CoddyTech

通用接口

本课属于 Coddy Lua 学习旅程的“面向对象编程”单元。第 40 课,共 70 课。

鸭子类型让我们看到,只要不相关的类拥有相同的方法名,就可以协同工作。这个理念引出了一个强大的设计概念:通用接口。当你有意让多个类拥有相同的方法签名时,就创建了可互换的组件。

想象一下,你正在构建一个日志系统。你可能想把消息发送到不同的目标:文件、屏幕,甚至是网络。你可以设计让所有输出类都实现相同的 :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

现在,任何需要输出消息的代码都可以接受这两种类型:

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] 正在启动
logMessage(screen, "Starting up")  -- [SCREEN] 正在启动

logMessage 函数不知道也不关心它接收的是哪个类。它只要求对象提供 :write() 方法。这样代码就更灵活了。以后你可以添加新的输出类型,而无需修改现有函数。

challenge icon

挑战

简单

让我们构建一个支付处理系统,让不同的支付方式可以互换使用!你将创建两个完全无关的支付类,它们共享一个通用接口,即 :process(amount) 方法。这样,处理支付的代码就能适用于任意一种类型,而无需了解它们之间的区别。

你将把代码分成 3 个文件:

  • CreditCard.lua:一个支付类,带有 :new(cardNumber) 构造函数,用于存储卡号。它的 :process(amount) 方法应打印 Charging {amount} to card {cardNumber}。
  • BankTransfer.lua:一个完全独立的支付类,带有 :new(accountId) 构造函数。它的 :process(amount) 方法应打印 Transferring {amount} from account {accountId}。
  • main.lua:创建一个名为 handlePayment(paymentMethod, amount) 的函数,该函数接收任意支付对象和一个金额,然后对该对象调用 :process(amount)。这个函数不需要知道它处理的是信用卡还是银行转账,只需要对象具有 :process() 方法即可。

你将收到 3 个输入:

  1. 信用卡号(例如:4532-1234-5678)
  2. 银行账户 ID(例如:ACC-9876)
  3. 支付金额(例如:150)

在主文件中,使用前两个输入分别创建一种支付类型的实例。然后,使用你的 handlePayment 函数,通过这两种支付方式处理给定金额:先使用信用卡,再使用银行转账。

例如,如果输入为 1111-2222-3333、ACC-5555 和 75,输出应为:

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

这种设计的优点是,你的 handlePayment 函数适用于任何实现了 :process() 的对象。你明天就可以添加一个 PayPal 类,只要它有一个 :process(amount) 方法,就能直接与现有函数配合使用,无需做任何修改!

亲手试试

-- 导入支付类
local CreditCard = require('CreditCard')
local BankTransfer = require('BankTransfer')

-- 读取输入
local cardNumber = io.read()
local accountId = io.read()
local amount = tonumber(io.read())

-- TODO:创建 handlePayment 函数,使其接受任意支付对象
-- 和一个金额,然后调用该对象的 :process(amount) 方法


-- TODO:使用输入创建 CreditCard 和 BankTransfer 的实例


-- TODO:使用 handlePayment,通过两种支付方式处理该金额
-- 先用信用卡,再用银行转账
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: Lua 在线编译器