防御式编程

编写优秀的代码

  1. 代码是程序可识别的代码
  2. 代码是程序员可识别的代码

防御性编程

防御性编程(Defensive programming)是防御式设计的一种具体体现,它是为了保证,对程序的不可预见的使用,不会造成程序功能上的损坏。它可以被看作是为了减少或消除墨菲定律效力的想法。防御式编程主要用于可能被滥用,恶作剧或无意地造成灾难性影响的程序上。---百度百科

为什么要进行防御性编程

防御性编程是为了让我写出可维护以及更少的bug的一种编程方式,它可能完全让我们写出无bug的应用程序所以我们在编写代码的时候要始终保持一颗警惕之心。通常的编码方式是下面这种:

  • 编码 -> 测试 -> 编码 -> 测试-> .......
    这种方式去编写和调试代码,如果我们使用防御性编程编写代码之后基本上就可以使用下面这种方式进行

  • 编码 -> 测试 -> 测试 -> .....

如何减少bug


// 程序出Bug了?
//    ∩∩
//   (´・ω・)
//   _| ⊃/(___
//  / └-(___/
//   ̄ ̄ ̄ ̄ ̄ ̄ ̄
// 算了反正不是我写的
//    ⊂⌒/ヽ-、_
//  /⊂_/____ /
//   ̄ ̄ ̄ ̄ ̄ ̄ ̄
// 万一是我写的呢
//    ∩∩
//   (´・ω・)
//   _| ⊃/(___
//  / └-(___/
//   ̄ ̄ ̄ ̄ ̄ ̄ ̄
// 算了反正改了一个又出三个
//    ⊂⌒/ヽ-、_
//  /⊂_/____ /
//   ̄ ̄ ̄ ̄ ̄ ̄ ̄

一个应用程序一般是处理外部输入,然后将处理结果输出。一般在这个过程中有以下几个地方会产生bug

  • 外部输入 -> 程序处理 -> 输出
  1. 外部输入

    不要相信任何输入,进行入参检测

  2. 程序处理

    注意抛出的异常,以及逻辑分支处理

  3. 程序输出

    异常时的输出,成功和失败时的输出

外部输入

比如服务器返回的数据,以及函数的一些入参。我们应该建立和服务器返回数据类型一样的接口为了接下来的编码工作更容易的进行。


{
    "code": 1,
    "data": {
        "count": 3,
        "result": [{
            "gId": "49",
            "iconUrl": "http:\/\/pic.cdn.sunmi.com\/IMG\/5a715de5d6162.png",
            "goods_title": "银豹零售版",
            "goods_describe": "全场景满足中小零售门店经营需求",
            "goods_unit": "3",
            "goods_spec": "标准版",
            "sId": "218",
            "goods_price": "180.00",
            "goods_type": "1",
            "num": "1",
            "subTotal": 180,
            "cart_id": "1615",
            "unit": "0",
            "begin_num": "1"
        }],
        "total": 14716,
        "number": 15
    },
    "msg": ""
}

我们的接口应该是这样的


interface GoodItem {
  gId: string,
  iconUrl: string,
  goodsTitle: string,
  goodsDescribe: string,
  goodsUnit: string,
  goodsSpec: string,
  sId: string,
  goodsPrice: string,
  goodsType: string,
  num: string,
  subTotal: number,
  cartId: string,
  unit: string,
  beginNum: string
}

interface CartListResponse {
  code: number,
  result: Array,
}

编写一个函数

编写一个函数应该注意这几点

  • 参数
  • 返回值
  1. 参数

    对于函数的入参是否需要检查,做哪些检查

    • 类型检查

      对于js这种弱类型语言进行类型检查是必须的,如果没有进行类型检查就会造成bug的产生比如

      function contain(item, array) {
        return array.indexOf(item) !== -1
      }
      

      这样看起来似乎没有问题,这是因为这看起来好像是我们这个方法从函数和参数的命名上是说我们查 看一个元素是否在数组中,似乎没有错,但是一般作为一个工具函数的话,我们肯定要考虑到多种情况就是如果传入的参数array是一个null,或者undefined或者是字符串,或者是object类型怎么样,当然传入一个字符串他也是能工作的,但是这不是我们创建这个函数的初衷。

      
      function contain(item, array=[]) {
          return array.indexOf(item) !== -1
      }
      
      

      我们给array默认传一个空数组那么就可以解决了上面的array为空和undfied等情况,但是其他类型函数不能进行工作。还是会报错。那么肯定有人会说我创建这个函数就是让用户输入数组,但是它输入其他类型就是不对的。由于js的类型是运行时才能确认出来的,所以我们在编写代码的时候就应该注意。

      function contain(item, array=[]) {
          if (!_.isArray(array)) {
              return false
          }
          
          return array.indexOf(item) !== -1
      }
      

      当然我们可以使用ts在编译阶段就能告诉我们这些错误

      
      function contain(item: T, array: Array) {
          return array.indexOf(item) !== -1
      }
      
      
  2. 返回值

    一个函数的返回值应该是确定的,除非这个函数是非受控的函数。

    
    function jsonParse(string: string): ?object {
        if (string.length === 0) {
          return null
        }
    
        let object = null
    
    
        try {
          object = JSON.parse(string)
        } catch (error) {
          // 处理错误
          console.log(error)
        }
        return object
    

}

```

上面这段代码是把一个json字符串转换成一个对象,可能存在解析失败的情况。我们看到这个函数就是一个非受控的函数,有两种返回值,空或者有值得情况。这一种返回值我们叫做option value 因为外界使用的过程会进行非空判断。

```js
const people = jsonParse("{"age":4}")
if(people) {
    console.log(people.age)
}

```

如果代码可以写成下面的多好啊

```js
const people = jsonParse("{"age":4}")
console.log(people?.age)
```

js可以使用babel插件实现这种可选链,以后可能会加到es标准中去。[ts](https://github.com/Microsoft/TypeScript/issues/16)还不支持。其实在js中我们经常能遇到这种情况比如要取得某个属性我们都是 
```
   const data = response && response.result && response.result.data
``` 


一个完整的函数应该是这样的

```js

function name(params:type) {
  // 条件检查
  if (condition) {
    return
  }

  // 变量声明和初始化

  const name = null
  const age = null

  // 逻辑操作数据处理

}

```

#### if语句需不需要else

```js

if(condition) {
    // 条件满足时的情况
} else {
    // 条件不满足的情况
}

```
如果条件不满足的情况我们不希望函数执行下去

```
    if(!condition) {
        return
    }
    
    //处理条件满足时的情况

```

#### switch 语句一定要有defautl

```js
    
    switch (key) {
      case value:
        
        break;
    
      default:
        break;
    }

```

减少变量的声明

在声明一个变量的时候应该考虑是否需要这个变量,特别是全局变量.下面有两个例子大家感觉那个代码比较容易读?


    enum PeopleType {
      none = '0',
      man = '1',
      woman = '2'
    }

 function getPeopleType(type:PeopleType) {
    let peopleType = null

    switch (type) {
      case PeopleType.none:
        peopleType = 'none'
        break;
      case PeopleType.man:
        peopleType = 'man'
        break;
      case PeopleType.woman:
        peopleType = 'woman'
        break;
      default:
        peopleType = 'none'
        break;
    }

    return peopleType
  }

    enum PeopleType {
      none = '0',
      man = '1',
      woman = '2'
    }

  function getPeopleType(type:PeopleType) {
    if (type === PeopleType.none) {
      return 'none'
    }

    if (type === PeopleType.man) {
      return 'man'
    }

    if (type === PeopleType.woman) {
      return 'woman'
    }

    return 'none'
  }

面对bug每个人的反应不同

首先来看看遇到bug各种程序员是怎么反应的:

理性型:这个 bug 能复现吗?
自负型:这不可能,在我这是好好的。
经验型:不应该,以前怎么没问题?
幻想型:可能是数据有问题。
无辜型:我好几个星期都没碰这块代码了!
乐观型:只需要改一行代码,不会影响其它程序的。
实践型:你重启一下服务试试。

---- 来自知乎如何减少bug

总结:

1. 进行code review
2. 编写UT
3. 良好的编码习惯(严谨的逻辑思维)

你可能感兴趣的:(防御式编程)