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);
帮助更多函数变为强纯
纯/不纯
是传递性
的.弱/强纯
不是.强纯
可调用弱纯
.并对外公开强纯
接口.更易重构不纯为弱纯
,且更轻松的使其他函数
变为强纯.
同样,编译器(和人)只需检查指针/引用
参数是否为常
,来推导强纯及其优化
.
从类型系统
角度来看,弱纯
对可变语言
更有意义.
弱纯和强纯
也是评估代码设计优缺点
的一个很好的维度.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现