カテゴリー
読みやすいコード

最後の例/A Final Example

この章の最後の例では、今まで説明した多くの原則を示す例を示したいと思います。
次のように配置された多数の入力テキストフィールドを持つWebページがあるとします。

<input type="text" id="input1" value="Dustin"> 
<input type="text" id="input2" value="Trevor"> 
<input type="text" id="input3" value="">
<input type="text" id="input4" value="Melissa"> 
...

ご覧のように、IDはinput1から始まり、そこからインクリメントします。
あなたの仕事は、文字列を受け取りページの最初の空の(この例では "input3")に置くsetFirstEmptyInput()という関数を書くことです。 この関数は、更新されたDOM要素を返す必要があります(空の入力が残っていない場合はnull)。 これを行うためのコードは、この章の原則を適用しないものです:

var setFirstEmptyInput = function (new_value) {
    var found = false;
    var i = 1;
    var elem = document.getElementById('input' + i);
    while (elem !== null) {
        if (elem.value === '') {
            found = true;
            break;
        }
        i++;
        elem = document.getElementById('input' + i);
    }
    if (found) elem.value = new_value;
    return elem;
};

このコードは仕事を終わらせるが、それほど美しいものではない。 何が問題なのですか?それをどのように改善しますか?
このコードを改善する方法はたくさんありますが、使用する変数の観点から検討します。

・var found
・var i
・var elem

これら3つの変数はすべて関数全体に存在し、複数回書き込まれます。 それぞれの使い方を改善しようとしましょう。
この章の前半で説明したように、発見されたような中間変数は、しばしば早期に返すことで排除することができます。 ここにその改善があります:

var setFirstEmptyInput = function (new_value) {
    var i = 1;
    var elem = document.getElementById('input' + i);
    while (elem !== null) {
        if (elem.value === '') {
            elem.value = new_value;
            return elem;
        }
        i++;
        elem = document.getElementById('input' + i);
    }
return null;
};

次に、elemを見てみましょう。 コード全体で複数回使用されていますが、その値を把握するのが難しい非常に「偽の」方法です。 コードは、elemが私たちが反復処理している値であるかのように見せます。 ですから、whileループをforループに再構成しましょう。

var setFirstEmptyInput = function (new_value) {
    for (var i = 1; true; i++) {
        var elem = document.getElementById('input' + i);
        if (elem === null)
            return null;  // Search Failed. No empty input found.
        if (elem.value === '') {
            elem.value = new_value;
            return elem;
        }
    }
};

特に、elemがどのように寿命がループ内に含まれる追記型変数として機能するかに注目してください。 true for aループ条件の使用は普通ではありませんが、代わりに、私は1行でiの定義と修正を見ることができます。 (伝統的な(真実)も合理的です。)

参考文献:

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック

The Art of Readable Code: Simple and Practical Techniques for Writing Better Code (English Edition)

The Art of Readable Code: Simple and Practical Techniques for Writing Better Code Kindle (English Edition)

カテゴリー
読みやすいコード

変数は一度だけ書き込む/Prefer Write-Once Variables

変数は一度だけ書き込む

これまでのところ、多くの変数がある「プログラム」を理解することが難しいことについて、この章では話し合いました。まあ、絶えず変化している変数について考えることはさらに困難です。 それらの値を追跡することは、さらに困難を生じさせます。
この問題に対処するために、少し奇妙に思えるかもしれない提案があります:追記型変数を好む。
「永続的なフィクスチャ」である変数は、考えるのが簡単です。 確かに、定数のような:

static const int NUM_THREADS = 10;

読者の側で多くのことを考える必要はありません。 同様の理由から、C ++でのconstの使用(およびJavaでのfinal)は非常に奨励されています。
実際、多くの言語(PythonやJavaを含む)では、文字列のような組込み型は不変です。 James Gosling(Javaの作成者)は、「[Immutables]は、より多くの場合、問題を起こさない傾向がある」と述べています。
しかし、変数を一度ライトワンスにすることはできませんが、変数が少ない場所で変更された場合でも役立ちます。

キー・アイデア
変数が操作される場所が多いほど、現在の値を推論するのが難しくなります。
それではどうしますか? どのように変数を追記型に変更することができますか? さて、多くの場合、次の例のように、コードを少し再構成する必要があります。

参考文献:

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック

