リーダブルコード

C#リーダブルコード #03_右に長いコードを書かない_隣のとなりまでしか訪ねない

まずは基本的な話からやっていきたいと思います。最初の項目は「右に長いコードを書かない」というテーマです。

右に長いコードを書かない

先ほど作成したReadableCodeクラスにメソッドを作成します。メソッド名は「右に長いコードを書かない」です。

//基本
private void 右に長いコードを書かない()
{
}

日本語のメソッド名には違和感があるかもしれませんが,メソッド名が解説と関係ない場合は,ReadableCode01Aなどとするよりは,どういった解説のためのコードかを理解してもらうためにも,日本語で内容を一言で示したメソッド名の方が,後で観た場合にわかりやすと思うので,日本語にしています。もちろん,後述する,メソッド名の命名の仕方や,メソッド名に意味を持たせる場合は,英語で適当な名前を付けますが,メソッド名に意味がない場合は,日本語のメソッド名にしていきたいと考えています。

それでは「右に長いコードは書かない」の話をしていきたいのですが,その前に,コードから呼び出すためのクラスやメソッドが無いと,サンプルコードを記述することができないので,準備として,次のようなクラスを事前に作成します。

Productクラスの作成

新しいクラスのファイル「Objects.cs」などを作成し,そこに次のようにProductクラスを作成します。

public sealed class Product
{
    public Product(int productId,
                    string productName,
                    int price,
                    int stock)
    {
        ProductId = productId;
        ProductName = productName;
        Price = price;
        Stock = stock;
    }

    public int ProductId { get; }
    public string ProductName { get; }
    public int Price { get; }
    public int Stock { get; set; }
}

ProductDtoクラスの作成

続いてProductDtoクラスを作成します。

public sealed class ProductDto
{
    public ProductDto(Product product)
    {
        ProductId = product.ProductId.ToString();
        ProductName = product.ProductName;
        Price = product.Price.ToString();
        Stock = product.Stock.ToString();
    }

    public string ProductId { get; }
    public string ProductName { get; }
    public string Price { get; }
    public string Stock { get; }
}

先ほど作成したProductクラスを全部stringで保持するクラスです。これは後程Productクラスの変換のために使用します。

Productクラスのリストを返却するクラスの作成

続いて,Productクラスのリストを返却するクラスを作成します。

public static class ProductSqlServer
{
    public static IEnumerable GetProducts()
    {
        var result = new List();
        result.Add(new Product(10, "p10", 100, 2));
        result.Add(new Product(20, "p20", 200, 6));
        result.Add(new Product(30, "p30", 300, 4));
        return result;
    }
}

これはSQLServerに接続してProductクラスのリストを返却するようなクラスのイメージにしてありますが,今回の解説では,実際にSQLServerには接続するコードまでは書かないので,ダミーの値を返却する形で実装しています。

右に長いコードを書かないメソッドにコードを追加

ここまでで,サンプルコードを記述する準備が整ったので,ReadableCodeクラスに,コードを書いていきます。

private void 右に長いコードを書かない()
{
    var dtos = new List();
    ProductSqlServer.GetProducts().Where(x => x.Price > 100).ToList().ForEach(x => dtos.Add(new ProductDto(x)));
}

 

紙面の都合上折り返されていますが,実際には改行コードなしでコーディングされていると考えてください。これはあえて右に長くなるように,たくさんドットをつけて,無理やり長くなるように記述しました。このように右に長いコードを書くと,行数としては1行で書けるかもしれませんが,右にスクロールしながら,さらに,いくつものオブジェクトと処理を記憶しながら読んでいくことになり,初めてコードを読む人や,作り手以外の人が読む場合は特に,読むのがしんどいコードになります。

少ない行が良いコード?

コードは短い方がいいということを誰かに教わっているせいか,少ないコードが良いコードだと思っているプログラマーの方がいます。確かにコード量を減らすことは良いことではあるが,1行で書いたからコード量が減っているということではない,ということがご理解いただきたいと思います。それに,1行が長くなると,横スクロールが発生しますね。スクロールはマウスホイールで行う関係上,横にスクロールするよりも縦にスクロールする方が断然楽です。横スクロールはカーソルをスクロールバーに合わせて左右にスクロールしなければいけませんが,縦スクロールはマウスホイールを使って一瞬でスクロールできますよね,なのでコードが右に長くなるくらいなら,縦に長くなる方が,よほど読みやすいということになります。

何行で書くかではなく何秒で読めるか?

コード量を減らすことにとらわれて,コードの行数を減らそうとして,前述のような右に長いコードが読みづらいことは解説した通りですが,ポイントは,何行で書くかではなく何秒で読めるか?ということです。行数を減らすのではなく,初めてコードを読んだ人が,何秒で理解できるか?ということを意識してコードを書くことが,読みやすいコードを書くことにつながります。ですので,コードの行数が減ったかどうかではなく,理解に必要な秒数が減っているかどうかをチェックしてみてください。

隣の隣まで

もう一度右に長いコードを確認してみましょう。

private void 右に長いコードを書かない()
{
    var dtos = new List();
    ProductSqlServer.GetProducts().Where(x => x.Price > 100).ToList().ForEach(x => dtos.Add(new ProductDto(x)));
}

