はじめに
日頃コーディングをする際、何を意識して書くべきでしょうか?
書いて動いたからOKで!とかでコーディングを終わらせていないでしょうか?
コーディングをする際、ちょっとしたことの積み重ねで保守性・可読性が一気に高くなるものです。
以下に基本的なコーディングスキルをまとめていきます。
命名規則を守っているか
※現場や担当システム、使用している言語によって異なる部分があります。
様々な記事が出ておりますが、まずは命名規則に関して参考記事から引用します。
キャメルケース (ローワーキャメルケース)
複合語の先頭を、小文字で書き始める。
例) getInputReader
パスカルケース (アッパーキャメルケース)
複合語の先頭を、大文字で書き始める。
例) GetInputReader
スネークケース
アンダースコア (_) を区切記号として単語をつなげる。
例) quoted_printable_encode
チェインケース
ハイフン (-) を区切記号として単語をつなげる。
例) Get-Process
私の担当していたシステム(C#)では、以下の通りでした。
- フィールド名:キャメルケース
- プロパティ名:パスカルケース
- 変数名:キャメルケース
- メソッド名:パスカルケース
- クラス名:パスカルケース
- 定数値:コンスタントケース(全て大文字、区切りはアンダースコア)
- 名前空間:パスカルケース
- プロジェクト名 :パスカルケース
まずは、この命名規則になっているかを確認します。
参考文献
変数名の命名規則/**ケースの使い分け
こちらにより詳しく書いてあるのでご参照ください。
認識齟齬が起こらない命名ができているか
-
命名は省略せず、具体的に記載する
- 消費税を例にします
【TAX】とするだけでは何の税なのかわかりません。
法人税、相続税、自動車税など日本には様々な税が存在します。このような場合はしっかり【COMSUMPTION_TAX】と命名してあげるべきです。
- 消費税を例にします
-
コンテキストに沿ったものにする
-
価格を例にします
一口に「価格」と言っても、コンテキストによって意味合いが変わってきます。
例)原価、税抜価格、税込価格、割引価格
これらが変数の使い回し等でコーディングされていたら、分からなくなってしまいます。
この変数に再度値を代入することを再代入と言います。
その時点における「価格」はどんな価格であるのかを考えて記載すると意図が伝わりやすくなります。- 原価:Cost
- 税抜価格:NonTaxedPrice
- 税込価格:PriceIncludingTax
- 割引価格:DiscountedPrice
-
-
より意図が伝わりやすい単語選びを意識する
- 英語は似たような言葉がとても多いですし、微妙にニュアンスが変わってきます。
安易な命名だと読み手に思わぬ印象を与えてしまう可能性すらあります。
なので、私は和英辞典で単語を検索したり、類語を調べたりして相応しい単語を選ぶようにしています。
以下の参考文献に詳しく書いてありますので、こちらもご参照ください。
コーディングの命名規則一覧
- 英語は似たような言葉がとても多いですし、微妙にニュアンスが変わってきます。
サンプルコード
- 定価から税込価格を計算するメソッドで考えていきます。
/// <summary>
/// 定価から税込価格を計算します
/// </summary>
/// <param name="price">定価</param>
public int Calculate(int price)
{
decimal price2 = price * (decimal)1.1;
price2 = Math.Round(price2, MidpointRounding.AwayFromZero);
return (int)price2;
}
- コメントがない場合、[Calculate]という単語から[定価から税込価格を計算する]こと分かるでしょうか?
- コメントがない場合、[price]という単語から[定価]と分かるでしょうか?
- [price][price2]としただけで、どんな価格なのか分かるでしょうか?
- [1.1]にどんな意味があるかすぐ分かりますか?
これを踏まえてより伝わりやすいように改良します。
/// <summary>
/// 定価から税込価格を計算します
/// </summary>
/// <param name="nonTaxedPrice">定価</param>
public int CalculatePriceIncludingTax(int nonTaxedPrice)
{
decimal COMSUMPTION_TAX = (decimal)1.1;
decimal priceIncludingTax = nonTaxedPrice * COMSUMPTION_TAX;
//AmountRounded:四捨五入した金額
decimal AmountRounded = Math.Round(priceIncludingTax, MidpointRounding.AwayFromZero);
return (int)AmountRounded;
}
これでコメントが最悪なくても伝わるくらいの情報量になりました!
※列挙体:MidpointRoundingについて
MidpointRounding 列挙型
ifのネストが深くなっていないか
基本的に現場で改修をする際、if文の中にif文をネストで入れるだけで改修終わり!といった事案をよく見かけますが、
ifのネストが深くなると可読性を落とし、バグを引き起こしやすくなります。問題点は以下のようなものがあげられます。
- どこまでがifブロックなのかわからない
- 処理を正しく追うのに時間がかかる
- どんな条件においてどの処理になるかが見えにくい
これらはガード節の考え方を用いることで、ネストを解消し可読性を上げるのがよいとされています。
- ガード節:処理対象外のものを先に持ってきて、return,continue,breakなどで抜ける考え方。
サンプルコード1
public string FizzBuzz(int number)
{
string result = string.Empty;
if (number % 3 == 0 && number % 5 == 0)
{
result = "FizzBuzz";
}
else if(number % 3 == 0)
{
result = "Fizz";
}
else if(number % 5 == 0)
{
result = "Buzz";
}
else
{
result = "NotFizzBuzz";
}
return result;
}
//最後まで読まないと何が返ってくるかわからない。
public string FizzBuzz2(int number)
{
if (number % 3 == 0 && number % 5 == 0)
{
return "FizzBuzz";
}
if (number % 3 == 0)
{
return "Fizz";
}
if (number % 5 == 0)
{
return "Buzz";
}
return "NotFizzBuzz";
}
//最後まで読まずとも返却値が分かるようになった。
サンプルコード2
public void DoSomething()
{
//3つ条件があったとします
bool a = true;
bool b = true;
bool c = true;
if (a)
{
// 処理1
if (b)
{
//処理2
if (c)
{
//処理3
}
}
}
}
//このくらいであればわかりますが、それぞれの処理が100行あったとしたらよくわからなくなってしまいますよね
public void DoSomething()
{
//3つ条件があったとします
bool a = true;
bool b = true;
bool c = true;
if (!a)
{
return;
}
// 処理1
if (!b)
{
return;
}
//処理2
if (!c)
{
return;
}
//処理3
}
//条件外のものを先に処理して早期リターンすることで可読性が高くなりました。
bool値の変数名にIs/Should/Canなどをつけているか
bool値の変数名を命名する際に重要なのは、変数名だけ見てどんな場合にTrue/Falseになるのかが分かることです。
- ユーザーが削除されたかどうか?
- 【UserDeleteFlg】
- どんな場合にTrue/Falseかが分からない
- 【IsUserDeleted】
- これならTrue/Falseの場合の意味が明確になる
- 【UserDeleteFlg】
まとめ
コーディングスキルは小さなことの積み重ねで保守性や可読性が少しずつ高くなってきます。大規模なリファクタリングができなくても、まずは小さなところから改善していくことがエンジニアとして大事になってくるのではないでしょうか。
以下、参考書籍です。
リーダブルコード
良いコード/悪いコードで学ぶ設計入門 ―保守しやすい 成長し続けるコードの書き方
プリンシプル オブ プログラミング3年目までに身につけたい一生役立つ101の原理原則