今回は「比較演算子はできるだけクラスにさせる」というお話をしていきます。少しオブジェクト指向的内容にもなってしまいますが,「比較」というテーマでもあるため,解説させていただきます。
GOOD:比較演算子はできるだけクラスにさせる
まず,次のようなメソッドがあったとします。引数がProductと,intのstockUseCountです。stockUseCountは「どのくらい在庫が必要か?」という意味とします。
//GOOD:比較演算子はできるだけクラスにさせる private void 比較演算子はできるだけクラスにさせる( Product product, int stockUseCount) { }
NOTBAD:クライアントコードで比較する
それではクライアントコードで比較する例を見ていきます。クライアントコードとは,使う側のコードという意味です。今回の例でいえばProductクラスのStock等の値を,Productクラス自身で使うのではなく,Productを保持している側が使っているような状況を,Productクラスのクライアントコードという言い方をします。Productクラスを使っている側ということです。
例えば次のようなコードを見てください。
private void 比較演算子はできるだけクラスにさせる( Product product, int stockUseCount) { //NOTBAD:クライアントコードで比較する if (product.Stock >= stockUseCount) //① { Console.WriteLine($"処理...{product.Stock}"); } }
①の部分でproduct.Stockの値とstockUseCountを比較しています。
ポイントは比較演算子「>=」の部分です。
比較演算子は「>=」でなくても,「>」でも「==」でもなんでも同じなのですが,要するに比較しているということが大事です。これをクライアントコード側で比較する場合,要するに,Productクラスではなく,Productクラスを使っている側でproduct.Stock>= stockUseCountのように比較するコードを書いてしまうと,こういった比較がいろいろなクライアントコードに散らばってしまいます。つまり,Productクラスを使うクラスというのは,商品マスター画面や受注画面,発注画面など,いたるところで使われる可能性があり,それぞれのクライアントコードで,在庫チェックの方法を「product.Stock>= stockUseCount」のように直接記述してしまうと,とあるクライアントコードでは「product.Stock> stockUseCount」のように「>=」の部分を「>」と間違えて書いてしまうクライアントコードも発生する可能性があります。ですので,こういった比較をクライアントコード側にさせるということは,非常にバグを生む可能性が高くなります。こういった比較演算子などを各クライアントコードに散らばって書かないようにするために,1か所にまとめてしまうというのが良いでしょう。
GOOD:比較演算子はクラスに任せる
比較演算子をクライアントコードに散らばって記述しないようにするために,その値を保持しているクラスに判断させてしまうのが一番良いやり方です。オブジェクト指向でいうところのカプセル化という発想がこれですが,ここではあまり難しく考えずに,値を持っているクラスが比較を担当すると考えてください。
ProductクラスにCheckStockを追加
今回の場合,ProductのPriceの値を比較したいわけなので,Priceの持ち主はProductクラスということになります。ですので,Productクラスの中で,Priceの比較をします。今回は在庫チェックがしたいわけですから,CheckStockなどという名前のメソッドを作成し,その中で①のように比較します。
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; } internal bool CheckStock(int stockUseCount) { return Stock >= stockUseCount; //① } }
このようにPriceを保持しているクラスで在庫チェックを行うことで,クライアントコードからすれば,処理を呼び出すだけで,在庫チェックができるので,クライアントコード側で「>=」で比較するのか「>」で比較するのかを悩むことなく,CheckStockを呼び出すだけで解決します。
クライアントコードからCheckStockを呼び出す
クライアントコード側は,次の①のようにCheckStockメソッドを呼び出します。
//GOOD:比較演算子はできるだけクラスにさせる private void 比較演算子はできるだけクラスにさせる( Product product, int stockUseCount) { //NOTBAD:クライアントコードで比較する if (product.Stock >= stockUseCount) { Console.WriteLine($"処理...{product.Stock}"); } //GOOD:比較演算子はクラスに任せる if (product.CheckStock(stockUseCount)) //① { Console.WriteLine($"処理...{product.Stock}"); } }
このようにすれば,どのクライアントコードが呼び出してもStockとの比較は「>=」で行われるので,アプリケーション全体が統一したロジックで安定します。少しオブジェクト指向的お話になってしまいましたが,こういったチェック処理等はよく出てくると思うので,比較やチェックは値を保持しているクラスにやらせるということを覚えておいてください。
#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_リーダブルコードまとめ