ゲッターとセッターは、オブジェクトのプロパティの値を読み書きするためのメソッドです。 Crystalにはマクロがあるので、プロパティのゲッターとセッターを簡単に定義できます。 マクロはコンパイル時にコードを生成する仕組みで、これについては後のマクロの概念で扱います。
Rubyでは、これらのメソッドはattr_reader、attr_writer、attr_accessorというメソッドを使って定義され、表面的にはCrystalの実装とよく似ています。
Crystalでは、これらはgetter、setter、propertyというマクロとして定義しています。
ゲッターは、インスタンス変数の値を返すメソッドを定義するマクロです。
つまり、インスタンス変数にアクセスしたいときに(initializeを除くメソッド内で)その前に@を書く必要がなくなり、代わりにゲッターのマクロが定義したメソッドを呼び出せます。
ゲッターのマクロには、カンマで区切って複数のインスタンス変数を渡せます。
# This:
class Person
@name : String
@age : Int32
def name
@name
end
def age
@age
end
end
# Is the same as this:
class Person
@name : String
@age : Int32
getter name, :age
end
見てのとおり、ゲッターを使うと書くコードの量が減り、読みやすくなります。 また、RubyとCrystalは、Crystalがゲッターのマクロの引数として変数名とシンボルの両方を受け付けるという点で異なります。 シンボルについては、後のシンボルの概念で扱います。
セッターは、インスタンス変数の値を設定するメソッドを定義するマクロです。
このマクロはあまり使われず、代わりにpropertyマクロがよく使われます。
作成されるメソッドはname_of_method=のようになります。この=があることで、プロパティを設定できるようになります。
このメソッド定義は少し特殊で、メソッドが受け取る引数が=記号の後に来ます。
Crystalの他のいくつかの特殊なメソッドも、+メソッドのようにこの構文を使います。
ゲッターのマクロと同様に、セッターを定義した後にインスタンス変数の値を更新したいときは、セッターのマクロが定義したメソッドを呼び出せます。
# This:
class Person
@name : String
@age : Int32
def name=(name : String)
@name = name
end
def age=(age : Int32)
@age = age
end
end
# Is the same as this:
class Person
@name : String
@age : Int32
setter name, :age
end
プロパティは、インスタンス変数のゲッターとセッターの両方を定義するマクロです。
class Person
@name : String
@age : Int32
property name, :age
end
この演習では、はかりをモデル化します。
初期化されるとき、はかりは工場出荷時の設定を参照する必要があります。この設定は、はかりが販売される地域によって異なります。 そのため、はかりは精度と、メートル法かヤード・ポンド法かの指定を付けて初期化できるようにします。
2つの引数を取るWeighingMachine#initializeメソッドを実装します。precisionはInt32、metricはBoolです。
metric引数がtrueのときはメートル法を、そうでなければヤード・ポンド法を使うことを意味します。
また、初期化メソッドでは、インスタンス変数@weightを0.0に設定します。
precision = 3
metric = true
vm = WeighingMachine.new(precision, metric)
さまざまな要求に応えるため、各はかりは精度(小数点以下の桁数)を指定してカスタマイズできるようにします。
WeighingMachine#precisionゲッターメソッドを実装し、はかりの精度を返すようにします。
precision = 3
metric = true
vm = WeighingMachine.new(precision, metric)
vm.precision
# => 3
WeighingMachine#weightプロパティを実装し、重さの取得と設定の_両方_ができるようにします。
precision = 3
metric = true
wm = WeighingMachine.new(precision, metric)
wm.weight = 60.5
wm.weight
# => 60.5
wm.weigh
# => 60.5
WeighingMachine#metric=プロパティを実装し、単位を設定できるようにします。
真偽値を受け取れるようにします。
precision = 3
metric = true
wm = WeighingMachine.new(precision, metric)
vm.weight = 60.5
wm.metric = false
vm.weigh
# => 133.377