LoginSignup
0

More than 1 year has passed since last update.

チャート式ruby-VI(hello class)

Last updated at Posted at 2020-11-10

お題:class化

チャート式Rubyの最終6回目,オブジェクト指向です.このキーとなる考え方が,

  • 隠蔽(capsulation)
  • 継承(inheritance)
  • 多形(polymorphism)

です.methodにするのはある種の隠蔽なんですが,さらに言語のシステムとして徹底したのが,classです.

解法:最初のclass

前にやったhello.rbを少し拡張したのをみてください.

def puts_hello name
  puts "Hello #{name}."
end

def gets_name
  name = ARGV[0] || 'world'
  return name
end

name = gets_name
puts_hello name

main loopがありますよね.「こいつも消せないか?」というのがclassの発想です.最終的には以下の通りできるんですが,これをrefactoringしましょう.

class Greeter
  def initialize
    @name = gets_name
    puts_hello
  end

  def puts_hello #salute
    puts "Hello #{@name}."
  end

  def gets_name
    name = ARGV[0] || 'world'
    return name.capitalize
  end
end

Greeter.new
> ruby hello_class.rb

としてみてください.同じでしょ.同じなんですよ.

  • Greeter.new
  • class Greeter
  • initialize method
  • @name

なんかがkeyになります.

さらにfoldingとかtoggleの機能を使うと見やすくなります.

解説(指向):method的かobject思考的か

methodとclassを比べるとオブジェクト指向の書き方の流儀の違いが実感できます.method名をverbとすると,

  • methodでは,verb(subject, object)
  • object指向では, subject.verb(object)

と読めて,英語の文法に近いので,object志向がいいと良く言われます.

サンプルコードを見てください.上から,method, 継承を使ったclass, classへの 上乗せ 上書き(override monkey patching)で書いてみました.

require 'colorize'

# method
def hello(name)
  "Hello #{name}."
end

# inherited class
class Greeter < String
  def hello
    "Hello #{self}."
  end
end

# extend class
class String
  def hello
    "Hello #{self}."
  end
end

# method call
name = ARGV[0]
puts hello(name).green

# inherited class call
greeter = Greeter.new(ARGV[0])
puts greeter.hello.green

# extend class call, override
puts ARGV[0].hello.green

最後にある通り,

ARGV[0].hello.green

ではSVO的な発想ができないですが,それでも,順番に振る舞いを付け加えていけるのは直感的です.methodだと,一番内側から順に外側へ向かって,括弧で括りながら書いていくんで,書く時はいいんですが,後で読む時に苦労します.object指向は,読む方だけでなく,慣れてくるとあてずっぽうでcodeが書けるようになりますんで,書く方にも優しいんですよ.

「上乗せ」って「ただ乗り」みたいで,ラクそうですし,上書きも可能です.これらは総称してmonkey patchingとか呼ばれます.あまり良くないって...でも...

解説(付録):attr_accessorとか

rubyのclassのidiom(慣用句)でよくあるのが,

./code/hello_class_accessor.rb
 1  class Greeter
 2    attr_accessor :name
 3    def initialize(name='world')
 4      @name = name
 5    end
 6  
 7    def hello
 8      puts "Hello #{@name.capitalize}."
 9    end
10  end
11  
12  greeter = Greeter.new()
13  greeter.hello
14  greeter.name = ARGV[0]
15  greeter.hello

これを動かしてみると

> ruby hello_class_accessor.rb bob
Hello World.
Hello Bob.

どうなっているか予測できますか?予測できれば相当な実力がついています.accessorとかdefault代入とかの意味がわかっているということ.

main loopの操作を順番に説明すると

  1. (L12)Helloというclassを引数なしでnewしてworldと名付けた.
    1. (L3)nameはdefaultでは'world'
    2. (L4)これをinstance(子供)変数の@nameに入れた
  2. (L13)次にhelloメソッドを呼んだ
    1. (L8)'Hello world.'
  3. (L14)world.nameにARGV[0]='bob'を代入した
    1. (L2)attr_accessorに:nameがあるので,代入される
    2. attr_accessorは以下のmethodsを作っているのと等価
  4. (L15)helloメソッドを呼んだ
    1. (L8)'Hello bob.'
attr_accessorと等価なmethod
def name  # reader(getter)
  return @name
end
def name=(new_name) # writer(setter)
  @name = new_name
end

getter, setterを一度に作るのがattr_accessorで,どちらかだけのときには,attr_reader, attr_writerを使います.

あとは,privateとかprotectedで,methodのaccess制限します.defaultはpublicなんですが,デメテルの箱入り娘は隠しておくべきとか...

言い忘れた一番大事なこと

classの「隠蔽,継承」とそれに関連する諸注意でした.

えっと,一番大事なことを伝えるのを忘れてました.変数の名前とかclassの構成とかはcodeを書くまえにあまり悩まないでください.掟ってのは,そのうちに身についてきます.考えてなんとかなるものではないんで.「下手の考え休みに似たり」です.下手がいくら考えても答えは出てきません.

それよりも動くcodeをガンガン書いて行ってください.どっちみち必要な動作はどっかで書く必要があります.置き場所とか名前とかは,ここまでで示した通り,あとで移動させたり変更したりするものであって,初めから決まっているものではありません.設計よりも実装です.

ただ,見やすくするためには「classまでもっていくべき」ということだけは意識してください.Rubular の所でも強調しましたが,構成要素ってのは意外と限られています.classを書くときに必要なidiomって,突き詰めると,ここに書いたことぐらいで,あとはそれらの組み合わせです.これらを駆使して,オブジェクト思考の「隠蔽,継承,多形」がうまくいくとほんとうに綺麗ですから.どうすれば綺麗にできるかの指針がRefactoringと名づけられたstepです.こいつにはMartin Fowlerの良書1があります.第一章をやるだけで,「多形」とDesign patternの必要性を実感できます.

発展問題

  • assert_equalをclass化して,Integerに override monkey patching しなさい.
    • 3.assert_equal 3でtrueが返るようになります.
    • selfで3とかが返るはず.
    • moduleをincludeする方が筋が良さそうですが...自信ありません.

参考資料


  • source ~/git_hub/ruby_docs/chart_style_ruby/c6_class_hello.org
  1. リファクタリング:Rubyエディション, ジェイ・フィールズ , シェーン・ハービー , マーティン・ファウラー , 長尾 高弘, アスキー・メディアワークス (2010/2/27).

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
0