コピー コードコードは次のとおりです:
!(json.content.from == "" && json.content.content == "キック") && !(json.command == "systemmessage" || json.content.type == " sysmsg")
外側の 2 つのスイッチ ケースをオンにします。この分岐の条件は次のとおりです:
json.result == "ok" && (json.command == "message" || json.command == "systemmessage") && !(json.content.from == "" && json.content.content == "キック") && !(json.command == "systemmessage" || json.content.type == "sysmsg")
ここに繰り返しのロジックがあります。省略すると、次のようになります。
json.result == "ok" && json.command == "message" && !(json.content.from == "" && json.content.content == "キック") && ! (json.content.type == "sysmsg")
単純な 4 文字の else からこのような長い一連の論理演算式を導き出すのに、どれだけの労力を費やしたでしょうか?しかも、この表現はよく見ないと何を言っているのか分かりません。
ここで、複雑なブランチの読み取りと管理が困難になります。 if else を含む switch ケースに直面していると想像してください。合計 3 つのケースがあり、それぞれのケースに 3 つの else があり、各分岐とその前提条件がすべて含まれています。すべての祖先分岐は非 then-AND の結果です。
複雑な分岐を避ける方法
まず第一に、複雑な論理演算を避けることはできません。リファクタリングの結果は同等のロジックになるはずです。私たちにできることは、コードを読みやすく、管理しやすくすることだけです。したがって、複雑な論理演算を読みやすく管理しやすくする方法に焦点を当てる必要があります。
クラスまたはファクトリーへの抽象化
オブジェクト指向設計に慣れている人にとって、これは複雑な論理演算を分割し、それらを異なるクラスに分散することを意味するかもしれません:
switch (json.result) {
case "ok":
var Factory = commandFactories .getFactory(json.command);
var command = Factory.buildCommand(json);
break;
少なくともブランチが短くなり、コードが読みやすくなりました。このスイッチケースはステータスコード分岐のみを処理します。「ok」ステータスコードをどのように扱うかは他のクラスの問題です。 getFactory 内には、この命令がどのファクトリを選択するかの選択を作成することに重点を置いた一連の分岐がある場合があります。同時に、buildCommand には、この命令の構築方法を決定する他の簡単な分岐がある場合があります。
この利点は、ブランチ間の入れ子関係が解消され、各ブランチが独自のコンテキスト内でのみ正しい状態を維持する必要があることです。たとえば、getFactory は名前付き関数になりました。そのため、この関数内の分岐は、getFactory が実際に呼び出されるコンテキストに注意を払うことなく、getFactory という名前によって暗示されるコントラクトを実装するだけで済みます。
抽象をパターン マッチングに変換します
もう 1 つの方法は、この複雑な論理演算をパターン マッチングに変換することです:
Network.listen({ "result": "ok",
"command": "message",
"content": { " from": "", "content": "kicked" }
}, function(json) { /* 切断 */ });
Network.listen([{
"result": " ok ",
"command": "message",
"content": { "type": "sysmsg" }
}, {
"result": "ok",
" command": "systemmessage"
}], function(json) { /* システム メッセージをレンダリング */ });
Network.listen({
"result": "ok",
" command": "message",
"content": { "from$ne": "", "type$ne": "sysmsg" }
}, function tion(json) { /* チャットメッセージをレンダリングする*/ });
Is it much clearer now? The first case is to be kicked offline and must match the specified from and content values. The second case is to display system messages. Since the system messages are slightly different in the two versions of the protocol, we need to capture two different JSONs, and any one that matches is considered a hit. The third situation is to display chat messages. Since system messages and kick-off instructions are special chat messages in the old version of the protocol, in order to be compatible with the old version of the protocol, these two situations must be excluded from the display of chat messages, so Just use the suffix "$ne" (meaning not equal) for matching.
Since the listen method is context-free, each listen independently declares what kind of JSON it matches, so there is no implicit logic. For example, to capture chat messages, you must explicitly exclude from == "" and type == "sysmsg", which does not need to be inferred from the context if else.
Using pattern matching, you can greatly improve the readability and maintainability of your code. Since what we want to capture is JSON, we use JSON to describe what each branch wants to capture, which is much clearer than a long logical operation expression. At the same time, every modification on this JSON is independent, and modifying one condition does not affect other conditions.
Finally, how to write such a pattern matching module is beyond the scope of this article.