mixiのコミュニティで挙がっていたトピックで、「Rubyって基本クラスを拡張できるから便利な機能はそこに追加できるけど、Java同様にUtilityとして別のクラスに分ける方法もあって、どちらかの採用に迷う」という旨のものがあった。
自分の考えをまとめる意味でブログのエントリとして書いてみる。
結論としては、「拡張にするとか、Utilityのような別の概念で括ってしまう前に、moduleにしておくのはどうだろうか」、です。
要するに大切なのは言語としての文脈なんだと思う。
僕の場合、基本クラスかどうかによらず、言語であることを強く意識して価値基準は以下のようにする。
例えば、以下のコードはシーザー暗号の機能を色々な方法で記述したもの。
(まだ勉強中の身なので他にこんな書き方がある、こっちの方が良いってのがあったら教えてください!)
この場合、文字列について暗号処理するのであれば、拡張の方がその文字列を主語とした時の文脈がうまく成立するだろう。
あとはこのシーザー暗号という言葉が、このコード以外のコードに対する文化としてどれだけ重要なものかだ思う。
その観点から行くと、クラス構造を決定せず、ひとまず機能だけのくくりでまとまりを作れるmoduleの考え方はとても良いのかもしれない。
これをUtilityと呼ぶ人はUtilityと呼んでも良いと思う。
新しい暗号アルゴリズムは以下で言うEncryptionの拡張として記述すればよいし、アプリケーションが文字列に対して暗号化することを日常語として要求するような性質のものであれば、基本クラスの拡張とすることを後から選ぶことができるのではないだろうか。
基本クラスそのものの拡張がどうしても懸念されるのであれば、特異メソッドという選択肢も出てくる。
自分の考えをまとめる意味でブログのエントリとして書いてみる。
結論としては、「拡張にするとか、Utilityのような別の概念で括ってしまう前に、moduleにしておくのはどうだろうか」、です。
要するに大切なのは言語としての文脈なんだと思う。
僕の場合、基本クラスかどうかによらず、言語であることを強く意識して価値基準は以下のようにする。
- そのオブジェクトやインスタンスが主語として成り立つものかどうか
- そのメソッドが汎用的な言葉として成り立つものか
例えば、以下のコードはシーザー暗号の機能を色々な方法で記述したもの。
(まだ勉強中の身なので他にこんな書き方がある、こっちの方が良いってのがあったら教えてください!)
この場合、文字列について暗号処理するのであれば、拡張の方がその文字列を主語とした時の文脈がうまく成立するだろう。
あとはこのシーザー暗号という言葉が、このコード以外のコードに対する文化としてどれだけ重要なものかだ思う。
その観点から行くと、クラス構造を決定せず、ひとまず機能だけのくくりでまとまりを作れるmoduleの考え方はとても良いのかもしれない。
これをUtilityと呼ぶ人はUtilityと呼んでも良いと思う。
新しい暗号アルゴリズムは以下で言うEncryptionの拡張として記述すればよいし、アプリケーションが文字列に対して暗号化することを日常語として要求するような性質のものであれば、基本クラスの拡張とすることを後から選ぶことができるのではないだろうか。
基本クラスそのものの拡張がどうしても懸念されるのであれば、特異メソッドという選択肢も出てくる。
module Encryption
def caesar(d)
v = ''
f = (d==:left ? proc{|a| a+1} : (d==:right ? proc{|a| a-1} : proc{|a| a}))
to_s.each_byte{|a| v += ("%c" % f.call(a))}
v
end
end
# 特異メソッド
x1 = 'def'
x1.caesar(:left) # => NoMethodError: undefined method
x1.extend(Encryption)
x1.caesar(:left) # => "efg"
x1.caesar(:right) # => "cde"
# 継承による拡張
class EncryptableString < String
include Encryption
end
# 基本クラスへの拡張
class String
include Encryption
end
# ユーティリティ
class Util
def self.caesar(str, d)
str.caesar(d) # 今回は面倒なのでこう書いておきます
end
end
x2 = 'def'
x2.caesar(:left) # => "efg"
x2.caesar(:right) # => "cde"
Util.caesar(x2, :left) # => "efg"
Util.caesar(x2, :right) # => "cde"
[フレーム]
コメントする