D纯的好处

原文
可通过创建所有指针或引用类型参数为常来得到强纯.否则,弱纯放弃透明引用,但仍有个非常重要的特征:
调用点可访问弱纯函数(包括持久状态)的所有输入和输出.
这很简单但强大.甚至(返回所有成员标记为私,无友等等的对象的)强纯都不保证.
本着可访问的输入和输出,看看好处:

可测试性

如果不能控制相关输入且看不到输出,则不能测试.一般是通过创建模拟并使用依赖注入来解决,增加了复杂性,且更难调试和分析代码.
时钟,文件系统和网络之类,根本上来说就是不纯的.
常见反模式是:执行大量纯计算并用不纯操作来完成–如,计算统计数据,再写入文件.而简单拆分纯工作为独立函数,则立即变得可测试.
能控制所有输入的一个重要推论是可完全重复测试.而弱纯函数仍然是相同输入提供相同输出.

处理错误

不是程序员懒惰,而是系统不支持.如用该游戏接口:

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);

帮助更多函数变为强纯

纯/不纯传递性的.弱/强纯不是.强纯可调用弱纯.并对外公开强纯接口.更易重构不纯为弱纯,且更轻松的使其他函数变为强纯.
同样,编译器(和人)只需检查指针/引用参数是否为,来推导强纯及其优化.
类型系统角度来看,弱纯可变语言更有意义.
弱纯和强纯也是评估代码设计优缺点的一个很好的维度.

posted @   zjh6  阅读(13)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示