Menu
Coddy logo textTech

ダックタイピング

CoddyのLuaジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 39/70。

Luaはオブジェクトのclassを気にしません。オブジェクトが何をできるかだけを気にします。この概念はduck typingと呼ばれます。「duckのように歩き、duckのように鳴くなら、それはduckです」。プログラミング用語では、オブジェクトに呼び出しているmethodがあれば、それは動作します。

これは、同じ method 名を共有している限り、完全に無関係な classes 同士でも一緒に使用できることを意味します。

local Circle = {}
Circle.__index = Circle
function Circle:new() 
    local obj = {}
    setmetatable(obj, Circle)
    return obj
end
function Circle:draw()
    print("Drawing a circle")
end

local Square = {}
Square.__index = Square
function Square:new()
    local obj = {}
    setmetatable(obj, Square)
    return obj
end
function Square:draw()
    print("Drawing a square")
end

これらのクラスには継承関係がなく、完全に独立しています。それでも、同一のものとして扱うことができます。

local shapes = {Circle:new(), Square:new(), Circle:new()}

for _, shape in ipairs(shapes) do
    shape:draw()
end
-- 出力:
-- 円を描画しています
-- 正方形を描画しています
-- 円を描画しています

このループは各オブジェクトの型を確認しません。単に:draw()を呼び出し、そのメソッドが存在すると信頼します。この柔軟性は強力です。ループのコードをまったく変更せずに、新しい形状の型を追加できます。新しいクラスが:draw()を実装している限り、そのまま動作します。

challenge icon

チャレンジ

簡単

互いにまったく関係のない class を、すべて同じ方法で処理できる notification system を構築して、duck typing の力を示しましょう!

互いに継承しない、独立した3つの notification class を作成します。ただし、それらはすべて :send() method を実装します。これにより、混在したコレクションをループして、実際の型を気にせず各要素に対して :send() を呼び出せます。

コードを4つのファイルに整理します。

  • Email.lua: :new(recipient) constructor を持つ class です。メールの送信先を保存します。その :send() method は Emailing {recipient} を出力します。
  • SMS.lua: 完全に独立した class で、:new(phoneNumber) constructor を持ちます。その :send() method は Texting {phoneNumber} を出力します。
  • PushNotification.lua: もう1つの独立した class で、:new(deviceId) constructor を持ちます。その :send() method は Pushing to {deviceId} を出力します。
  • main.lua: すべてをまとめます!3つの入力を読み取り、各 notification type の instance を1つずつ作成し、それらをすべて1つの table に入れてから、ループしてそれぞれに :send() を呼び出します。

3つの入力を受け取ります。

  1. メールの送信先(例: alice@example.com
  2. 電話番号(例: 555-1234
  3. device ID(例: device_99

main file で3つすべての notification object を作成し、それらを table に保存して、table を反復処理しながらそれぞれに :send() を呼び出します。順序は Email、次に SMS、最後に PushNotification である必要があります。

たとえば、入力が bob@mail.com555-9876phone_42 の場合、出力は次のようになります。

Emailing bob@mail.com
Texting 555-9876
Pushing to phone_42

loop では、各 object の type を確認する必要がないことに注目してください。コレクション内のすべての object に :send() method があると信頼しているだけです。これが実際の duck typing です!:send() を実行できるなら、それは notification です。

自分で試してみよう

-- main.lua
-- すべての通知タイプをまとめ、ダックタイピングを実証する

-- 通知クラスをインポートする
local Email = require('Email')
local SMS = require('SMS')
local PushNotification = require('PushNotification')

-- 入力を読み取る
local recipient = io.read()
local phoneNumber = io.read()
local deviceId = io.read()

-- TODO: 各通知タイプのインスタンスを1つ作成する

-- TODO: すべての通知を1つのテーブルに入れる(Email、次にSMS、次にPushNotification)

-- TODO: テーブルをループして各通知で :send() を呼び出す
-- これはダックタイピングを実証します - 型をチェックせず、ただ :send() を呼び出します
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: Luaオンラインコンパイラ