假设我有这个重要的方法:
int generateId(int clientCode, int dataVersion) {
return clientCode * 2 + dataVersion % 2;
}
这两个参数都是int
,因此使用错误的参数(例如generateId(dataVersion, clientCode)
)调用此方法非常容易。它将被成功编译和执行。但是生成的id将完全错误,这可能会导致严重的问题。
所以,我的问题是:有没有办法保护自己免受这种参数错位的影响?
现在我只想把它改成int
包装类,如:
int generateId(@Nonnull ClientCode clientCode, @Nonnull Version version) {
return clientCode.getValue() * 2 + version.getValue() % 2;
}
static class IntWrapper<T> {
private final int value;
IntWrapper(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
static class ClientCode extends IntWrapper<ClientCode> {
ClientCode(int value) {
super(value);
}
}
static class Version extends IntWrapper<Version> {
Version(int value) {
super(value);
}
}
并使用generateId(new ClientCode(clientCode), new Version(version))
调用它。当然,它不能保证完全的保护,但至少它以这种方式更透明。
有没有更好的/其他方式?
答案 0 :(得分:6)
考虑通过Method-Chaining配置你的对象
configure().withDataVersion(dataVersion).withClientCode(clientCode).generateId();
虽然它使配置更加冗长,但阅读起来也更清晰。
DataVersion 和 ClientCode - 信息可以移动到内部类中。 configure()
启动内部类,withDataVersion(int)
和withClientCode(int)
基本上是设置者。 generateId()
将像今天一样构建并返回您的ID。
答案 1 :(得分:1)
为每个参数编写包装类,只是为了确保某人不会对参数的顺序混淆听起来非常极端,并且使用起来很麻烦。
如果clientCode
或dataVersion
适合较小的数据类型,例如byte
或short
,则可以将其用于区分。如果这些值中的某些值具有指定的值范围(例如,从1到100.000),则在方法中使用编译时检查,如果提供的值不合适则抛出异常(如果调用者可能会发生这种情况)错位的参数)。
如果您添加更多int
个参数,您的担忧就会更加合理。在这种情况下,只写一个包含所有参数的包装类:
public class Input {
private int clientCode;
private int version;
//other parameters
..
// getters and setters
}
通过使用显式getter和setter,您强制调用者关注提供的值。奖励 - 如果需要,您可以为某些参数添加默认值。方法签名现在是int generateId(Input input)
。
这是Joshua Bloch的Effective Java中记录的良好做法。
答案 2 :(得分:0)
你以正确的方式,进一步重构代码,制作高级ClientCode, Version
和Id
类。移除int包装器。让Id
了解如何生成版本,换句话说,在Id
类中填充代码。
Id generateId(ClientCode code, Version version) {
return new Id(code, version);
}
答案 3 :(得分:0)
正如Tom在注释中指出的那样,java没有办法避免这种问题。其他语言如python和scala支持命名参数来解决这个问题 虽然很好但没有必要。
Java依赖程序员正确理解和使用所有库。因此,应使用正确的文档注释并生成文档。
使用您建议的包装类的另一种方法是使代码繁琐且难以理解。更不用说添加额外的代码行了。
结论:依靠程序员的知识,并生成文档。不要使用包装类。因为这是java社区中广泛使用的方法。