概要
保守性を上げるためのコーディングの理論は、オブジェクト指向をはじめとして、いくつもあります。しかし、現場では理想とのギャップがありすぎて、「理論としては学んだが、現実に使うものではない」というような扱いを受けがちだと感じます。
0か100かではなく、まず1から少しずつやっていこうとした場合、何からやれば良いか。現場ですぐ取り入れやすく、かつ本質的に重要だと思う部分を切り出して紹介していきます。この記事は第一弾という感じで、反響次第で続けていこうと思います。
最初に結論まとめ
- ポイント1:仕様に出てくる要素に、日本語と英語で名前を付けましょう。コード上の関数・変数名を、その名前で統一しましょう。
オブジェクト指向を使おうが使うまいが、これは保守性を大きく左右する重要ポイントです。理論的なキーワードはたぶん、「ドメイン分析」「モデリング」といったあたりと近いでしょう。
なぜこの記事を書いたか
オブジェクト指向、UML、クラス図、シーケンス図、ドメイン駆動設計。たいていのプログラマは、こうした用語を聞いたことは有ると思います。しかし同時に、学問として学んだだけで、プログラムの現場では使っていないという人が多いのではないでしょうか。
その理由を聞いてみると、「時間の無駄だと思われている」あるいは逆に「一から作る案件で完璧にやらないと意味が無い」といった話が多いように思います。
私は、どちらも極端すぎる勘違いだと思います。部分的に取り組むだけでも、明らかに保守性が上がる効果が得られます。また、一部だけを改修する、何ならバグ修正をするだけの場合にも効果はあります。 ※ここでいう保守性は、いわゆる技術的負債を増やさず変更を容易にすることを指します。ログや監視などは指しません。
しかしこれらの勘違いにも無理はないと思います。こうした内容の解説書を読むと、何の役に立つかは説明されておらず、ただやり方だけが書かれていたりします。あるいは、要件定義から始めて全体を完全に理論通りやりきるという例しか載っていなかったりします。これでは、なかなかメリットもピンと来ませんし、理想の案件でしか取り込めないように見えてしまいます。
この隙間を埋めるチャレンジとして、理論から入るのではなく、まず現実的に取り入れやすくて効果が高いポイントを紹介しようと考えました。これなら、やったことがない現場での最初のチャレンジや、新人への説明にも使えます。順番にそれらを取り入れていくと、結果的にオブジェクト指向などの共通の理論に繋がっていることに気付き、理論にも興味が出る。という風になれば理想です。
初回の記事なのでだいぶ前置きが長くなりました・・・紹介するポイントよりも前置きの方が長かった気がします。具体的な話へ進みます。
ポイント1
仕様に出てくる要素に、日本語と英語で名前を付けましょう。コード上の関数・変数名を、その名前で統一しましょう。
具体例
ユーザー名一覧の情報を持った、DBがあるとしましょう。これを読む処理のメソッドと各処理のメソッドがあり、それらが共通して使うDB操作用のクラスがあるとします。
悪い例
これに対する、悪い例です。サンプルとして短く示すために、かなり極端な内容になってしまうのはご容赦。
//DBからユーザーを全て取得するメソッド string[] ReadDB(){ Database db; db.xxxx(); } //ユーザーを追加するメソッド void AddUserName(){ Database db; db.xxxx(); } //Databaseの読み書きをするクラス class Database { }
どうでしょう、名前がいまいちだなあ・・・と感じますかね?このコードでは、次のような問題点があります。
- AddUserName()で追加したものをReadDB()で読み込む事ができる、という対応関係が分かりづらい
- Read/Write(あるいはCRUD)とAdd/Remove/Getのような用語の組み合わせが混ざって使われている
- 日本語・英語共に、どのキーワードで検索しても、3つ全てが同時にヒットしない(改修対象を探す時に苦労する)
- ユーザー名だけを扱うDBに対して「Database」という一般名詞を付けてしまっているので、他にDBが増えると混乱する
これを見て、「アホか、こんなコード新人でも書かない」と思う人は、多分チーム全員がこの記事の内容をすでに理解しているので、スルーで良いと思います。
しかし、一からコードを書くと、意外にこういうコードを書いてしまう人は多いように思います。これは、「読み込みの処理」「書き込みの処理」をそれぞれコーディングしていき、その場でそれぞれの名前を考えているためだと思います。
良い例
まず、次のように名前を付けます。
仕様に出てくる要素・日本語名 | 英語名 |
---|---|
ユーザー名 | UserName |
ユーザー名を保持するDB | UserNameDatabase |
そして、コードをそれに合わせて直します。ついでに、Add/Remove/Getのような用語の組み合わせも直します。
//ユーザー名を全て取得するメソッド string[] GetUserNames(){ UserNameDatabase db; db.xxxx(); } //ユーザー名を追加するメソッド void AddUserName(){ UserNameDatabase db; db.xxxx(); } //ユーザー名を保持するDatabaseの読み書きをするクラス class UserNameDatabase { }
どうでしょう、ちょっとした違いですが、だいぶコードの見通しが良くなったと感じませんか?悪い例に対応させて具体的に書くと、次の通りです。
- AddUserName()で追加したものをGetUserNames()で読み込める、という対応関係が名前から分かる
- Add/Remove/Getで統一されている
- 「ユーザー名」や「UserName」での検索で、関連するコードがヒットする(改修対象の見当を付けやすい)
- ユーザー名だけを扱うDBに対して「UserNameDatabase」という個別の名詞を付けているので、他のDBが増えても重複しない
(興味のある人だけ)理論的には
名前を付けて整理するのは、ドメイン分析、あるいはドメインモデリングの考え方です。(この場合はビジネス領域の用語ではなく、かなりコード寄りの名前付けですが)
ユースケース駆動開発においても、クラス図やシーケンス図の作成へ進む前に、まずはドメイン分析でこうした名前付けの整理を行うべきだという話が出ています。
またそもそも、「対象領域にあるものに名前を付けることで、対象領域の変化をコードへ反映しやすくする」というのは、オブジェクト指向という考え方そのものでもあります。
まとめ
長々と書きましたが、実のところ「日本語名と英語名を付けて、コード上で名前を統一しましょう」しか言っていません。現場で取り入れたり新人に教えるとしても、無理なくすぐに使えるのではないでしょうか。
そうでありながら、これはオブジェクト指向などのめんどくさそうな理論の実践でもあります。しかも、その中でも特に効果が高いものです。オブジェクト指向とかはそんなに面倒なものじゃなく、意外と実用的な話なんだな、と思ってもらえたら嬉しいです。