The code examples in this article use the Go language as an example

Welcome to follow “SH full stack Notes” on wechat.

What is cyclomatic complexity

If you haven’t heard of it before and wonder what it’s used for, there’s an explanation on Wikipedia.

Cyclomatic complexity is a software metric used to indicate the complexity of a program. It is a quantitative measure of the number of linearly independent paths through a program’s source code. It was developed by Thomas J. McCabe, Sr. in 1976.

Cyclomatic complexity is a measure of code complexity. Cyclomatic complexity was invented by Thomas J. McCabe Sr in 1976.

1. Why cyclomatic complexity

If you project now, the readability of the code is very poor, difficult to maintain, special long single function code, all kinds of the if, else case nested at chunks to write bad code do not know how to start, even to the extent that simply don’t understand, you can consider using cyclomatic complexity measure yourself by the complexity of the code in the project.

Without deliberate control, as our projects reach a certain size, some of the more complex business logic will result in some developers writing very complex code.

For a real complex business example, if you are developing in a test-driven Development (TDD) way, you may have written dozens of cases of single tests before you have actually written the implementation of an interface, and the real business logic has not even begun to be written

Again, for example, a function, there are hundreds, even thousands of lines of code, in addition to various if else while nested, even write code, may be back in a few weeks and see the context to code, and may also look not to understand, because of its poor the readability of the code, you understand is very difficult, what maintainability and expansibility, talked?

So how do we avoid this earlier in the Code, CR (Code Review)? The cyclomatic complexity detection tool is used to detect cyclomatic complexity in the submitted code, and then refactor according to the cyclomatic complexity detection. Break down overly long and complex code into smaller, single and clear functions, or use design patterns to solve the massive if and else nested logic in your code.

Some people might think, well, I don’t get a lot of benefit from reducing cyclomatic complexity, maybe in the short term, or you might even have to refactor someone else’s code because you’re touching someone else’s code, triggering a cyclomatic complexity check.

But in the long run, code with low cyclomatic complexity is more readable, extensible, and maintainable. Your coding skills will also improve as you use design patterns in action.

2. Cyclomatic complexity metrics

So cyclomatic complexity, how does cyclomatic complexity measure the complexity of code? Not by feeling, but by calculating by its own rules. There are two methods of calculation, as follows:

  1. Node decision method
  2. Point edge method

I organized the criteria into a table for reference only.

Cyclomatic complexity instructions
1-10 The code is OK and the quality is OK
11-15 The code is already complex, but it’s okay to try to refactor some points
16 – up The code is already very complex, low maintainability, and high maintenance costs, so refactoring is necessary

Of course, I personally believe that the cyclomatic complexity criterion should not be arbitrarily applied to all companies in all cases, but should be analyzed according to their own actual situation.

This is completely to see their own business volume and the actual situation to decide. If your business is very simple and a single application with very simple CRUD functions, your cyclomatic complexity will not be as easy as you would like. At this point you can choose to set the cyclomatic complexity refactoring threshold to 10.

If your business is very complex and involves multiple other microservice system calls, plus corner case judgment in various businesses, cyclomatic complexity above 100 might not be a problem.

If such code is not refactored, it will become more and more difficult to maintain as the requirements increase.

2.1 Node determination method

Here is only the simplest one, the node determination method, because some tools are also included in accordance with this algorithm to algorithm, its calculation formula is as follows.

Cyclomatic complexity = Number of nodes + 1

What does the number of nodes represent? These are the control nodes below.

If, for, while, case, catch, and, not, Boolean, ternary operators

In plain English, when you see the sign above, you increase cyclomatic complexity by 1. So let’s look at an example.

If the number of nodes is 13, the cyclomatic complexity will be 13 + 1 = 14, and the cyclomatic complexity will be 14. It should be noted that && will also be counted as one of the nodes.

2.2 Using Tools

For we can use to judge the cyclomatic complexity gocyclo golang you can use the go install github.com/fzipp/gocyclo/cmd/gocyclo fast installation. Then use gocyclo $file to determine. We can create a new file called test.go.

package main

import (
	"flag"
	"log"
	"os"
	"sort"
)

func main(a) {
	log.SetFlags(0)
	log.SetPrefix("cognitive: ")
	flag.Usage = usage
	flag.Parse()
	args := flag.Args()
	if len(args) == 0 {
		usage()
	}

	stats := analyze(args)
	sort.Sort(byComplexity(stats))
	written := writeStats(os.Stdout, stats)

	if *avg {
		showAverage(stats)
	}

	if *over > 0 && written > 0 {
		os.Exit(1)}}Copy the code

Then use the command gocyclo test.go to calculate the cyclomatic complexity of the code.

$ gocyclo test.go
5 main main test.go:10:1
Copy the code

The main method representing the main package starts at line 11 and evaluates to cyclomatic complexity of 5.

3. How can cyclomatic complexity be reduced

There are many, many methods, and there are many names for them, but they may not be easy to understand for those who are new to cyclomatic complexity. So I’ve summed up how to reduce cyclomatic complexity in one sentence: “Minimize the number of nodes in a node determination method.”

In plain English, write as few if, else, while, case statements as possible.

