官术网_书友最值得收藏!

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.

主站蜘蛛池模板: 杭锦旗| 修武县| 古田县| 南投县| 兴宁市| 汤阴县| 寿光市| 静乐县| 班戈县| 会宁县| 寻乌县| 永登县| 灌南县| 文山县| 长丰县| 共和县| 溧阳市| 石狮市| 神木县| 武义县| 府谷县| 大港区| 讷河市| 北流市| 浦北县| 五华县| 保康县| 三台县| 资阳市| 观塘区| 碌曲县| 门源| 泽库县| 肇东市| 扶余县| 太谷县| 香港 | 宣威市| 融水| 西安市| 鹤山市|