Home  >  Article  >  Web Front-end  >  javascript from if else to switch case to abstraction_javascript skills

javascript from if else to switch case to abstraction_javascript skills

WBOY
WBOYOriginal
2016-05-16 18:23:061387browse

My answer is, if with more than two elses, or switch with more than two cases. But it’s normal to use if else and switch case a lot in your code, right? wrong! Most if else and switch cases with more than two branches should not be hard-coded.
Where do complex branches come from?
First of all, the first question we want to discuss is why there are often so many complex branches in legacy code. These complex branches often do not exist in the first version of the code. Assuming that the designer still has some experience, he should foresee areas that may need to be expanded in the future and reserve abstract interfaces.

But after the code goes through several iterations, especially after several adjustments to the requirements details, complex branches will appear. Detailed adjustments to requirements are often not reflected in UML, but directly in code. For example, messages were originally divided into two categories: chat messages and system messages. During design, these were naturally designed as two subcategories of the message category. But then one day the requirements are adjusted in detail. Some of the system messages are important and their titles should be displayed in red. At this time, programmers often make the following modifications:
Add an important attribute to the system message class
Add a branch about the important attribute in the corresponding render method to control the title color
Why did the programmer make such a change? Maybe it's because he didn't realize it should be abstract. Because the requirement says "some of the system messages are important", for programmers who have received more training in imperative programming languages, the first thing they may think of is the flag bit - a flag bit can distinguish between important and non-important ones. important. He did not expect that this requirement could be interpreted in another way, "System messages are divided into two categories: important and unimportant." Interpreting it this way, he knew that the system messages should be abstracted.
Of course it is also possible that the programmer knows that abstraction is possible, but for some reason, he chooses not to do so. A very common situation is that someone forces programmers to sacrifice code quality in exchange for project progress-adding a property and a branch is much simpler than abstract refactoring. If you want to do 10 of this form Modify, is it faster to make 10 branches or 10 abstractions? The difference is obvious.
Of course, if there are too many if elses, some smart people will stand up and say "Why don't we change it to switch case". In some cases, this can indeed improve code readability, assuming each branch is mutually exclusive. But when the number of switch cases increases, the code will also become unreadable.
What are the disadvantages of complex branches
What are the disadvantages of complex branches? Let me take a section from the old code of Baidu Hi web version as an example.

Copy code The code is as follows:

