D纯的好处 深碍√TFBOYSˉ_ 2022-09-15 06:11 85阅读 0赞 [原文][Link 1] 可通过创建`所有指针或引用类型参数为常`来得到`强纯`.否则,弱纯放弃透明引用,但仍有个非常重要的特征: 调用点可访问`弱纯`函数(包括`持久`状态)的所有`输入和输出`. 这很`简单但强大`.甚至(返回所有成员标记`为私,无友`等等的对象的)`强纯`都不保证. 本着可访问的`输入和输出`,看看`好处`: # 可测试性 # 如果不能控制`相关输入`且看不到`输出`,则不能测试.一般是通过`创建模拟并使用依赖注入`来解决,增加了复杂性,且更难`调试和分析`代码. `时钟,文件系统和网络`之类,根本上来说就是不纯的. 常见反模式是:`执行大量纯计算`并用`不纯`操作来完成–如,`计算统计数据`,再写入文件.而简单拆分`纯工作`为独立函数,则立即变得`可测试`. 能控制`所有输入`的一个`重要推论`是可完全`重复测试`.而`弱纯`函数仍然是`相同输入提供相同输出`. # 处理错误 # 不是程序员懒惰,而是系统不支持.如用该`游戏`接口: void addGameDataToPlayerRanking(string filename); 你得到`I/O`异常了,该怎么办?函数有副作用,再试有问题. 纯函数则无此问题,拆分`加载游戏`为:解析数据和更新排名,来`最小化`不纯代码,其他函数`均可为`弱纯. # 代码可读性 # void maybeEnableBooster(); 让调用者知道该函数可启动助推器,很重要.但不是`可能或是否`发生,有`半隐藏`的副作用,封装不好.而下面更好: void selectEngineParameters(); 尽管签名相同,但函数不同,`完全隐藏`了内部工作.我们不会得到`前一个`函数所暗示的不良耦合. 更好的是弱纯接口: void selectEngineParameters(Engine* engine, const(VehicleMotion*) motion_data, const(Environment*) environment_data) pure; `输入和输出`在调用点都可见,可能代码不好看,但`更易理解及检查错误`.如果发现`奇怪`错误,你愿意读哪个? # 模块化代码 # 有人可能反对`暴露`实现细节,但`输入和输出`应只是函数接口.函数纯度不保证`模块化代码`.但使函数更像电子组件`输入和输出`的引脚. 一般,`处理文件`等不纯操作与`处理数据`等纯操作混在一起,是`非模块化`代码库的常见原因.虽然分开并不保证`模块化代码`,但有意义. 纯函数无`导致耦合`的隐藏内部状态,这与`封装`不一样. 如果`代码库`的两部分调用同一函数,则可能`因隐藏在其中的内部状态`而耦合. # 与命令式代码无妥协集成 # 回到`统计数据,写入文件`的示例,`不纯版`可按局部变量`在栈上`存储所有数据.如果`分离`纯代码,新计算函数必须`按返回值`输出数据, 我们不能直接返回`局部变量`,所以现在必须`复制数据或返回`堆分配数据结构,有点浪费.但如果要求强纯时,这是我们必须付出的代价. Stats stats; //栈上分配 // calculateStats可直接返回输出至状态对象,并仍为弱纯 calculateStats(raw_data, &stats); //写文件,永远不会为弱纯 //但写至通用输出区间,则至少可测试 writeStats(stats, output_file); `C++`返回值优化允许`C++`编译器类似重构,很神奇且可选.我更喜欢`弱纯`方法.它更明确,也更灵活. 相同的代码`D`可以这样(不放弃`纯`): Stats stats; //栈上分配 stats.calculate(raw_data); //编译器仍识别为弱纯 stats.write(output_file); # 帮助更多函数变为强纯 # `纯/不纯`是`传递性`的.`弱/强纯`不是.`强纯`可调用`弱纯`.并对外公开`强纯`接口.更易重构不纯为`弱纯`,且更轻松的使`其他函数`变为强纯. 同样,编译器(和人)只需`检查指针/引用`参数是否为`常`,来推导`强纯及其优化`. 从`类型系统`角度来看,`弱纯`对`可变语言`更有意义. `弱纯和强纯`也是`评估代码设计优缺点`的一个很好的维度. [Link 1]: https://theartofmachinery.com/2016/03/28/dirtying_pure_functions_can_be_useful.html
还没有评论,来说两句吧...