概要
便利な機能であっても、少なからずデメリットも発生することがあると思います。
なので使う側はメリット・デメリットのバランスを判断して、適切に実装を行うことができるようになる必要があるのでは?
と思い、記事にまとめてみました。
実際に保守しづらさ のほうが勝っているケースがあり、改めて必要性があるのか?を検討するための参考になればと思います
まずattr_*を使うとどうなるか?
attr_reader :name
とすることで引数の名称でgetterメソッドが定義されます
def name
@name
end
attr_writer :name
とすることで引数の名称でsetterメソッドが定義されます
def name=(val)
@name = val
end
attr_accessor :name
はreaderとwriterの複合メソッドです
通常、インスタンス変数はインスタンスの内部にて @hoge
でアクセスできるものがpublicでメソッドが定義されるため外部からインスタンス変数の値を触ることができるようになります。
この時private
を使用することによってprivateでメソッドを定義させることも可能です。
private
attr_reader :hoge
メリットとデメリットについて
メリット
外部からの呼び出し
本来の用途である、public
メソッドとして切られるため外部からの使用ができます
※privateにもできます
ここは皆さんご存知のとおりでしょう。
改修のしやすさ
class Hoge
private
attr_accessor :hoge
def initialize(hoge:)
@hoge = hoge
end
def connect
Service.call(hoge: hoge) # <- ココ
end
end
このような実装になっているとき、
def hoge
@hoge + 1
end
のように改修する必要が出たときにオーバーライドすることで修正はしやすくなります。
デメリット
スコープの意識
public
かprivate
かなどを正しく考える必要がでる
メソッドの定義
メソッドを不用意に定義することで、メソッド名が競合してしまう可能性
可読性
- オーバーライドできるということは、メソッドがオーバーライドされていないか?を確認する必要がでる => 可読性が下がる
例
class Hoge
include Hoge::XXXable
include Hoge::ZZZable
private
attr_accessor :hoge
def initialize(hoge:)
@hoge = hoge
end
def connect
Service.call(param: param)
end
end
module Hoge::XXXable
include ::YYYable
def param
{
hoge: hoge # <- ココ
}
end
end
このように対象のクラスにincludeされる前提のモジュール(切り出しモジュール)に対して
インスタンス変数を参照しているだけだがメソッド呼び出しにできます
これが
module Hoge::XXXable
include ::YYYable
def param
{
hoge: @hoge # <- ココ
}
end
end
となっていれば「includeして読み込まれるクラスのインスタンス変数をセットしている」とモジュール内のソースを見るだけでパッとわかります。
※インスタンス変数をほかのクラスやモジュールで直接触るようなものは書きたくない という意味であれば気持ちは分かりますが。
しかし、これがattrのgetterメソッドとなっていることによって、XXXable
のモジュールを見るだけだと、hoge
は一体どこで定義されているメソッドなのか?が分かりません。
そのため下記のどこか?を探す作業が発生してしまいます。
-
XXXable
内で定義されているか -
XXXable
にてincludeやextendされているモジュールの中 -
XXXable
をincludeしている側のクラスやモジュール -
XXXable
をincludeしている側のクラスやモジュールが読み込んでいるモジュール
結論
メリットで挙げた、保守がしやすくなる というのは上書きが簡単 という意味では当てはまりますが、
その必要性が現段階で想定できないようなケースであれば、逆に 保守がしにくく なっていないでしょうか?
使う際には こうしたデメリットが大きくなるケースではないか? を今一度考えながら使用するかどうかを検討していき
よりよいプログラムを書いていきたいなと思います