エンジニアのソフトウェア的愛情

または私は如何にして心配するのを止めてプログラムを・愛する・ようになったか

double dispatch by Lua

module("message", package.seeall)

FOO = 1
BAR = 2
BAZ = 3

ALPHA = 1
BETA  = 2
GAMMA = 3

-- NULLメッセージ(インタフェースはあるが中身がないメッセージ)をつくる
function create_null_message()
  local result = { protocol = "null", type = "null" }
  function result:load(sequence)
  end
  function result:dispatch(receiver)
  end
  return result
end

-- 基本形のメッセージをつくる
function create_message(protocol)
  local result = { ["protocol"] = protocol }

  -- ディスパッチ関数の定義
  -- receiver:protocol_type_receivedという名前の関数を呼び出す
  function result:dispatch(receiver)
    if receiver then
      local f = receiver[self.protocol .. "_" .. self.type .. "_received"]
      if not f then
        f = receiver["default_received"]
        if not f then
          f = function(x, y) end
        end
      end
      f(receiver, self)
    end
  end

  return result
end

-- sequence から protocol foo のメッセージを作る関数群

function load_foo_alpha(self, sequence)
  self.type = "alpha"
end

function load_foo_beta(self, sequence)
  self.type = "beta"
end

function load_foo_gamma(self, sequence)
  self.type = "gamma"
end

function load_foo(sequence)
  local foo = create_message("foo")
  local type = sequence:byte(2)
  if type == ALPHA then
    foo.load = load_foo_alpha
  elseif type == BETA then
    foo.load = load_foo_beta
  elseif type == GAMMA then
    foo.load = load_foo_gamma
  else
    foo = create_null_message()
  end

  foo:load(sequence)
  return foo
end

-- sequence から protocol bar のメッセージを作る関数群

function load_bar_alpha(self, sequence)
  self.type = "alpha"
end

function load_bar_beta(self, sequence)
  self.type = "beta"
end

function load_bar_gamma(self, sequence)
  self.type = "gamma"
end

function load_bar(sequence)
  local bar = create_message("bar")
  local type = sequence:byte(2)
  if type == ALPHA then
    bar.load = load_bar_alpha
  elseif type == BETA then
    bar.load = load_bar_beta
  elseif type == GAMMA then
    bar.load = load_bar_gamma
  else
    bar = create_null_message()
  end

  bar:load(sequence)
  return bar
end

-- sequence から protocol baz のメッセージを作る関数群

function load_baz_alpha(self, sequence)
  self.type = "alpha"
end

function load_baz_beta(self, sequence)
  self.type = "beta"
end

function load_baz_gamma(self, sequence)
  self.type = "gamma"
end

function load_baz(sequence)
  local baz = create_message("baz")
  local type = sequence:byte(2)
  if type == ALPHA then
    baz.load = load_baz_alpha
  elseif type == BETA then
    baz.load = load_baz_beta
  elseif type == GAMMA then
    baz.load = load_baz_gamma
  else
    baz = create_null_message()
  end

  baz:load(sequence)
  return baz
end

-- sequence からメッセージを作る関数

function load(sequence)
  local protocol = sequence:byte(1)
  if protocol == FOO then
    return load_foo(sequence)
  elseif protocol == BAR then
    return load_bar(sequence)
  elseif protocol == BAZ then
    return load_baz(sequence)
  else
    return create_null_message
  end
end
require("message")

-- メッセージを受け取るオブジェクト receiver の定義

receiver = {}

-- protocol:foo / type:alpha を受け取る関数
function receiver:foo_alpha_received(msg)
  print("foo-alpha received")
end

-- protocol:bar / type:beta を受け取る関数
function receiver:bar_beta_received(msg)
  print("bar-beta received")
end

-- protocol:baz / type:gamma を受け取る関数
function receiver:baz_gamma_received(msg)
  print("baz-gamma received")
end

-- メッセージに対応する関数がない場合、デフォルトで呼び出される関数
function receiver:default_received(msg)
  print("default received")
end

-- サンプルのメッセージ

messages =
{
  string.char(message.FOO, message.ALPHA),
  string.char(message.FOO, message.BETA),
  string.char(message.BAR, message.BETA),
  string.char(message.BAZ, message.GAMMA)
}

-- メッセージを load (deserialize) してディスパッチ

for i = 1, #messages do
  msg = message.load(messages[i])
  msg:dispatch(receiver)
end


実行結果。

foo-alpha received
default received
bar-beta received
baz-gamma received


まだLuaはまだまだ道半ば。というかまだ入り口。

12/16追記

なんか"type"がハイライトされてるなと思ったら、型の種類が入ってる値だった。上書きしてしまった。