设计模式问题:删除布尔函数参数的过度使用

Design Pattern question: removing overuse of boolean function parameters

我已经几十年没刷过四人帮了。我最近从某些代码中闻到难闻的气味,正在寻找有关优化设计的建议。

存在一个通过接受二进制上传、对其执行各种处理并存储文件来为 API POST 提供服务的函数。随着时间的推移,随着新需求的出现,根据上传的二进制文件的类型,需要跳过该功能中的一些步骤。随着时间的推移,方法签名演变为:

第一次迭代:

public ResponseObject uploadThing(long user_id, long location_id, byte[] file_bytes)

第二次迭代:

public ResponseObject uploadThing(long user_id, long location_id, byte[] file_bytes, boolean ignore_azure)

第三次迭代:

public ResponseObject uploadThing(long user_id, long location_id, byte[] file_bytes, boolean ignore_azure, boolean ignore_aws)

第四次迭代:

public ResponseObject uploadThing(long user_id, long location_id, byte[] file_bytes, boolean ignore_azure, boolean ignore_aws, boolean log_to_airtable)

布尔值对应于包装函数相关部分的新添加条件。此函数有多个路径,因此每次添加新布尔值时,都需要重新访问所有调用代码。此外,代码的自文档化程度越来越低。这里有一个例子:

ro = uploadThing(user_id, org_id, file_bytes, true, false, true)

如果实际触发了任何布尔相关代码,则执行顺序很关键。其他参数中也没有包含可用于确定执行 uploadThing 方法的哪些部分的信息 - 它完全基于特定调用代码的位置。

我不喜欢这个的一些事情:调用者和被调用者之间的耦合越来越紧密,在重构中覆盖多个点的需求越来越大,以及方法调用的预期行为在这一点上越来越模糊它被调用的地方。你会如何重组它?

添加标志枚举,并使用可变参数传递它们。

ResponseObject uploadThing(long user_id, long location_id, byte[] file_bytes, Flags... flags)

您的最终来电者可能看起来像

uploadThing(userId, orgId, fileBytes, Flags.IGNORE_AWS, Flags.LOG_TO_AIRTABLE);

在不了解您的客户端代码(调用 uploadThing 的代码)的要求的情况下,很难给出一个笼统的答案。

你所有的调用都有固定的布尔常量吗?如果是这样,您可以将布尔值重构为单独的配置 class 并将其对象传递给 uploadThing。此外,如果某些代码使用了一些布尔标志而不是全部,您可以为配置创建一个继承层次结构 class,其中在每个子 class 中仅使用相关标志。

另一种解决方案可能是将 uploadThing 重构为新的 class 并通过合成将其包含在内。在构造函数中,您可以提供适当的布尔值组合。

您可以根据自己的喜好增强这些选项(依赖注入等)

但这在很大程度上取决于被调用方的要求以及他们如何使用uploadThing

你有一个伸缩参数反模式的例子。

使用 builder 模式,尤其是在添加功能、可选性、规定时。 示例:旧的 HttpClient,Base64 编码器。

这里的问题是你想要一个薄的API,甚至可能生成。

使用一个类似地图的参数class:

public ResponseObject uploadThing(UploadParams params)

public class UploadParams {
    private UploadParams() { }

    public static UploadParamsBuilder(long user_id, long location_id) { }

}

class UploadParamsBuilder {
    UploadParams build() { }
    UploadParamsBuilder withFileBytes(byte[] bytes) { }
    ...

引入可能已经在第二次迭代中完成,因为 azure 是额外的附加功能。