- Hands-On Dependency Injection in Go
- Corey Scott
- 468字
- 2021-06-10 19:17:44
Code bloat
Code bloat smells are cases where unwieldy slabs of code have been added to structs or functions so that they have become hard to understand, maintain, and test. Frequently found in older code, they are often the result of a gradual degradation and lack of maintenance rather than intentional choices.
They can be found with a visual scan of the source code or by employing a cyclomatic complexity checker (a software metric that indicates the complexity of a piece of code) such as gocyclo (https://github.com/fzipp/gocyclo).
These smells include the following:
- Long methods: While the code is run on computers, it is written for humans. Any method of more than about 30 lines should be split into smaller chunks. While it makes no difference to the computer, it makes it easier for us humans to understand.
- Long structs: Similar to long methods, the longer a struct, the harder it is to understand and therefore maintain. Long structs typically also indicate the struct is doing too much. Splitting one struct into several smaller ones is also a great way to increase the reusability potential of the code.
- Long parameter lists: Long parameter lists also indicate that the method is likely doing more than it should. When adding new features, it is tempting to add a new parameter to an existing function to account for the new use case. This is a slippery slope. This new parameter is either optional/unnecessary for the existing use cases or is an indication of a significant increase in complexity in the method.
- Long conditional blocks: Switch statements are amazing. The problem is they are very easy to abuse and tend to multiply like proverbial rabbits. Perhaps the most significant problem, however, is their effect on the readability of the code. Long conditional blocks take up a lot of space and interrupt the readability of the function. Consider the following code:
func AppendValue(buffer []byte, in interface{}) []byte{
var value []byte
// convert input to []byte
switch concrete := in.(type) {
case []byte:
value = concrete
case string:
value = []byte(concrete)
case int64:
value = []byte(strconv.FormatInt(concrete, 10))
case bool:
value = []byte(strconv.FormatBool(concrete))
case float64:
value = []byte(strconv.FormatFloat(concrete, 'e', 3, 64))
}
buffer = append(buffer, value...)
return buffer
}
By taking interface{} as input, anywhere we wish to use it, we are almost forced to have a switch like this one. We would be better off changing from interface{} to an interface and then adding the necessary operations to the interface. This approach is better illustrated by the json.Marshaller and driver.Valuer interfaces in the standard library.
Applying DI to these smells will typically reduce the complexity of individual pieces of code by breaking them into smaller, separate pieces, which in turn makes them easier to understand, maintain, and test.
- 大話PLC(輕松動漫版)
- Mastering Visual Studio 2017
- Go語言高效編程:原理、可觀測性與優化
- Python程序設計(第3版)
- HTML5 移動Web開發從入門到精通(微課精編版)
- Learning Network Forensics
- Mastering Linux Network Administration
- 搞定J2EE:Struts+Spring+Hibernate整合詳解與典型案例
- INSTANT Adobe Edge Inspect Starter
- 代替VBA!用Python輕松實現Excel編程
- 運維前線:一線運維專家的運維方法、技巧與實踐
- C++從入門到精通(第6版)
- 青少年學Python(第2冊)
- Learning Image Processing with OpenCV
- Mastering ArcGIS Server Development with JavaScript