リーダブルコードを心がける
レールの上のルビーが好きな前田です。
梅雨で雨が続いています。
主な移動手段が自転車な私としては辛い時期になりました。
現在、リーダブルコード(オライリー・ジャパン)を再読しています。
進行中のプロジェクトで、メソッド名や変数名に悩むことが多かったので、その参考になればと再読している次第です。
命名の大切さ
忙しいから、めんどくさいから、とついつい命名に対して疎かになっていませんでしょうか。
プロジェクトスタート時におかしな名前を命名してしまうと、プロジェクトの途中で担当者が代わったりした時など、全く意味が分からなかったり、非常に扱いにくいデータとなってしまったりなど、弊害は計り知れません。
一番最初に名前やアーキテクチャをしっかり設計しておく、ということがメンテナンスしやすいWebシステムを作る上で非常に重要だということです。
Webシステムを作っていく上で決めていかなければならない重要な名前はたくさんあります。
データベース名、テーブル名、テーブルカラム名、メソッド名、変数名、などなど。
プログラミング言語はアルファベットで書いていくので、当然日本語から英語に変換しなければなりません。
どうしても該当が無い場合は日本語のアルファベットにするしかないですが。
本当にビシっと決まる名前を命名することは非常に難しいと思います。
弊社ではこの名前決めに非常に時間を割いて議論しているように思います。
一番重点を置いているのは、名前を見ただけでその役割を理解することができるような名前になっているか。
命名規則
リーダブルコードの中では命名の際に、下記のルールを守れと書いてあります。
①明確な単語を選ぶ
②汎用的な名前を避ける(あるいは、使う状況を選ぶ)
③抽象的な名前よりも具体的な名前を使う
④接尾辞や接頭辞を使って情報を追加する
⑤名前の長さを決める
⑥名前のフォーマットで情報を伝える
①〜④までは名前を具体的にする、ということ。
⑥については、RailsやRubyの規約がありますので、名前のフォーマットもそれらに従うのが良いと考えられます。
⑤については難しいところだなと思いました。
リーダブルコード文中では「長い名前を命名するのは問題じゃない」と言っています。
スコープが小さい時は短くても良い、ただしスコープが大きい時は、長さを優先させたほうが良い、という内容でしたが、弊社ではrubocopでコードをチェックしており、あまりメソッド名や変数名が長いと1ラインあたり80桁の制限に引っかかりやすくなり、無駄な改行などで余計にコードが見づらくなってしまう、という本末転倒なことになることが想像できるので、やはり短く具体的にする、というハードな命名センスが必要になってきます。
重要なのはやはり名前に情報を詰め込む、ということ。
コード修正
以上を踏まえてカタカナを扱うカタカナクラスのコードを修正してみました。
各メソッドの役割は以下の通りです。
-
include_kana_list?
与えられた文字(ローマ字)が、KANA_LIST
の各行の先頭文字をローマ字にしたキー値一覧['a', 'ka', 'sa', 'ta', 'na', 'ha', 'ma', 'ya', 'ra', 'wa']
に含まれているかどうかを返す。
例:'na'
→true
例:'ナ'
→false
'20'
→false
-
kana_list_line
与えられた文字(ローマ字)からKANA_LIST
の各行を取得する。
例:'a'
→['ア', 'イ', 'ウ', 'エ', 'オ']
例:'sa'
→['サ', 'シ', 'ス', 'セ', 'ソ']
-
alphabetical_head_string
与えられたカタカナ文字列の先頭文字が、50音のどの行(あ行、か行など)に所属しているか判断し、あ行なら'a'
、か行なら'ka'
といったローマ字で返す。
例:'ニシノミヤシ'
→'na'
例:'ゲロシ'
→'ka'
# カタカナクラス
class Katakana
KANA_LIST = { a: %w(ア イ ウ エ オ),
ka: %w(カ キ ク ケ コ ガ ギ グ ゲ ゴ),
sa: %w(サ シ ス セ ソ ザ ジ ズ ゼ ゾ),
ta: %w(タ チ ツ テ ト ダ ヂ ヅ デ ド),
na: %w(ナ ニ ヌ ネ ノ),
ha: %w(ハ ヒ フ ヘ ホ バ ビ ブ ベ ボ パ ピ プ ペ ポ),
ma: %w(マ ミ ム メ モ),
ya: %w(ヤ ユ ヨ),
ra: %w(ラ リ ル レ ロ),
wa: %w(ワ ヰ ヱ ヲ ン) }
def initialize(str)
@katakana = str
end
def self.include_kana_list?(param)
KANA_LIST.key?(param.to_sym)
end
def self.kana_list_line(param)
KANA_LIST[param.to_sym]
end
def alphabetical_head_string
KANA_LIST.each do |key, val|
return key.to_s if val.include?(@katakana[0])
end
nil
end
end
順に見ていきます。
3行目 KANA_LIST
実際にこのKANA_LIST
にアクセスすることはありませんが、アクセスすると、
Katakana.KANA_LIST
となり、kana
が続いています。
Katakanaクラスのリストは当然カタカナとなりますので、KANA_
を削除します。
18行目 include_kana_list?
3行目のKANA_LIST
に含まれているか、を判断するものとして、kana_list
としましたが、3行目をLIST
だけにしたので、こちらもkana
を削除します。
各行のキー値一覧
をline_keys
としますと、include_list_line_keys?
となります。
実際にメソッドを使う時はKatakana.include_list_line_keys?(chara)
となりますが、keys
というのは外から見た時には何のkey
かが分かりにくいかなと思いました。
そこで最終的にはinclude_line_head_charas?(chara)
としました。
22行目 kana_list_line
こちらもkana_list
を削除します。
line
だけだと分かりづらいので、ありふれていますがget_
を付けて、get_line
としました。
26行目 alphabetical_head_string
alphabetical
とはアルファベット順の
という意味です。
日本語の50音
、を英語に変換しているのですが、少し無理がありそうですね。
50音の各行の先頭文字
というのを表したかったのですが少し説明不足と誤解を与えてしまうことになるかもしれません。
alphabetical
を大胆に削除して、get_line_head_chara
とし、行の先頭文字を取得する
という意味を持たせることにしました。
その他
引数やメソッド名の中の文字列
はstr
、一文字
はchara
としました。
chara
といいながらもka
などを返していたりするのですが、このクラスはKatakana
クラスということで、あくまでも日本語の意味での一文字という意味でka
なども一文字とみなすことにしました。
整形後
# カタカナクラス
class Katakana
LIST = { a: %w(ア イ ウ エ オ),
ka: %w(カ キ ク ケ コ ガ ギ グ ゲ ゴ),
sa: %w(サ シ ス セ ソ ザ ジ ズ ゼ ゾ),
ta: %w(タ チ ツ テ ト ダ ヂ ヅ デ ド),
na: %w(ナ ニ ヌ ネ ノ),
ha: %w(ハ ヒ フ ヘ ホ バ ビ ブ ベ ボ パ ピ プ ペ ポ),
ma: %w(マ ミ ム メ モ),
ya: %w(ヤ ユ ヨ),
ra: %w(ラ リ ル レ ロ),
wa: %w(ワ ヰ ヱ ヲ ン) }
def self.include_line_head_charas?(chara)
LIST.key?(chara.to_sym)
end
def self.get_line(chara)
LIST[chara.to_sym]
end
def self.get_line_head_chara(str)
LIST.each do |key, val|
return key.to_s if val.include?(str[0])
end
''
end
end
まとめ
読みやすいコードを書くということは奥が深いですし、終わりが無い作業のようにも思います。
弊社ではプルリクエストでプロジェクトに携わる関係メンバーにコードレビューをしてもらいますが、何故その名前にしたのか、無駄は無いかなど、激しくツッコミを受けますので気を付ける癖が自然と身に付いてきたように思います。
大事なことは、日々考えながら少しでも良いコードを書けるように努力することだと思いますので、これからも頑張ります!
リーダブルコードを意識した保守性の高いシステム開発やモバイルアプリケーション開発をご検討の企業様は、是非MMMにご相談下さいませ!