ようへいの日々精進XP

よかろうもん

2018 年 03 月 23 日(金)

ジョギング

  • 体調はイマイチだったが、汗を少しかいたら楽になるかなと思って走った
  • 香椎浜 x 2 周
  • 左膝の内側、同足底筋も痛い...

日課

  • お休み

体調

  • やっぱり、終日辛い感じだった

夕飯

  • 元気つけようと思って、久しぶりにざいとんで台湾ラーメンと餃子

今日のるびぃ ~ Ruby 技術者認定試験 Gold 演習問題を解いていく (6) ~

もう何度か解いているけど, Ruby 技術者認定試験合格教本 の演習問題と模擬試験を数問ずつ解いていく. ポイントは, 問題が何について問われているかがちゃんと理解出来ていること.

Q16. オブジェクト指向

以下のコードを実行するとどうなるか.

module M1; end
module M2; end
class Cls1; include M1; end
class Cls2 < Cls1
  p self.ancestors
  include M2
end

以下のように出力される (はず).

[Cls2, Cls1, M1, Object, Kernel, BasicObject]

以下, irb での実行例.

[Cls2, Cls1, M1, Object, Kernel, BasicObject]
=> Cls2

以下, 解説とドキュメントより抜粋.

  • クラス, モジュールのスーパークラスとインクルードしているモジュールを優先順位順に配列に格納して返す
  • Cls2 クラスの include M2 は ancestors 実行後の為, ancestors 対象外となる
  • モジュールの機能追加は、クラスの継承関係の間にそのモジュールが挿入されることで実現される
  • メソッドの探索などはスーパークラスよりもインクルードされたモジュールのほうが先に行われる

例えば, 以下のようなコードだとどうなるか.

module M1; end
module M2; end
class Cls1; include M1; end
class Cls2 < Cls1
  include M2
end
Cls2.ancestors

スーパークラスよりもインクルードされたモジュールの方が先にメソッド探索されるということなので, 以下のようになる.

[Cls2, M2, Cls1, M1, Object, Kernel, BasicObject]

実際に irb で試してみる.

irb(main):007:0> Cls2.ancestors
=> [Cls2, M2, Cls1, M1, Object, Kernel, BasicObject]

LGTM.

Q17. オブジェクト指向

以下のコードを実行すると何が表示されるか.

module M1; end
module M2; end
class Cls1; prepend M1; end
class Cls2 < Cls1
  prepend M2
end
Cls2.ancestors

以下のように出力される (はず)

[M2, Cls2, M1, Cls1, Object, Kernel BasicObject]

以下, irb での実行例.

irb(main):007:0> Cls2.ancestors
=> [M2, Cls2, M1, Cls1, Object, Kernel, BasicObject]

以下, ドキュメントと解説より抜粋.

  • prepend は指定したモジュールを self の継承チェインの先頭に追加することで self の定数, メソッド, モジュール変数を「上書き」する
  • prepend の引数として渡したモジュールのインスタンスメソッドで super を呼ぶことで self のモジュール/クラスのメソッドを呼び出すことが出来る
  • prepend されたモジュールは, prepend した対象のクラスより先にメソッドの探索が行われるように継承関係が作られる

ちなみに, 以下のコードを実行した場合にどうなるか.

module M1
  def foo
    puts 'M1#foo'
    super
  end
end

class Cls1
  def foo
    puts 'Cls1#foo'
  end
end

class Cls2 < Cls1
  prepend M1
  def foo
    puts 'Cls2#foo'
  end
end

Cls2.new.foo

以下のようになるはず.

M1#foo
Cls2#foo

irb で実行してみる.

irb(main):020:0> 
irb(main):021:0* Cls2.new.foo
M1#foo
Cls2#foo
=> nil

prepend の引数として渡したモジュールのインスタンスメソッドで super を呼ぶことで self のモジュール/クラスのメソッドを呼び出すことが出来るということから, 上記のように Cls2 の同名メソッド (Cls2#foo) を呼ぶことが出来ている.

Q18. オブジェクト指向

以下の実行結果になるように, [ x ] に記述する適切なコードを全て選ぶ.

Class Example
  def hoge
    self.piyo
  end
  
  [ x ]
  def piyo
    puts 'piyo'
  end
end
Example.new.hoge

以下, 実行結果.

piyo

piyo がレシーバ付きで呼びだされている点に注目すると, [ x ] で定義することが出来るコードは以下となる (はず).

  • protected
  • public
  • 何も記述しない

以下, irb での実行結果の抜粋.

# protected
irb(main):011:0> Example.new.hoge
piyo

# public
irb(main):011:0> Example.new.hoge
piyo

# 何も記述しない
irb(main):021:0> Example.new.hoge
piyo

# private だと...
irb(main):011:0> Example.new.hoge
NoMethodError: private method `piyo' called for #<Example:0x0055acdca4ac98>

private 指定されたメソッドはレシーバ付きで呼び出すことが出来ない.

ふむふむ.