When you reduce the cyclomatic complexity of your code, you’re actually refactoring it. For most business code, the less code there is, the easier it will be for people who will read it later.

The simple summary comes down to two directions, one is to split up small functions, the other is to try to minimize the flow control statements.

3.1 Splitting small functions

Split small functions, cyclomatic complexity of calculation range is within a function, put your code into complex business one by one the responsibilities of a single function, so behind the code reader can understand at a glance about do you think you’re doing, and then specific to each function, due to its single responsibility, and the code quantity is little, you also can understand easily. In addition to reducing cyclomatic complexity, splitting small functions also improves code readability and maintainability.

For example, there are a lot of condition judgments in the code.

In fact, it can be optimized so that we can separate a judgment function and only do condition judgment.

3.2 Write less flow control statements

Here’s a particularly simple example.

You can actually optimize it to look like this.

I’ll leave you with this example, but as you can see, as I mentioned above, the goal is to reduce flow control statements like if. In fact, in another way, complex logical judgment will definitely increase the cost of understanding the code we read, and it is not convenient for later maintenance. So, when refactoring, try to simplify your code as much as possible.

Is there a more direct way to do this? For example, try to avoid this problem from the beginning of the code.

4. Use the go – linq

Before we rush to understand what Go-Linq is, let’s look at a classic business scenario problem.

Gets a list of ids from a list of objects

If we were in GO, we could do that.

A little tedious, familiar with Java students may say, why such a simple function will write so complex, so three times five divided by two to write the following code.

The image above uses Stream, a new Java8 feature, which is not currently available in the Go language. This is where go-Linq comes in, and the code that uses Go-LinQ looks like this.

This is go-linq. We used an example to introduce the definition of go-linq. The following is a brief introduction to several common uses.

4.1 the ForEach

Similar to Foreach in Java 8, it is a traversal of a collection.

The first is a From, which represents the input, the place where dreams start, and can be equated with stream in Java 8.

Then you can see that there are ForEach and ForEachT, ForEachIndexed and ForEachIndexedT. The former simply iterates over the element, while the latter prints its subscripts as well. It’s the same as Range in Go and ForEach in Java 8, but Java 8 ForEach doesn’t have a subscript. Go-ling has it because it records an index of its own. ForEachIndexed source code is as follows.

What’s the difference between the two? I think it’s whether you’re sensitive to the type of elements you’re iterating over, and most of the time you are. If you use a T, the go-ling will change the interface to the type you defined in the function during traversal, such as fruit String.

Otherwise, we need to manually convert the interface to the corresponding type, so I’ll use ForEachT for all the examples.

4.2 the Where

You can think of it as a WHERE condition in SQL or as a filter in Java 8, which filters a collection based on certain conditions.

The above Where filter filters out elements with a string length greater than 6. You can see that there is a ToSlice, which prints the filtered result to the specified slice.

4.3 the Distinct

The same thing you know about Distinct in MySQL, or Distinct in Java 8, is de-duplication.

4.3.1 Simple Scenario

4.3.2 Complex Scenarios

Of course, in real development, such a single integer array is rare, and most objects to determine are struct arrays. So let’s do a slightly more complicated example.

The above Code is the slice of a product, which is de-weighted according to the Code field of the product.

4.4 the Except

Take the difference set of two sets.

4.4.1 Simple Scenario

4.4.2 Complex Scenarios

4.5 intersects

Take the intersection of two sets.

4.5.1 Simple Scenario

4.5.2 Complex Scenario

4.6 Select

Select is functionally similar to ForEach, with the following differences.

Select returns a Query object

ForEach has no return value

You don’t have to worry about what a Query object is, just as the map, filter, and other control functions in Java8 return a Stream.

4.6.1 Simple Scenario

A SelectT iterates through a collection, performs some operations, and outputs the results to a new slice.

SelectMany returns a Query for each element in the collection, similar to flatMap in Java 8, which creates a Stream for each element. Basically, you take a two-dimensional array and flatten it into a one-dimensional array.

4.6.2 Complex Scenarios

4.7 Group

Group groups the union according to the specified element. The source of Group ‘is as follows.

Key is the Key we use when grouping, and Group is the list of elements corresponding to the Key obtained after grouping.

Well, due to space reasons, the use of ‘Go-linq’ is first introduced here, interested can go to the go-linq website to see all the usage.

5. About the use of Go-linq

First of all, I believe that using Go-LinQ is not just about “escaping” cyclomatic complexity checks by inspection tools, but about refactoring your code to make it more readable.

For example, in some complex scenarios, using Go-LinQ can actually make your code harder to understand. The code needs to be shown to you and the subsequent maintenance of the students, do not blindly pursue low cyclomatic complexity code, and crazy use of Go-LinQ.

I personally only prefer to use go-linQ for a few operations on collections, other complications, good code, with appropriate comments, and not digging holes for others (including yourself). And that’s not to say that all if else is bad code. If necessary if else can greatly increase the readability of your code, why not? (I’m not talking about if else presets all over the screen.)

That’s all for this blog post. If you found it helpful, please give it a thumbs up, a comment, a share and a comment.

Welcome to wechat search to follow [SH full stack Notes] and check out more related articles