You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

避免函数参数修改的最佳实践:如何消除代码重复?

问题描述

我有如下抽象代码片段:

// 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 06:45:37