The Art of Readable Code: Simple and Practical Techniques for Writing Better Code (English Edition)

The Art of Readable Code: Simple and Practical Techniques for Writing Better Code Kindle (English Edition)

カテゴリー
読みやすいコード

変数のスコープを縮める/Shrink the Scope of Your Variables

"グローバル変数を避ける"というアドバイスを聞いたことがあります。これは良いアドバイスです。グローバル変数がどこでどのように使用されているかを把握するのは難しいからです。 そして、 "名前空間を汚染する"(あなたのローカル変数と衝突する可能性のある名前の束を置く)ことによって、コードは、ローカル変数を使用しようとしているときにグローバル変数を誤って変更する可能性があります。
実際、グローバル変数だけでなく、すべての変数の「範囲を縮小する」ことは良い考えです。

多くのプログラミング言語は、モジュール、クラス、関数、ブロックスコープなど、複数のスコープ/アクセスレベルを提供します。 より制限されたアクセスを使用することは、コードの行数を減らして変数を「見える」ことができるという意味で一般的に優れています。
なぜこれを行うのですか? なぜなら、読者が同時に考えなければならない変数の数を効果的に減らすからです。 すべての変数の範囲を2倍に縮小した場合、平均して一度に範囲の半分の変数が存在します。
たとえば、次のように2つのメソッドだけで使用されるメンバ変数を使用して、非常に大きなクラスがあるとします。

class LargeClass { 
    string str_;
    void Method1() { 
        str_ = ...;
        Method2(); 
    }
    void Method2() { 
        // Uses str_
    }
    // Lots of other methods that don't use str_ ... 
};

ある意味では、クラスメンバ変数は、クラスの領域内の「ミニグローバル」のようなものです。 大規模なクラスの場合、特に、すべてのメンバー変数を追跡するのは難しく、どのメソッドがそれぞれを変更するのかが難しいです。 ミニグローバルの数が少ないほど良い。
この場合、str_をローカル変数に「降格」することは意味があります:

class LargeClass {
    void Method1() {
        string str = ...;
        Method2(str); 
    }
    void Method2(string str) { 
        // Uses str
    }
    // Now other methods can't see str. 
};

クラスメンバーへのアクセスを制限する別の方法は、可能な限り多くのメソッドを静的にすることです。 静的メソッドは、読者に「これらのコード行がこれらの変数から分離されている」ことを知らせるのに最適な方法です。
あるいは、大きなクラスを小さなクラスに分割する方法もあります。 このアプローチは、より小さいクラスが実際には互いに分離されている場合にのみ役立ちます。 お互いのメンバーにアクセスする2つのクラスを作成する場合、あなたは本当に何も達成していません。
大きなファイルを小さいファイルに分割するか、大きな関数を小さな関数に分割する場合も同じです。 そのための大きな動機は、データ(すなわち、変数)を分離することである。
しかし、異なる言語は、スコープを正確に構成するためのルールが異なります。 変数の範囲に関するもっと興味深い規則のほんのいくつかを指摘したいと思います。

ifステートメントスコープをC ++で、次のC ++コードがあるとします。

PaymentInfo* info = database.ReadPaymentInfo(); 
if (info) {
    cout << "User paid: " << info->amount() << endl; 
}
// Many more lines of code below ...

変数infoは残りの関数のスコープ内にとどまるので、これを読んでいる人
コードが情報を念頭に置いて、それが再び使用されるかどうかを疑問に思うかもしれません。
しかし、この場合、infoはif文の内部でのみ使用されます。 C ++では、実際には
条件式の情報:

if (PaymentInfo* info = database.ReadPaymentInfo()) { 
    cout << "User paid: " << info->amount() << endl;
}

今では、読者は情報が範囲外になった後、情報を簡単に忘れることができます。

JavaScriptでの「プライベート」変数の作成

1つの関数だけで使用される永続変数があるとします。

submitted = false; // Note: global variable
var submit_form = function (form_name) { 
    if (submitted) {
            return;  // don't double-submit the form
    }
    ...
    submitted = true;
};

提出されたようなグローバル変数は、このコードを読んでいる人に大きな心配を引き起こす可能性があります。 それはsubmit_form()がサブミットを使用する唯一の機能だとは思われますが、あなたは確かにそれを知ることができません。 実際、別のJavaScriptファイルでは、別の目的のためにsubmitという名前のグローバル変数を使用している可能性があります。
クロージャ内で送信されたラッピングによって、この問題を防ぐことができます。

