ダックタイピング
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()を実装している限り、そのまま動作します。
チャレンジ
簡単互いにまったく関係のない 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つの入力を受け取ります。
- メールの送信先(例:
alice@example.com) - 電話番号(例:
555-1234) - device ID(例:
device_99)
main file で3つすべての notification object を作成し、それらを table に保存して、table を反復処理しながらそれぞれに :send() を呼び出します。順序は Email、次に SMS、最後に PushNotification である必要があります。
たとえば、入力が bob@mail.com、555-9876、phone_42 の場合、出力は次のようになります。
Emailing bob@mail.com
Texting 555-9876
Pushing to phone_42loop では、各 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() を呼び出します
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
自分で練習してみよう: Luaオンラインコンパイラ