はじめまして! 新米iOSエンジニアのMasaです。今回は、僕が業務でプログラムを書くようになってから、1~2年で受けたコードレビューやフィードバック、実装経験を元に、 綺麗なコードを書くTips を紹介していきたいと思います。
綺麗なコード
とは?
そもそも綺麗なコード
とは、どんなコードなのでしょうか? 僕が考える綺麗なコードの要素を以下に挙げます。
-
可読性が高い
- コーディング規約を導入する
- 分かりやすい命名
- 深いネストを避ける
-
再利用性がある
- ソフトコーディング
- アーキテクチャに則って責務分けを行う
- 疎結合にする(= 処理の干渉範囲が狭く、変更/拡張がしやすい)
コーディング規約の導入
どんな遊びにもルールが存在するように、プログラムを組むときのルール = 規約を導入しましょう!
規約を導入すると、例えば分岐処理を記述する際、どんな基準でif/guard/switch/三項演算子を使い分けるか/関数や変数の命名方法が明確になります。
参考として、以下に有名な規約を載せます。
-
Swift
-
Ruby
-
Python
また、規約に書いてないことは、各言語の仕様に基づいて考えられるとベストなので、公式ドキュメントは目を通しておくと良いです!
swiftの場合
分かりやすい命名
例えば、新規会員登録をする際に、ユーザー名がダブっていたり不正な値が使われていないかをチェックし、検証結果をbool値で返す関数を次の様に定義するとします。
not preferred
func isValid(_ userName: String) -> Bool
上記の記述だと、以下の様な問題点が考えられます。
- メールアドレス/パスワードのvalidationと区別がしにくい
- コードを読む際に、isValid()が何をvalidしているのか一目で分からない
そこで、関数や変数の命名をする際は 出来るだけ多くの情報量を盛り込みましょう
preferred
func isUserNameEnable(_ userName: String) -> Bool
その他の分かりやすい命名の仕方としては、
- 最大値/最小値を表す際は
max/min
- bool値を扱うときは、
is/has/should/can
を先頭につける(ex:isEnabled
)
等があります。
より詳細に知りたい方は、リーダブルコードを読むとよく分かると思います。
深いネストを避ける
前述のユーザー名チェックで以下の様なロジックが使われていたとします。
not preferred
func isUserNameEnable(_ userName: String?) -> Bool {
// 今回はuserNameのnil判定と重複だけチェックします。(本当は不正文字の検出なども行った方が良い)
if userName.isNotNil {
if userName != "" {
// 初心者向けなのでAPI処理は今回は書きません。 responseに入力したuserNameが含まれていればtrueを返す関数だと思ってもらえると
if userNameAPIResponse.contains(userName) {
return false
} else {
return true
}
} else {
return false
}
} else {
return false
}
}
わざとネストを深く書きました。こんなに深いネストのコード読みたくないし、間違える可能性高くなります・
ネストは絶対に浅い方が読みやすいので、あの手この手を使ってなんとか浅くしましょう。
-
String?
がnil
か""
かを判別する処理を切り出す(他のところでも使いそうなところは共通化する) - 不要な
else
は削除する - guard節で早期return
等で、以下の様に書くことができます。
func isUserNameEnable(_ userName: String?) -> Bool {
if isStringNotNilOrEmpty(userName) {
return !userNameAPIResponse.contains(userName)
}
return false
}
func isStringNotNilOrEmpty(_ string: String?) -> Bool {
// string?.isEmpty == falseでも良いが今回は分かりやすい様にif letを使用
if string.isNotNil {
return string != ""
}
return false
}
ソフトコーディング
ソフトコーディングとは、プログラム上で使われる値を定数/変数に格納することです。
ソフトコーディングをしない場合、以下の様なデメリットがあります。
- 値に変更が加わった時に修正範囲が膨大になる
- プログラム中に
1.10
のような値(マジックナンバー)が出てきた際に、何を指しているか分からない
例えば、商品の合計金額を入力したら、消費税込みの値を算出するプログラムがあるとします。
not preferred
totalPrice = sum([stuff]) * 1.1
ここで1.1
と定義してしまうと、将来消費税が増税した時に、いちいち1.1
を変更しなければならず、面倒です。
また、消費税額のみを計算したい時、税抜き前の定価からセールで半額になった時等様々なケースでややこしくなりそうです。
そこで、なるべく値は以下のように格納しましょう。
preferred
let consumptionTax = 0.1
let includingTaxPriceRatio = (1 + consumptionTax)
totalPrice = sum([stuff]) * includingTaxPriceRatio
アーキテクチャに則って責務分けを行う
コードの再利用性を高めるために、アーキテクチャに則ってコードを分割しましょう。
- MVC
- MVVM
- Clean architecture
などの有名どころ抑えておくといいと思います。(俺的アプリケーション・アーキテクチャまとめ in Swift)
参考: iOSアプリ設計パターン入門
疎結合にする
コードとコードの依存関係/ファイルとファイルの依存関係は
- 依存関係は双方向ではなく、単方向にする
などの制限していきましょう(疎結合にすると言います。)
(基本的には、アーキテクチャにしっかり分けていけば自然と疎結合なコードに近づくと思いあmす。)
詳しくはこちらの記事がとても参考になります!
プログラムの依存関係とモジュール構成のこと
依存関係を限定しておくと変更可能性に強いコードになります。
アクセス修飾子で範囲を限定
private
/fileprivate
/public
などのアクセス修飾子を変数や定数、関数につけていき、公開範囲を限定させましょう。
終わりに
追加でこのTipsを入れた方が良い、もしくは内容が分かりづらい等ありましたらコメントいただけると嬉しいです!