V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
buzhiming
V2EX  ›  编程

代码优化之 - pure function

  •  
  •   buzhiming · 2018-02-04 20:35:30 +08:00 · 2678 次点击
    这是一个创建于 2516 天前的主题,其中的信息可能已经有所发展或是发生改变。

    很多人在写一个类时会写出这样的代码:

    class Person {
        talk (name, msg) {
            this._initName(name)
            this.msg = msg
            this._send()
        }
    
        _initName (name) {
            this.name += ': '
        }
        
        _send () {
            console.log(this.name + this.msg)
        }
    }
    

    我认为更好的写法是这样:

    class Person {
        talk (name, msg) {
            this.name = this._initName(name)
            this._send(this.name, msg)
        }
        
        _initName (name) {
            return name += ': '
        }
        
        _send (name, msg) {
            console.log(name + msg)
        }
    }
    

    在实现了同样的功能的同时,下面的代码有以下几个优点:

    initName 成为了一个纯函数

    纯函数的特点是没有副作用,如果在该类的其他地方仍需复用这个逻辑,可以直接使用,比如

    const anotherName = this._initName(anotherName)
    

    而原来的 initName(name) 因为和 this.name 绑定,无法很好的复用。

    方法意图更加明显,可读性高

    阅读代码 1 时,当看到 talk 的代码时很难看出来 this.initName(name)this.send() 到底在做什么,代码 2 比代码 1 多传递了以下信息:

    • this.initName 改变了 name 的值,并且这个值被赋值到了 this.name 属性
    • this.send 依赖于 name 和 msg

    总结为以下

    • 当一个方法只修改了一个状态(实例属性)时,可以不从该方法内部去修改状态,而是返回这个修改后的值,让调用的方法去处理赋值
    • 当一个方法依赖于其他状态(实例属性)时
      • 如果依赖的状态较少,建议将依赖申明为参数,并从调用处传递依赖
      • 如果依赖的状态特别多且复杂,传参将会变得很麻烦,但是这通常说明这个方法过于庞杂,需要先精简和拆分,再遵从上面的规则优化

    http://buzhiming.me/2018/01/30/func-state/

    viko16
        1
    viko16  
       2018-02-04 21:29:47 +08:00   ❤️ 2
    那为什么不直接把 _initName 和 _send 抽离出去?别的类复用的时候还少一次实例化呢
    wwqgtxx
        2
    wwqgtxx  
       2018-02-04 22:18:59 +08:00 via iPhone
    你这种写法在 python 下都会建议你把写法函数改成 classmethod 或者 staticmethod 了
    buzhiming
        3
    buzhiming  
    OP
       2018-02-04 22:26:26 +08:00
    @viko16 对,_initName 和 _send 是完全可以抽出去的,具体看情况而定吧。
    buzhiming
        4
    buzhiming  
    OP
       2018-02-04 22:27:31 +08:00
    @wwqgtxx 是的,js 也可以写成 static method,限于篇幅我就没讲那么细啦
    doubleflower
        5
    doubleflower  
       2018-02-05 08:25:03 +08:00 via Android
    你把数据都从函数里面传进去还要写个类干嘛?不伦不类,面向对象类就是用来封装数据的
    buzhiming
        6
    buzhiming  
    OP
       2018-02-05 09:00:44 +08:00
    @doubleflower 不对对外暴露的接口做这种优化,所有和类的使用并不冲突。
    doubleflower
        7
    doubleflower  
       2018-02-05 09:54:08 +08:00
    @buzhiming 这和对外暴露有什么关系。你既然实现用了类,数据就是绑在对象上的,方法是操作对象数据的,你要增加灵活性可以不从对象上取数据,还你的函数为何还要放在类里?定义一个类外的独立函数不是更好?
    buzhiming
        8
    buzhiming  
    OP
       2018-02-05 12:09:13 +08:00
    @doubleflower 当然有关系,数据封装在了类里面,所以在外面调用类的对外暴露的方法时,只需传必要的参数,而不要去传封装的参数,这些方法是不能优化为纯函数的。

    而供类内部自己使用的私有方法,这么优化是有我说的以上的几点好处的。

    你说的没错,这些函数完全可以放在类的外部作为独立函数,只不过这不是重点。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2508 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 15:53 · PVG 23:53 · LAX 07:53 · JFK 10:53
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.