このコードを読む場合,何が読みづらいかというと,頭の中,つまり,コードを読む人間の脳みその中に貯めておかないいけないことが多いということが問題です。まずProductSqlServerGetProducts()に対してWhereを行って,Price100より大きい値で,それをListに変換して,ForEachで回してDTOに変換してdtosにAddしている。という感じで,いくつものオブジェクトや処理を読みながら,頭に記憶しておかないといけません。人間が一度に認識できる数は,数字は7つ,文字は6つ,単語は5つと言われています。それ以上のことを頭に入れると,覚えていたことが1つ抜けていくということです。これも個人差があるので,値は前後するので,基本的に,コードの記述は「隣の隣まで」くらいがストレスなく読めるコード量だといえます。要するに,ドット(ピリオド)の数が2つになる程度が適当だといえます。もちろん,ドットの数は少なければ少ない方が読みやすいのですが,長くなっても隣の隣くらいまでが良いでしょう。今回のケースであれば,ProductSqlServerから始まるコードなので,隣のGetProduct(),その隣のWhereくらいまでが適当です。

ProductSqlServer.GetProducts().Where(x => x.Price > 100)

これ以上続くと,最初に何をしていたかを忘れ始めて,結局前後しながら読むことになり,余計に時間がかかります。ということでこの「右に長いコードを書かない」という項目をBADコードとして記述しておきます。

private void 右に長いコードを書かない()
{
    var dtos = new List();
    //BAD:右に長いコードを書かない(隣のとなりくらいまでにする)
    ProductSqlServer.GetProducts().Where(x => x.Price > 100).ToList().ForEach(x => dtos.Add(new ProductDto(x)));
}

このようにコメントにBADやGOOD,NOTBADを記載することで,後で見たときにわかりやすいと思うので,こんな感じで直接コードに書いていきます。本書の最終版をチームに新人が入ってきたタイミングで,全部読んでもらえば,チーム全体で統一した考え方でコーディングができる等の使い方もできると思います。もちろん,すべて私の考えに従っていただく必要はないので,考えが異なる箇所は,書き換えた状態で,全員に共有すればよいと思います。

コードの評価

コードの評価は前述した通り,次の3つです。

  • BAD:良くない書き方
  • NOTBAD:別に悪くはない
  • GOOD:いい書き方,推奨

BADとGOODはそのままですが,NOTBADは,複数の書き方がある場合等,どちらで書いてもいいということもあったり,さほどいい書き方ではないが,BADというほどではない場合もあるので,NOTBADという評価も含めています。

今後はこんな感じで,コードを評価しながら解説を進めていきます。

リーダブルコードC#

#01_はじめに

#02_プロジェクトの作成
#03_右に長いコードを書かない_隣のとなりまでしか訪ねない
#04_隣のとなりまで_右スクロールより縦スクロールの方がいい
#05_IFとELSEがある時は肯定系をIF否定形をELSEにする
#06_比較する時は変数を左_定数を右にする
#07_複数の比較を1回のif文でやらない
#08_booの比較でTrueやFalseを書かない
#09_否定の否定はしない
#10_型チェックはasを使う
#11_メソッドはできるだけ早く抜ける_返却する値を無駄に変数に入れない
#12_対象外の時はすぐに抜ける
#13_都合が悪いケースはガードする
#14_必ずやりたい処理はfinallyを使う
#15_比較演算子はできるだけクラスにさせる
#16_ifの中括弧の省略はしない
#17_if文のリーダブルコードまとめ
#18_名前の付け方
#19_意図が明確な名前を付ける
#20_名前は素直に付ける_連想ゲーム的な名前を付けない
#21_1つの事しかしていなければ短い名前でも理解できる
#22_長いクラス名の扱い方
#23_単数形と複数形で表現する
#24_対になる言葉の組み合わせを決めておく
#25_業務で使う名前は統一する
#26_名前を統一するための辞書ツール作成
#27_メンバー変数にアンダーバーを付ける
#28_ハンガリアン記法を使わない
#29_メソッド内の変数をメソッド最初に全部宣言しない
#30_メソッド内の変数は直前に宣言する
#31_ループの変数はループ内で宣言する
#32_変数を使いまわさない
#33_boolの戻り値はどちらがTrueかをわかるようにする
#34_解放が必要なオブジェクトにはusingを使う
#35_varを推奨する場合
#36_メソッド名の付け方
#37_voidとFunctionを意識する
#38_インテリセンスを意識した名前にする
#39_生成メソッドはCreate_型変換はToを使う
#40_無駄に変数に入れて返却しない
#41_重複をなくす
#42_リージョンで区切らない
#43_アクセス修飾子とsealedを付ける
#44_クラス名はソリューションエクスプローラーで並べることを意識する
#45_クラス名は名詞か名詞句で命名する
#46_クラス名で継承元や特性を表現する
#47_メソッド内にコメントを書かない
#48_分かりづらい部分はメソッド化をしてメソッド名で想いを伝える
#49_コードを読んだ人が「えっ?」と思うことが予想される場所にだけコメントを付ける
#50_コメントで悪いコードを取り繕うことはできない
#51_未実装機能はTODOコメントを書く
#52_リーダブルコードまとめ