はじめに
この記事では、
- 設計においての命名の重要性
- 関心事を小さく目的に特化して分けることの重要性
- 命名と小さく分けることとの関係性
これらについて、
9年間ほど現場で設計やリファクタリングをしてきた自分なりの体験や観点、
現場で役立つシステム設計の原則第1章 小さくまとめてわかりやすくする
を参考に図などを使ってまとめました。
新卒や社内でもこの内容を共有していく予定で、
特に駆け出しエンジニア・プログラマーの方達には
早い段階で理解して頂きたい内容です。
現場で役立つシステム設計の原則を読んでの気づきなどのまとめ記事を作ったので
こちらも合わせてご覧ください。
設計の目指すところ
システム開発が、
もし納期を守って納品して終わりであれば、
性能要求や機能要求を満たせて動きさえすればOKで、
設計なんて頑張ってやらなくてもいいのかもしれません。。。
(まぁそれは言い過ぎかもですが、)
しかし、
最近のほとんどのシステム開発においては、
(使い物にならず即捨てられるケースを除けば)
リリースしたらそこからは保守開発をほぼ必ず行い続けることになります。
設計は作ってリリースして終わりでなく、
そこからさらに保守開発し続けることにフォーカスしていると言ってもいいのかもしれません。
設計はリリースでなく保守開発を見据えているイメージ
設計の善し悪しは、ソフトウェアを変更するときにはっきりします。
出典:現場で役立つ設計の原則 第1章(増田 亨)
もし設計をテキトーにやってソースコードが整理整頓されておらず、
システムリリースをしてしまうと、
機能追加したり仕様変更といった保守開発の作業がカオスになります。
リリース時はバグっていなくても、
その先の保守開発でバグる可能性が非常に高くなったり、
保守開発の際に非常に危険でめちゃくちゃ時間がかかったり・・・
そうならないように、きちんとソースコードを整理整頓して、
どこに何があるかわかりやすくする必要があり、
ソースコードを整理整頓するために、
設計のいろんなテクニックがあるのだと僕は思います。
個人的にお気に入りのところ・・・
ソースコードを整理整頓して、どこに何が書いてあるかわかりやすくすることが設計の基本です
出典:現場で役立つ設計の原則 第1章(増田 亨)
設計において命名と小さく分けること関係と重要性
ここまで設計が目指すところについて見てきたので、
ここからは設計において最も基本で重要となる(個人的にそう思う)
命名と関心事を分けることの重要性について見ていきます。
命名が微妙だと後から見た人が混乱したり、
略語でamt(金額を表してるつもり)より
amountの方がわかりやすい。
と、ここまでは当たり前じゃんってなると思いますが、、、、
もしクラス名にふわっとした命名をすると、
そのクラスにふわっといろんな関心事が集まってくる。
その結果、いろんな関心事が歪に1箇所に集まることで、
分岐やフラグ、パラメータやモードなどで処理がカオスになってくる。
その結果、変更の影響範囲が危険で厄介になる。
といったことに陥ります。
(現場でリファクタリングや設計をするとこういうケース本当によく見かけます)
例えば、変数名がふわっとしてしまうと
最悪、色んな意味で1つの変数が使い回されてしまったりします。
(そもそもimmutableにしとけよってのは一旦置いておいて・・・)
var amount = 1000.0
val fee = 50
val tax = 0.1
amount *= (1 + tax)
amount += fee * (1 + tax)
val amount = 1000.0
val feeIncludeTax = fee * (1 + tax)
val amountIncludeTax = amount * (1 + tax)
val billingAmount = amountIncludeTax + feeIncludeTax
例えば、クラス名がふわっとしてしまうと
全然関係のない関心事のメソッドがそこに集りがちです。
商品というふわっと命名のせいでいろんな関心事が集まってしまった例
data class Goods(
・
・
・
){
fun shippingLogic(){・・・}
fun orderLogic(){・・・}
}
data class ShippingGoods(
・
・
・
){
fun orderLogic(){・・・}
}
例えば、メソッド名がふわっとしてしまうと
一つのメソッドの中でいろんな処理をする神メソッドになりがちです。
fun transaction(mode: String) {
when(mode) {
"1" -> // オペレーターの取引
"2" -> // エンドユーザの取引
"3" -> // 店舗担当者の取引
else -> // エラー
}
}
fun operatorTransaction() {
// オペレーターに特化した取引の処理を書く
}
などなど・・・
こういうモノが出来上がると、
歪にいろんな関心事が絡まって1箇所に集まるので、
仕様変更や機能追加の際に
影響範囲がとてつもなく広かったり、
プログラマーが混乱しやすく、バグが発生しやすくなり、
変更時の影響調査も大変になります。
こうならないように、
とある小さな目的に特化してクラスやメソッドを命名するのが重要です。
目的から外れるもの(命名と関係ない目的のもの)は
別のクラスやメソッドして切り出し、
どこに何が書いてあるかを整理して保守しやすく、
影響範囲が歪に波及しないように意識し続けることも重要です。
そうすることで、
強く関連するものが1箇所に集まり、
どこに何があるかの整理整頓もしやすくなります。
命名が設計(どこに何が書いてあるか整理)の基本であり、
いろんな勉強会で登場するくらい重要
であることがここまで見ると駆け出しエンジニアの方達にも
納得できるかなと思います。
ここからはおまけで、
現場のリアルな話で・・・
リリース当初は綺麗に命名できて関心事を小さく分けて整理できていても、
保守開発で仕様変更や機能追加を重ねるごとに当時のクラスやメソッドに
徐々にいろんな意味を持たせてしまって結果的に命名がぼやける。
と言うこともよくあります。
(自分の経験ではこっちの方が陥りやすい印象)
目の前の仕様変更や機能追加の対応を、
リリース当時の命名の意図を無視して
(人の入れ替えも激しく引き継ぎもろくにしなくて)、
とりあえず処理をコピーしたり分岐ちゃちゃっと増やして
その場しのぎの改修を繰り返してる現場では良くこれになります。
その結果、
リリース当初は小さく目的に特化した命名やメソッド、クラスだったものが、
いつの間にかいろんなフラグや分岐が入り混じってきて
パラメータによっていろんなモードで動く巨大メソッド
みたいなものが完成してしまいます。
まとめ
1つの小さな目的に特化した命名をクラスやメソッドにすることで、
関心事を小さくメソッドやクラスに分けることに繋がる。
その結果、
変更時の影響の波及を小さい範囲に抑えることに繋がる。
だから命名をふわっとやっちゃダメ!!
現場で役立つシステム設計の原則を読んでみて、
今後現場で取り入れたいこと、気付きなどなど
まとめ記事を作ったので是非こちらも見てください!!
おまけ
ドメイン駆動設計についても学習した内容をまとめているので、
興味がある方は是非こちらの記事も読んでみてください!!
ドメイン駆動設計でモデリング〜サンプルプログラム・テストコード作成(まとめ記事)
参考文献