避免函数参数修改的最佳实践:如何消除代码重复?
问题描述
我有如下抽象代码片段:
// definitions MyClass1 mc1 = new MyClass1(); MyClass2 mc2 = new MyClass2(); List<String> elements = new ArrayList<>(); // some black magic... // setters based on condition 0 mc2.setElements(elements); String id = saveMyClass2(mc2); mc1.setMc1Id(id); // Some other business black magic // setters based on condition 1 mc2.setElements(elements); String id = saveMyClass2(mc2); mc1.setMc1Id(id); // Some other business black magic // setters based on condition 2 mc2.setElements(elements); String id = saveMyClass2(mc2); mc1.setMc1Id(id);
我在代码多个位置使用上述设置流程,原本想封装为如下包装函数:
void doSettings(MyClass1 mc1, MyClass2 mc2, List<String> elements) { mc2.setElements(elements); String id = saveMyClass2(mc2); mc1.setMc1Id(id); }
但该函数会修改参数,而我希望避免这种操作;同时由于类结构为三层嵌套,深拷贝成本过高也不可行。请问有哪些最佳实践或编程模式可帮助消除代码重复?
可行的解决方案
1. 调整函数职责,返回新对象而非修改入参
既然不想修改原对象,可重构函数让它创建并返回处理后的新对象组合,原对象保持不变:
// 返回封装了处理后mc1和mc2的容器类,或者用简单的Tuple(如果项目允许) record ProcessedResult(MyClass1 updatedMc1, MyClass2 updatedMc2) {} ProcessedResult doSettings(MyClass1 originalMc1, MyClass2 originalMc2, List<String> elements) { // 对mc2做浅拷贝(仅拷贝对象引用,不深嵌套),避免修改原对象 MyClass2 newMc2 = new MyClass2(originalMc2); // 假设MyClass2有拷贝构造函数 newMc2.setElements(elements); String id = saveMyClass2(newMc2); // 同样浅拷贝mc1,设置新id MyClass1 newMc1 = new MyClass1(originalMc1); // 假设MyClass1有拷贝构造函数 newMc1.setMc1Id(id); return new ProcessedResult(newMc1, newMc2); }
使用时直接接收返回的新对象:
// 原mc1、mc2不受影响 ProcessedResult result = doSettings(mc1, mc2, elements); mc1 = result.updatedMc1(); mc2 = result.updatedMc2();
如果类没有拷贝构造函数,也可以手动实现一个copy()方法,只拷贝当前层的属性(因为深拷贝成本高,浅拷贝足够避免修改原对象的直接属性)。
2. 将操作封装为纯函数,分离副作用
把saveMyClass2这个有副作用的操作和对象修改逻辑分离,让核心逻辑变成纯函数(只根据输入生成新对象,不修改原对象、无外部副作用),再单独处理持久化:
// 纯函数:仅生成处理后的mc2,不修改原对象 MyClass2 prepareMyClass2(MyClass2 originalMc2, List<String> elements) { MyClass2 newMc2 = new MyClass2(originalMc2); newMc2.setElements(elements); return newMc2; } // 单独处理持久化和mc1的更新 String saveAndUpdateMc1(MyClass1 originalMc1, MyClass2 preparedMc2) { String id = saveMyClass2(preparedMc2); MyClass1 newMc1 = new MyClass1(originalMc1); newMc1.setMc1Id(id); mc1 = newMc1; // 替换原对象引用,原对象不受影响 return id; }
使用时组合调用:
MyClass2 preparedMc2 = prepareMyClass2(mc2, elements); saveAndUpdateMc1(mc1, preparedMc2);
这样既消除了重复,又保证原对象不被修改。
3. 利用建造者模式封装对象构造逻辑
如果MyClass1和MyClass2的创建/修改逻辑复杂,可以用建造者模式把重复的流程封装进去:
// 为MyClass2创建建造者 class MyClass2Builder { private MyClass2 mc2; public MyClass2Builder(MyClass2 original) { this.mc2 = new MyClass2(original); // 浅拷贝原对象 } public MyClass2Builder withElements(List<String> elements) { mc2.setElements(elements); return this; } public MyClass2 build() { return mc2; } } // 封装完整流程的工具方法 MyClass1 processAndUpdate(MyClass1 originalMc1, MyClass2 originalMc2, List<String> elements) { MyClass2 newMc2 = new MyClass2Builder(originalMc2) .withElements(elements) .build(); String id = saveMyClass2(newMc2); MyClass1 newMc1 = new MyClass1(originalMc1); newMc1.setMc1Id(id); return newMc1; }
使用时:
mc1 = processAndUpdate(mc1, mc2, elements); mc2 = new MyClass2Builder(mc2).withElements(elements).build();
建造者模式让对象的修改逻辑更清晰,也方便后续扩展其他属性的设置。
4. 调整业务逻辑,避免重复触发相同操作
如果多次执行相同的mc2.setElements + save + mc1.setId操作是因为业务逻辑设计冗余,可以考虑:
- 合并条件判断,只在最终需要的地方执行一次该操作
- 将
elements的设置和持久化逻辑提前到初始化阶段,避免后续重复处理
内容的提问来源于stack exchange,提问作者HowToTellAChild
相关产品推荐
相关产品推荐

