マジックナンバーを避けるために、文字通り意味のある名前を付けるべきだとよく耳にします。といった:
//THIS CODE COMES FROM THE CLEAN CODE BOOK
for (int j = 0; j < 34; j++) {
s += (t[j] * 4) / 5;
}
-------------------- Change to --------------------
int realDaysPerIdealDay = 4;
const int WORK_DAYS_PER_WEEK = 5;
int sum = 0;
for (int j = 0; j < NUMBER_OF_TASKS; j++) {
int realTaskDays = taskEstimate[j] * realDaysPerIdealDay;
int realTaskWeeks = (realdays / WORK_DAYS_PER_WEEK);
sum += realTaskWeeks;
}
私はこのようなダミーメソッドを持っています:
説明:私が提供する人々のリストがあるとします。デフォルトでは、食料を購入するために5ドルを費やしますが、複数の人がいる場合、水と食料を購入する必要があるため、多分6ドルを費やす必要があります。私のコードを変更しますリテラル1に注目してください、それについての私の質問。
public int getMoneyByPersons(){
if(persons.size() == 1){
// TODO - return money for one person
} else {
// TODO - calculate and return money for people.
}
}
友達にコードを確認するよう依頼したところ、値1に名前を付けるとコードがわかりやすくなり、もう1つは値自体が意味があるので定数名は必要ない、と言いました。
したがって、私の質問はリテラル値1に名前を付ける必要がありますか?値がマジックナンバーである場合とそうでない場合は?コンテキストを区別して、最良の解決策ですか?
いいえ。その例では、1は完全に意味があります。
しかし、persons.size()がゼロの場合はどうなりますか? persons.getMoney()
が0と2では機能し、1では機能しないのは奇妙に思えます。
コードにその特定のリテラル値が含まれているのはなぜですか?
リテラル値にコンテキストから明確ではない意味がある場合、はい、定数または変数を通じてその値に名前を付けると役立ちます。後で、元のコンテキストが忘れられたときに、意味のある変数名を持つコードがより保守しやすくなります。コードの対象読者は主にコンパイラーではありません(コンパイラーは恐ろしいコードで問題なく動作します)が、そのコードの将来のメンテナーです。
最初の例では、34
、4
、5
などのリテラルの意味はコンテキストからは明らかではありません。代わりに、これらの値のいくつかは、問題のドメインで特別な意味を持っています。したがって、それらに名前を付けるのは良いことでした。
2番目の例では、リテラル1
の意味がコンテキストから非常に明確です。名前の紹介は役に立ちません。
実際、明白な値に名前を付けることは、実際の値を隠すため、悪いことにもなります。
これにより、名前付きの値が変更されたり、正しくなかったりした場合、特に同じ変数が無関係なコードで再利用された場合に、バグがわかりにくくなる可能性があります。
コードの一部は特定の値でも問題なく機能する場合がありますが、一般的なケースでは正しくない場合があります。不要な抽象化を導入することにより、コードはもはや明らかに正しくありません。
これは完全にコンテキストに依存するため、「明白な」リテラルのサイズ制限はありません。例えば。リテラル1024
は、ファイルサイズ計算のコンテキストでは完全に明白であるか、ハッシュ関数のコンテキストではリテラル31
、CSSスタイルシートのコンテキストではリテラルpadding: 0.5em
です。 。
このコードにはいくつかの問題があり、ところで、次のように短縮できます。
_public List<Money> getMoneyByPersons() {
return persons.size() == 1 ?
moneyService.getMoneyIfHasOnePerson() :
moneyService.getMoney();
}
_
なぜ一人が特別なケースなのかは不明です。一人からお金を稼ぐことは、複数の人からお金を稼ぐこととは根本的に異なることを示す特定のビジネスルールがあると思います。ただし、getMoneyIfHasOnePerson
とgetMoney
の両方を調べて、なぜ異なるケースがあるのかを理解する必要があります。
getMoneyIfHasOnePerson
という名前は正しくありません。名前から、私はこの方法で一人がいるかどうかを確認し、そうである場合は彼からお金を受け取ることを期待します。それ以外の場合は、何もしません。あなたのコードから、これは起こっていることではありません(または条件を2回実行しています)。
コレクションではなく_List<Money>
_を返す理由はありますか?
あなたの質問に戻ります理由なぜ一人の人に特別な扱いがあるのかは不明です、数字の1は定数に置き換える必要がありますnless別の方法があります明示的なルール。ここで、1つは他のマジックナンバーとそれほど違いはありません。特別な扱いが1人、2人、3人、または12人以上にのみ適用されることをビジネスルールに記載することができます。
コンテキストを区別して最適なソリューションを選択するにはどうすればよいですか?
あなたはあなたのコードをより明確にする何でもします。
次のコードを想像してみてください。
_if (sequence.size() == 0) {
return null;
}
return this.processSequence(sequence);
_
ここでゼロは不思議な値ですか?コードはかなり明確です。シーケンスに要素がない場合は、処理せずに特別な値を返します。ただし、このコードは次のように書き換えることもできます。
_if (sequence.isEmpty()) {
return null;
}
return this.processSequence(sequence);
_
ここでは、定数はなくなり、コードはさらに明確になります。
別のコードを見てみましょう:
_const result = Math.round(input * 1000) / 1000;
_
round(value, precision)
オーバーロードを持たないJavaScriptなどの言語で何が行われるかを理解するのにそれほど時間はかかりません。
さて、定数を導入したい場合、どのように呼び出されますか?取得できる最も近い項はPrecision
です。そう:
_const precision = 1000;
const result = Math.round(input * precision) / precision;
_
読みやすさは向上しますか?それは可能性があります。ここでは、定数の値はかなり制限されており、本当にリファクタリングを実行する必要があるかどうか疑問に思うかもしれません。ここでの良い点は、精度が1回だけ宣言されるようになったため、精度が変更されても、次のような間違いをするリスクがないということです。
_const result = Math.round(input * 100) / 1000;
_
一方の場所で値を変更し、もう一方の場所でそれを行うのを忘れています。
これらの例から、数値はすべての場合の定数で置き換える必要があるという印象を受けるかもしれません。本当じゃない。場合によっては、定数を使用してもコードの改善につながらないことがあります。
次のコードを見てください。
_class Point
{
...
public void Reset()
{
x, y = (0, 0);
}
}
_
ゼロを変数で置き換えようとすると、意味のある名前を見つけるのが困難になります。どのように名前を付けますか? ZeroPosition
? Base
? Default
?ここに定数を導入しても、コードはまったく強化されません。少し長くなりますが、それだけです。
ただし、このようなケースはまれです。したがって、コードで数値を見つけたときはいつでも、コードをリファクタリングする方法を見つける努力をしてください。数にビジネス上の意味があるかどうか自問してください。はいの場合、定数は必須です。いいえの場合、番号にどのような名前を付けますか?意味のある名前を見つけたら、それはすばらしいことです。そうでない場合、定数が不要であるケースが見つかる可能性があります。
単一のパラメーターを取り、4を5で割った時間を返す関数を作成して、最初の例を使用しながら、関数の動作に明確なエイリアスを提供できます。
私は自分の通常の戦略を提供しているだけですが、多分私も何かを学びます。
私が考えているのは.
// Just an example name
function normalize_task_value(task) {
return (task * 4) / 5; // or to `return (task * 4) / NUMBER_OF_TASKS` but it really matters what logic you want to convey and there are reasons to many ways to make this logical. A comment would be the greatest improvement
}
// Is it possible to just use tasks.length or something like that?
// this NUMBER_OF_TASKS is the only one thats actually tricky to
// understand right now how it plays its role.
function normalize_all_task_values(tasks, accumulator) {
for (int i = 0; i < NUMBER_OF_TASKS; i++) {
accumulator += normalize_task_value(tasks[i]);
}
}
私がベースから外れている場合は申し訳ありませんが、私は単なるJavaScript開発者です。私がこれを正当化した構成がわからない、私はすべてが配列またはリストにある必要があるとは限らないと思いますが、.lengthは多くの意味をなすでしょう。そして* 4はその起源が曖昧なので良い定数を作るでしょう。
その数1、それは別の数かもしれませんか? 2、3、または1でなければならない論理的な理由はありますか? 1でなければならない場合は、1を使用するのが適切です。それ以外の場合は、定数を定義できます。その定数をONEと呼ばないでください。 (私はそれが行われたのを見てきました)。
1分で60秒-定数が必要ですか?まあ、それはis 60秒であり、50または70ではありません。そして、誰もがそれを知っています。したがって、それは数にとどまることができます。
1ページあたり60アイテムが印刷されます-その数は簡単に59または55または70でした。実際、フォントサイズを変更すると、55または70になる可能性があります。そのため、ここでは意味のある定数をさらに求めます。
意味がどれほど明確かということも問題です。 「分=秒/ 60」と書くと、それは明らかです。 「x = y/60」と書いてもわかりません。どこかにsome意味のある名前が必要です。
絶対的なルールが1つあります。絶対的なルールはありません。練習して、いつ数値を使用するか、いつ名前付き定数を使用するかを理解します。本がそう言うのでそれをしないでください-それがなぜそれを言うのか理解するまでは。
DB検索での(変更された)OPのようなかなりの量のコードを見てきました。クエリはリストを返しますが、ビジネスルールでは要素は1つしか存在できないとしています。そしてもちろん、「これだけのケース」から、複数の項目を含むリストに変更されました。 (ええ、私はかなりの回数を言いました。それはほとんど彼らのようです... nm)
したがって、定数を作成するのではなく、(クリーンなコードメソッドで)名前を付けるメソッドを作成するか、条件の検出対象を明確にします(そして、条件の検出方法をカプセル化します)。
public int getMoneyByPersons(){
if(isSingleDepartmentHead()){
// TODO - return money for one person
} else {
// TODO - calculate and return money for people.
}
}
public boolean isSingleDepartmentHead() {
return persons.size() == 1;
}