var submit_form = (function () {
    var submitted = false; // Note: can only be accessed by the function below
    return function (form_name) { 
        if (submitted) {
            return;  // don't double-submit the form
        }
        ...
        submitted = true; 
    };
}());

最後の行のかっこに注意してください。匿名の外部関数はすぐに実行されますが、
内部関数を返します。
以前この技術を見たことがないなら、最初は奇妙に見えるかもしれません。 内部関数だけがアクセスできる「プライベート」スコープを作成するという効果があります。 今読者はどこに提出されたのか不思議に思う必要はありませんか? 同じ名前の他のグローバルと競合することを心配する必要があります。 (JavaScript:Douglas CrockfordのGood Parts [O'Reilly、2008]を参照してください)。

JavaScriptのグローバルスコープ

JavaScriptでは、変数定義(var x = 1ではなくx = 1など)からキーワードvarを省略すると、変数はグローバルスコープに配置され、すべてのJavaScriptファイルとブロックがグローバルスコープにアクセスできます。 次に例を示します。

 <script>
     var f = function () {
         // DANGER: 'i' is not declared with 'var'!
         for (i = 0; i < 10; i += 1) ... 
     };
     f();
 </script>

このコードは私を不注意にグローバルスコープに入れます。したがって、後のブロックでもそれを見ることができます:

<script>
    alert(i); // Alerts '10'. 'i' is a global variable!
</script>

多くのプログラマーはこのスコープルールを認識しておらず、この驚くべき動作は奇妙なバグを引き起こす可能性があります。 このバグは、2つの関数が同じ名前のローカル変数を作成しても、varを使うのを忘れたときによく見られます。 これらの機能は無意識のうちに「クロストーク」し、貧しいプログラマは自分のコンピュータが所有しているとか、RAMが悪いと結論づけることがあります。
JavaScriptの一般的なベストプラクティスは、varキーワード(var x = 1など)を使用して常に変数を定義することです。 この方法は、変数のスコープを、定義されている(最も内側の)関数に制限します。

PythonとJavaScriptでネストされたスコープはありません

C ++やJavaなどの言語では、if、for、tryなどの構造体内で定義された変数がそのブロックのネストされたスコープに限定されるブロックスコープがあります。

if (...) {
    int x = 1;
}
x++; // Compile-error! 'x' is undefined.

しかし、PythonやJavaScriptでは、ブロック内で定義された変数が関数全体に「流出」します。 たとえば、この完全に有効なPythonコードでexample_valueを使用していることに注意してください。

# No use of example_value up to this point.
if request:
    for value in request.values:
        if value > 0:
            example_value = value 
            break
for logger in debug.loggers: 
    logger.log("Example:", example_value)

このスコープルールは多くのプログラマーにとって驚くべきことですが、このようなコードは読みにくいです。 他の言語では、example_valueが最初に定義された場所を見つける方が簡単です。内部にある関数の「左端」に沿って見てください。
前の例もバグです。コードの最初の部分にexample_valueが設定されていない場合、2番目の部分で例外が発生します。 "NameError: 'example_value'が定義されていません。 これを修正し、コードをより読みやすくするために、 "最も近い共通の祖先"(入れ子にして)でexample_valueを使用する場所に定義します。

example_value = None
    
if request:
    for value in request.values:
        if value > 0: 
            example_value = value 
            break
if example_value:
    for logger in debug.loggers:
        logger.log("Example:", example_value)

ただし、これはexample_valueを完全に削除できる場合です。 example_valueは中間結果を保持しているだけで、「中級者を排除する」
95ページの「結果」を参照してください。これらの変数は、「できるだけ早くタスクを完了させる」ことで、しばしば排除できます。
新しいコードは次のようになります:

def LogExample(value):
    for logger in debug.loggers:
        logger.log("Example:", value)
if request:
    for value in request.values:
        if value > 0:
            LogExample(value)  # deal with 'value' immediately
            break

定義を下に移動する

元のCプログラミング言語では、すべての変数定義を関数またはブロックの先頭に置く必要がありました。 多くの変数を持つ長い関数では、あまり時間がかからずにすぐに使用されなくても、読者はこれらの変数についてすぐに考えるようになったため、この要件は残念でした。 (C99とC ++はこの要件を削除しました)。
次の例では、すべての変数が関数の先頭に無意味に定義されています。

def ViewFilteredReplies(original_id):
    filtered_replies = []
    root_message = Messages.objects.get(original_id) 
    all_replies = Messages.objects.select(root_id=original_id)
    root_message.view_count += 1
    root_message.last_view_time = datetime.datetime.now()
    root_message.save()
    for reply in all_replies:
        if reply.spam_votes <= MAX_SPAM_VOTES:
            filtered_replies.append(reply)
    return filtered_replies

このサンプルコードの問題点は、読者が3つの変数を一度に考え、それらの間でギアを切り替えることです。
読者は後でそれを知る必要はないので、最初の使用の直前に各定義を移動するのは簡単です。

ef ViewFilteredReplies(original_id):
   root_message = Messages.objects.get(original_id)
   root_message.view_count += 1
   root_message.last_view_time = datetime.datetime.now()
   root_message.save()
   all_replies = Messages.objects.select(root_id=original_id
   filtered_replies = []
   for reply in all_replies:
       if reply.spam_votes <= MAX_SPAM_VOTES:
           filtered_replies.append(reply)
   return filtered_replies

all_repliesが必要な変数であるのか、それとも排除できるのか不思議に思うかもしれません
行うことによって:

for reply in Messages.objects.select(root_id=original_id): 
...

この場合、all_repliesはかなり説明変数なので、私たちはそれを保つことに決めました。

参考文献:

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック

The Art of Readable Code: Simple and Practical Techniques for Writing Better Code (English Edition)

The Art of Readable Code: Simple and Practical Techniques for Writing Better Code Kindle (English Edition)

カテゴリー
読みやすいコード

変数を削除する/Eliminating Variables

役に立たない一時変数

次のPythonコードのスニペットで、now変数を考えてみましょう:

now = datetime.datetime.now()
root_message.last_view_time = now

今は変える価値のある変数ですか? いいえ。理由は次のとおりです。
•複雑な表現を分解しているわけではありません。
•明示的な追加は行われません。式datetime.datetime.now()は十分明確です。
•一度だけ使用されるため、冗長コードは圧縮されません。

これまでになくても、コードはわかりやすくなりました。

root_message.last_view_time = datetime.datetime.now()

現在のような変数は、通常、コードが編集された後に残る「残り物」です。 変数は、元々複数の場所で使用されていた可能性があります。 あるいは、コーダーが複数回使用することを予期していたかもしれませんが、実際にはそれを必要としませんでした。

中間結果の削除

次に、配列から値を削除するJavaScript関数の例を示します。

var remove_one = function (array, value_to_remove) { 
    var index_to_remove = null;
    for (var i = 0; i < array.length; i += 1) {
        if (array[i] === value_to_remove) { 
            index_to_remove = i;
            break;
        } 
    }
    if (index_to_remove !== null) { 
        array.splice(index_to_remove, 1);
    }
};

変数index_to_removeは、中間結果を保持するために使用されます。 このような変数は、取得するとすぐに結果を処理することで、時には排除することができます。

var remove_one = function (array, value_to_remove) {
    for (var i = 0; i < array.length; i += 1) {
        if (array[i] === value_to_remove) {
            array.splice(i, 1);
            return;
        }
    }
};

コードを早期に返すことで、index_to_removeを完全になくして単純化しました
コードはかなりです。
一般的に、できるだけ早くタスクを完了することは良い戦略です。

制御フロー変数の削除

場合によっては、このコードパターンがループ内に表示されることがあります。

boolean done = false;
while (/* condition */ && !done) {
    ...
    if (...) {
        done = true;
        continue;
    }
}

完了した変数は、ループ全体の複数の場所でtrueに設定される場合もあります。
このようなコードは、しばしば、ループの途中から脱却すべきではない、暗黙のルールを満たすようにしています。 そのようなルールはありません!
doneのような変数は、「制御フロー変数」と呼ばれるものです。その唯一の目的はプログラムの実行を操作することです。実際のプログラムデータは含まれていません。 我々の経験では、構造化プログラミングをより有効に活用することで、制御フロー変数を削除することができます。

while (/* condition */) {
    ...
    if (...) {
        break;
    }
}

このケースはかなり簡単に修正できましたが、単純なブレークでは十分ではない複数のネストされたループがある場合はどうなりますか? このようなより複雑なケースでは、ソリューションはしばしば、コードを新しい関数(ループ内のコード、またはループ全体のコード)に移動する必要があります。

参考文献:

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック

The Art of Readable Code: Simple and Practical Techniques for Writing Better Code (English Edition)

The Art of Readable Code: Simple and Practical Techniques for Writing Better Code Kindle (English Edition)

カテゴリー
読みやすいコード

表現を簡素化する別の創造的方法/Another Creative Way to Simplify Expressions

今度は、各式で多くのことが起こっている別の例があります。今回はC++での例です:

void AddStats(const Stats& add_from, Stats* add_to) {
    add_to->set_total_memory(add_from.total_memory() + add_to->total_memory());
    add_to->set_free_memory(add_from.free_memory() + add_to->free_memory());
    add_to->set_swap_memory(add_from.swap_memory() + add_to->swap_memory());
    add_to->set_status_string(add_from.status_string() + add_to->status_string());
    add_to->set_num_processes(add_from.num_processes() + add_to->num_processes());
    ...
}

繰り返しになりますが、あなたの目は長くて似ているがまったく同じではないコードに直面しています。

10秒間の綿密な調査の結果、各行が毎回異なるフィールドに対してだけ同じことを実行していることに気付くかもしれません。

add_to->set_XXX(add_from.XXX() + add_to->XXX()); 

C++では、これを実装するマクロを定義できます:

void AddStats(const Stats& add_from, Stats* add_to) {
    #define ADD_FIELD(field) add_to->set_##field(add_from.field() + add_to->field())
    ADD_FIELD(total_memory);
    ADD_FIELD(free_memory);
    ADD_FIELD(swap_memory);
    ADD_FIELD(status_string);
    ADD_FIELD(num_processes);
    ...
    #undef ADD_FIELD
}

煩雑さを取り除いたので、コードを見て、何が起こっているのかをすぐに理解できます。 各行が同じことをしているのは明らかです。

マクロの使用を頻繁に推奨しているわけではないことに注意してください。実際、マクロはコードを混乱させ、微妙なバグを招く可能性があるため、通常は回避します。

ただし、この場合のように、シンプルで読みやすさに明確なメリットをもたらす場合があります。

参考文献:

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック

The Art of Readable Code: Simple and Practical Techniques for Writing Better Code (English Edition)

The Art of Readable Code: Simple and Practical Techniques for Writing Better Code Kindle (English Edition)

カテゴリー
読みやすいコード

巨大なステートメントを分解する/Breaking Down Giant Statements

この章では、個々の式を分解する方法について説明しますが、より大きなステートメントを分解する場合にも同じ手法が適用されます。 たとえば、次のJavaScriptコードは一度に多くのことを取り入れることができます。

var update_highlight = function (message_num) {
    if ($("#vote_value" + message_num).html() === "Up") {
        $("#thumbs_up" + message_num).addClass("highlighted");
        $("#thumbs_down" + message_num).removeClass("highlighted");
    } else if ($("#vote_value" + message_num).html() === "Down") {
        $("#thumbs_up" + message_num).removeClass("highlighted");
        $("#thumbs_down" + message_num).addClass("highlighted");
    } else {
        $("#thumbs_up" + message_num).removeClass("highighted");
        $("#thumbs_down" + message_num).removeClass("highlighted");
    }
};

このコードの個々の表現はそれほど大きなものではありませんが、すべて一緒に配置されていると、一度にすべてに当てはまる1つの巨大なステートメントを形成します。

幸いなことに、多くの式は同じです。つまり、関数の上部で要約変数としてそれらを抽出できます(これは、DRY(Do n’t Repeat Yourself)原則のインスタンスでもあります)。

var update_highlight = function (message_num) {
    var thumbs_up = $("#thumbs_up" + message_num);
    var thumbs_down = $("#thumbs_down" + message_num);
    var vote_value = $("#vote_value" + message_num).html();
    var hi = "highlighted";

    if (vote_value === "Up") { 
        thumbs_up.addClass(hi); 
        thumbs_down.removeClass(hi);
    } else if (vote_value === "Down") { 
        thumbs_up.removeClass(hi); 
        thumbs_down.addClass(hi);
    } else { 
        thumbs_up.removeClass(hi); 
        thumbs_down.removeClass(hi);
    }
};

var hi = "highlighted"の作成は厳密には必要ありませんが、6つのコピーがあったため、魅力的なメリットがありました。

-入力ミスを防ぐのに役立ちます。 (実際、最初の例では、文字列のスペルが5番目のケースで "highighted"と間違っていたことに気付きましたか?)

-ライン幅をさらに縮小し、コードをスキャンしやすくします。

-クラス名を変更する必要がある場合は、変更する場所は1つだけです。

参考文献:

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック

The Art of Readable Code: Simple and Practical Techniques for Writing Better Code (English Edition)

The Art of Readable Code: Simple and Practical Techniques for Writing Better Code Kindle (English Edition)

カテゴリー
読みやすいコード

例:複雑なロジックとの闘い/Example: Wrestling with Complicated Logic

次のRangeクラスを実装しているとします。

struct Range {
    int begin;
    int end;
    // For example, [0,5] overlaps with [3,8]
    bool OverlapsWith(Range other);
};

次の図は、いくつかの範囲の例を示しています。

endは非排他的であることに注意してください。 だから、A、B、Cは互いに重なり合わないが、Dはすべて重なる。
ここでは、OverlapsWith()を実装する試みがあります。範囲のいずれかの端点が他の範囲内にあるかどうかをチェックします。

bool Range::OverlapsWith(Range other) {
    // Check if 'begin' or 'end' falls inside 'other'.
    return (begin >= other.begin && begin <= other.end) ||
            (end >= other.begin && end <= other.end);
}

Even though the code is only two lines long, there’s a lot going on. The following figure shows all the logic involved.

コードは2行だけですが、多くのことが起こっています。 次の図は、関連するすべてのロジックを示しています。

バグがすり抜けるのは簡単であると考えるには、非常に多くのケースと条件があります。

そういえば、バグがあります。 前のコードは、範囲[0,2]が[2,4]と実際には重なっていないと主張しています。

問題は、<=または<を使用して開始値と終了値を比較するときは注意が必要であることです。 ここにこの問題の修正があります:

return (begin >= other.begin && begin < other.end) ||
 (end > other.begin && end <= other.end);

いいね—このコードは非常に複雑になっています。 誰もがこのコードを読んで、それが正しいことを自信を持って知っているとは期待できません。 どうしようか? この巨大な表現をどのように分解できるでしょうか?

よりエレガントなアプローチを見つける

これは、立ち止まって、全く別のアプローチを考える必要がある場合の1つです。

単純な問題(2つの範囲が重複しているかどうかの確認)として始まったものが、驚くほど複雑なロジックに変わりました。

これは、多くの場合、もっと簡単な方法がなければならないというサインです。

しかし、よりエレガントなソリューションを見つけるには創造性が必要です。

どのようにそれについて行きますか? 1つの手法は、問題を「反対の」方法で解決できるかどうかを確認することです。

状況によっては、配列を逆方向に反復したり、一部のデータ構造を前方ではなく後方に埋めたりすることを意味します。

ここで、OverlapsWith()の反対は「重複しない」です。 2つの可能性しかないため、2つの範囲が重なっていないかどうかを判断することは、はるかに単純な問題であることがわかります。

1.他の範囲は、この範囲が始まる前に終了します。

2.もう一方の範囲は、この範囲の終了後に始まります。

これをコードに簡単に変換できます。

bool Range::OverlapsWith(Range other) {
    if (other.end <= begin) return false;  // They end before we begin
    if (other.begin >= end) return false;  // They begin after we end
    return true;  // Only possibility left: they overlap
}

ここのコードの各行は、はるかに単純です。これは、1回の比較のみを含みます。 これにより、読者は<=が正しいかどうかに集中するのに十分な頭脳力を持つことができます。

参考文献:

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック

The Art of Readable Code: Simple and Practical Techniques for Writing Better Code (English Edition)

The Art of Readable Code: Simple and Practical Techniques for Writing Better Code Kindle (English Edition)

カテゴリー
読みやすいコード

短絡ロジックの悪用/Abusing Short-Circuit Logic

ほとんどのプログラミング言語では、論理演算子は短絡評価を実行します。

たとえば、文 if(a || b)は、aがtrueの場合にbを評価しません。 この動作は非常に便利ですが、複雑なロジックを達成するために悪用されることがあります。
著者の一人が書いたステートメントの例を以下に示します。

assert((!(bucket = FindBucket(key))) || !bucket->IsOccupied());

英語では、このコードの意味は次のとおりです。「このキーのバケットを取得します。 バケットがnullでない場合は、占有されていないことを確認してください。」

コードはたったの1行ですが、ほとんどのプログラマーは実際に立ち止まって考えさせられます。 それをこのコードと比較してください:

bucket = FindBucket(key);
if (bucket != NULL) assert(!bucket->IsOccupied());

これはまったく同じことですが、2行のコードであっても、理解するのがはるかに簡単です。

そもそも、なぜコードが1つの巨大な式として記述されたのでしょうか。 その時は、それはとても賢いと感じました。

ロジックを簡潔なコードナゲットに分類することには、一定の喜びがあります。

それは理解できます。それはミニチュアパズルを解くようなものであり、私たちは皆、仕事で楽しみたいです。

問題は、コードがコードを読んでいる人にとって、精神的なスピードのバンプだったことです。

キー・アイデア
「巧妙な」コードナゲットに注意してください。他の人が後でコードを読むときに混乱することがよくあります。

これは、短絡動作の利用を避けるべきであることを意味しますか? いいえ。クリーンに使用できるケースはたくさんあります。例えば:

if (object && object->method()) ...

言及する価値のある新しいイディオムもあります。Python、JavaScript、Rubyなどの言語では、「or」演算子は引数の1つを返します(ブール値に変換されません)。そのため、次のようなコードを記述します。

x = a || b || c

a、b、またはcから最初の「真の」値を取り出すために使用できます。

参考文献:

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック

The Art of Readable Code: Simple and Practical Techniques for Writing Better Code (English Edition)

The Art of Readable Code: Simple and Practical Techniques for Writing Better Code Kindle (English Edition)

カテゴリー
読みやすいコード

デ・モルガンの法則を使う/Using De Morgan’s Laws

回路やロジックの講座を受講したことがあるなら、デ・モルガンの法則を覚えているかもしれません。 ブール式を同等の式に書き換えるには、次の2つの方法があります。

1) not (a or b or c) ⇔ (not a) and (not b) and (not c)
2) not (a and b and c) ⇔ (not a) or (not b) or (not c)

これらの法則を覚えていない場合は、

「notを割り当ててand/orをスイッチします。」
(または、逆に言うと、「そうでないものを取り除きます」)。

ブール式を読みやすくするためにこれらの法則を使用することがあります。 例えば、あなたのコードが:

if (!(file_exists && !is_protected)) Error("Sorry, could not read file.");

それは次のように書き直すことができます:

if (!file_exists || is_protected) Error("Sorry, could not read file.");

参考文献:

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック

The Art of Readable Code: Simple and Practical Techniques for Writing Better Code (English Edition)

The Art of Readable Code: Simple and Practical Techniques for Writing Better Code Kindle (English Edition)

カテゴリー
読みやすいコード

要約変数/Summary Variables

表現が説明を必要としなくても(意味するものを理解することができるため)
新しい変数でその式を捕捉することは依然として有効です。

その目的が単により大きなコードのチャンクを管理し、より簡単に考えられる小さな名前に置き換えることであれば、これを要約変数と呼びます。

たとえば、次のコードの式を考えてみましょう。

if (request.user.id == document.owner_id) { 
    // user can edit this document...
}
...
if (request.user.id != document.owner_id) { 
    // document is read-only...
}

式request.user.id == document.owner_idはそれほど大きくないように見えるかもしれませんが、5つの変数があるため、考えるのに少し時間がかかります。

このコードの主な概念は、「ユーザーは文書を所有していますか?」という概念です。その概念は、要約変数を追加することでより明確に記述できます。

final boolean user_owns_document = (request.user.id == document.owner_id);

if (user_owns_document) {
    // user can edit this document...
}
...

if (!user_owns_document) {
    // document is read-only...
}

あまりよくないように見えるかもしれませんが、(user_owns_document)の場合のステートメントの方が少し簡単です。

また、上部にuser_owns_documentが定義されていると、「これはこの機能全体で参照する概念です」と読者に事前に伝えます。

参考文献:

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック

The Art of Readable Code: Simple and Practical Techniques for Writing Better Code (English Edition)

The Art of Readable Code: Simple and Practical Techniques for Writing Better Code Kindle (English Edition)