switch (json.result) {
case " ok":
switch (json.command) {
case "message":
case "systemmessage":
if (json.content.from == ""
&& json.content .content == "kicked") {
/* disconnect */
} else if (json.command == "systemmessage"
|| json.content.type == "sysmsg") {
/* render system message */
} else {
/* render chat message */
}
break;
}
break;

This code is not difficult to understand, so I ask a simple question, which branch does the following JSON hit:
Copy code The code is as follows:

{
"result": "ok",
"command": "message",
"content": {
"from" : "CatChen",
"content": "Hello!"
}
}

You can easily get the correct answer: this JSON hit /* render chat message */ (show chat messages) this branch. So I want to know how you made this judgment? First, you have to see if it hits the case "ok": branch, and the result is a hit; then, you have to see if it hits the case "message": branch, and the result is also a hit, so there is no need to look at the case "systemmessage": ; Next, it does not hit the condition in if; and it does not hit the condition in else if, so it hits the else branch.
See the problem? Why can't you look at this else and just say this JSON hits this branch? Because else itself does not contain any condition, it only implies the condition! The condition of each else is the result of the negation and then AND operation of each if and else if before it. In other words, judging the hit of this else is equivalent to a complex set of conditions for judging the hit:
Copy code The code is as follows:

!(json.content.from == "" && json.content.content == "kicked") && !(json.command == "systemmessage" || json.content.type == " sysmsg")

Put on the outer two switch cases. The conditions of this branch are like this:
Copy code The code is as follows:

json.result == "ok" && (json.command == "message" || json.command == "systemmessage") && !(json.content.from == "" && json.content.content == "kicked") && !(json.command == "systemmessage" || json.content.type == "sysmsg")

There is repeated logic here. After omitting it, it looks like this:
Copy code The code is as follows:

json.result == "ok" && json.command == "message" && !(json.content.from == "" && json.content.content == "kicked") && ! (json.content.type == "sysmsg")

How much effort did we spend to derive such a long series of logical operation expressions from the simple four letters else? Moreover, if you don’t look carefully, you really can’t understand what this expression says.
This is where complex branches become difficult to read and manage. Imagine you are facing a switch case with an if else. There are 3 cases in total, and each case has 3 elses. This is enough for you to study - each branch and condition contain all its prerequisites. The branch and the preceding branches of all ancestor branches are the result of non-then-AND.
How to avoid complex branches
First of all, complex logical operations cannot be avoided. The result of refactoring should be equivalent logic. All we can do is make the code easier to read and manage. Therefore, our focus should be on how to make complex logical operations easy to read and manage.
Abstract into classes or factories
For those who are used to object-oriented design, this may mean breaking up complex logical operations and distributing them into different classes:
Copy code The code is as follows:

switch (json.result) {
case "ok":
var factory = commandFactories.getFactory(json.command);
var command = factory.buildCommand(json);
command.execute();
break;
}

this It looks good, at least the branches are shorter and the code is easier to read. This switch case only handles the status code branch. How to deal with the "ok" status code is a matter for other classes. There may be a set of branches inside getFactory that focus on creating a choice of which factory this instruction should select. At the same time, buildCommand may have other trivial branches that determine how to build this instruction.
The advantage of this is that the nested relationship between branches is lifted, and each branch only needs to remain correct in its own context. For example, getFactory is now a named function, so branches within this function only need to implement the contract implied by the name getFactory, without paying attention to the context in which getFactory is actually called.
Abstract into pattern matching
Another way is to translate this complex logical operation into pattern matching:
Copy code The code is as follows:

Network.listen({
"result": "ok",
"command": "message",
"content": { "from": "", "content": "kicked" }
}, function(json) { /* disconnect */ });
Network.listen([{
"result": " ok",
"command": "message",
"content": { "type": "sysmsg" }
}, {
"result": "ok",
"command": "systemmessage"
}], function(json) { /* render system message */ });
Network.listen({
"result": "ok",
"command": "message",
"content": { "from$ne": "", "type$ne": "sysmsg" }
}, func tion(json) { /* render chat message */ });

이제 좀 더 명확해졌나요? 첫 번째 경우는 오프라인으로 추방되며 지정된 원본 및 콘텐츠 값과 일치해야 합니다. 두 번째 경우는 시스템 메시지를 표시하는 것입니다. 두 버전의 프로토콜에서 시스템 메시지가 약간 다르기 때문에 두 개의 서로 다른 JSON을 캡처해야 하며 일치하는 항목은 적중으로 간주됩니다. 세 번째 상황은 채팅 메시지를 표시하는 것입니다. 시스템 메시지와 킥오프 지침은 구 버전 프로토콜의 특수 채팅 메시지이므로 구 버전 프로토콜과 호환되기 위해서는 이 두 가지 상황을 제외해야 합니다. 채팅 메시지가 표시되므로 일치를 위해 접미사 "$ne"(같지 않음을 의미)를 사용하세요.
listen 메서드는 컨텍스트 프리이므로 각 Listen은 어떤 종류의 JSON과 일치하는지 독립적으로 선언하므로 암시적 논리가 없습니다. 예를 들어 채팅 메시지를 캡처하려면 == ""에서 명시적으로 제외하고 == "sysmsg"를 입력해야 하며, 그렇지 않은 경우 컨텍스트에서 추론할 필요가 없습니다.
패턴 일치를 사용하면 코드의 가독성과 유지 관리성을 크게 향상시킬 수 있습니다. 캡처하려는 내용은 JSON이므로 각 분기에서 캡처하려는 내용을 설명하기 위해 JSON을 사용합니다. 이는 긴 논리 연산 표현식보다 훨씬 명확합니다. 동시에 이 JSON의 모든 수정 사항은 독립적이며 한 조건을 수정해도 다른 조건에는 영향을 미치지 않습니다.
마지막으로 이러한 패턴 일치 모듈을 작성하는 방법은 이 기사의 범위를 벗어납니다.
Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn