Skip to content

includeMaple/iface

Repository files navigation

Github Releases Github Releases Github Releases

what

js接口检查工具,为js定制一份约束契约。

iface是一个工具,可以作为单元测试的一部分,也可以作为开发文档的一部分,辅助开发,方便后期维护,信息传递。

why

面向接口而非实现编程,这意味着当两个类实现了同样的接口就可以相互替换,类变成了可替换的零部件。

我们只关心一个类的接口,而不关心他的实现,程序员可以自行发挥,只要最终提供了接口。

同时我们可以跳出类与接口的概念,如果一个模块export(module.exports或export default)了某些方法和属性,那么只要另外的模块实现了同样的方法和属性,两个模块就可以相互替换,模块变成了可替换的零部件。

同样的,我们只关心模块开放出来的属性和方法,而不关心他的实现,程序员可以自行发挥,只要最终export。

when

如果你的项目使用传统Javascript类,或者ES6的class,可以使用iface

如果你只是经常使用某个对象,里面有一些属性和方法,但只能通过备注、文档、记忆等记录,长期苦于维护,也可以使用iface,我们将提供自动生成接口文档功能。

如果你使用TypeScript,可以使用TypeScript的interface,当然你仍然可以使用Iface

使用

安装

npm install iface or

  <script src='../dist/iface.js'></script>
  <script>
    let {Iface} = iface
  </script>

定义接口

let stackInterface = Iface({
  methods: ['pop', 'push', 'isEmpty', 'isFull'],
  props: ['length', 'top'],
  name: 'stackInterface'
})

注意:接口不能使用new关键字,接口只能实现,不能被实例化

自动生成接口文档

上面是最简单的接口定义,下面定义了一个包含描述信息的接口,定义后可以使用render自动生成markdown

let iface = Iface({
  methods: [
    {
      name: 'pop',
      description: '获取栈顶元素',
      return: '栈顶元素',
      example: `
      let stack = new Stack()
      stack.pop()
    `},
    {
      name: 'push',
      description: '入栈',
      params: ['val: 入栈元素'],
      return: 'boolean',
      example: `
      let stack = new Stack()
      stack.push('data')
      `
    }
  ],
  props: [{
    name: 'length',
    description: '返回栈长度'
  }],
  name: 'stackInterface'
})
console.log(Iface.render(Iface.doc)) // 当定义多个接口时,Iface.doc会有所有接口的信息

md

接口继承

let exStackInterface = Iface.extends(stackInterface, {
  methods: ['clear', 'join'],
  name: 'exStackInterface'
})

静态方法与类方法

let jsonInterface = Iface({
  methods: ['static stringify', 'static parse', 'toJson'],
  name: 'jsonInterface'
})
let iface = Iface({
  methods: ['class extends', 'class ensure'],
  props: ['class all'],
  name: 'iface'
})

接口检查

Iface.ensure(检查对象, 接口),检查类是否实现了某个接口,检查通过返回true

Iface.ensure(new Stack(), stackInterface)

查看所有接口

Iface.all

类型判断

let stackInterface = Iface({
  methods: ['pop', 'push', 'isEmpty', 'isFull'],
  props: ['length', 'top'],
  name: 'stackInterface'
})
Iface.isIface(stackInterface)
if (stackInterface.constructor === Iface) return 'stackInterface is a interface'

how

duck type

iface只关心类开放了哪些属性和方法,不关心这些属性和方法怎么实现,如下,定义了一个类Stack,iface不关心Stack没有length和max属性,只关心后续“期待”怎么被使用,所以可以检查length和max属性,而不是get length和set max方法

export class Stack {
  constructor (max) {
    this._data = []
    this._length = 0
    this._max = max
  }
  pop () {
  }
  push (val) {
  }
  isEmpty () {
  }
  isFull () {
  }
  get length () {
    return this._length
  }
  set length (val) {
    return
  }
  get max () {
    return this._max
  }
  set max (val) {
    return this._max
  }
}

私有变量

Javasript与其他语言相比,没有天然的public、private、protected,我们在使用私有变量通常有以下几个方式

  • 约定使用下划线开头为私有变量/属性
  • 立即执行函数
  • ES6可以使用Symbol

但显然这几种私有形式都存在问题

  • 使用下划线开头作为私有变量,这只是一种约定,实际变量仍然可以在类外访问、修改
  • 使用立即执行函数,虽然确实能达到私有的效果,但不具有可读性,与private表示的方式大相径庭
  • Symbol,我们只是利用了symbol的特性,实际并不具有可读性,symbol可以作为私有变量也可以作为唯一key的生成方式,如果你在代码里看到这个,只能通过语境上下文判断Symbol的用途,另外这个属性也并非真正私有,外部仍然可以访问到

介于以上几点,Iface也没有使用public、private、protected关键字,我们推荐只关心最后这个类开放出来的接口,而不关心私有变量

接口与抽象类

如果你用过其他面向对象语言,应该已经看出这里接口的不同之处:没有实际实现,只是做了一次定义(定义后会将接口信息写入all),需要ensure手动检查。

Iface虽然有构造函数,但不能用new,因为接口与抽象类都不能被实例化,不过抽象类可以达到代码的复用,基于这点,我们可以自己定义一个抽象类做接口工作.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

No packages published