英文:
Why is it bad practice to store calculated values in Java classes, and how can I avoid it?
问题
我对Java还不熟悉,在研究生课程中正在学习这门语言。现在才过去几周时间。我刚刚完成了一个实验,然后教授告诉我“我们不打算在我们的类中存储计算出来的值”,并给了我一个最佳实践文档的链接。我看了那个文档,但是没找到关于计算出来的值的内容。
我想他指的是这段代码:
public double getCalories() {
this.calories = (((this.weight * 0.12) * 9) + ((this.weight * 0.09) * 4)
+ ((this.weight * 0.02) * 4));
return this.calories;
}
我必须使用体重来计算卡路里的数量。这是我在这个类中唯一真正进行的计算,所以我想这可能是问题所在。有没有更好的编写方式?那应该如何进行这个计算呢?
英文:
I'm new to Java, and I am in a graduate program learning the language. It's only been a few weeks. I got back a lab I just worked on, and the professor told me "we're not going to store calculated values in our classes" and referred me to a Best Practices document. I looked at the document, but I don't see a reference to calculated values.
I think he is referring to this piece of code:
public double getCalories() {
this.calories = (((this.weight * 0.12) * 9) + ((this.weight * 0.09) * 4)
+ ((this.weight * 0.02) * 4));
return this.calories;
}
I had to use the weight to calculate the amount of calories. This is the only real calculation I am doing in this class, so I assume this is the problem. Is there a better way to write this? How can you perform the calculation then?
答案1
得分: 4
你需要问问你的教授,这并不是“Java的做与不做”清单中的重点。然而,针对你的特定代码片段,也许你的教授指的是一个稍微不同的观点:
对于你在这里所做的事情,实际上没有任何目的,或者如果有的话,你的代码是错误的。
只有两种可能性:
-
这是你整个代码库中唯一涉及
calories
字段的地方,或者 -
还有其他地方在使用这个字段。
选项1 - 这是唯一使用它的地方。
那么它就是无用的。每次有人调用 getCalories()
时,你都会重新计算卡路里。Java不是巫术;如果调用了一个方法,其中的每一行都会按顺序执行。像这样添加一个字段只是意味着Java会将结果存储在那个字段中。这并不意味着Java下一次会跳过计算!
因此,你的 getCalories()
调用将进行计算,存储结果并返回结果。存储的结果在任何地方都没有被使用,完全浪费了空间。修复方法:只需...不要存储它。删除这个字段。让这个方法成为一行代码(将 this.calories =
替换为 return
)。
选项2 - 你在其他地方使用它
假设在这个类中你还有另一个方法:
public boolean exceedsRecommendedDaily() {
return this.calories > 2000;
}
那么这段代码是__错误的__ - 如果我调用了这个方法,那么卡路里字段仍然是0,并且会一直保持为0,直到有人调用 getCalories()
。我猜我们可以通过像这样记录这个行为来修复它:
/**
* 计算这个食物项目本身是否超过了每日推荐摄入量。
* 注意:如果你之前没有在这个对象上调用 `getCalories()`,
* 这个方法会直接对你撒谎!
*/
但我想我们都可以同意这意味着这个方法很愚蠢。
不,为什么不像这样做:
public boolean exceedsRecommendedDaily() {
return getCalories() > 2000;
}
完成。不再需要愚蠢的警告了。
那么...这是最佳实践吗?
不,不是。_如果_计算需要足够长的时间,计算的结果经常需要,而且对象是不可变的(没有设置方法/在构建后字段不能更改)或者每次字段更新都值得清除缓存值,那么缓存值就是一个好主意。
例如,Java自己的 java.lang.String
缓存了哈希码,因为计算哈希码是相当昂贵的操作(至少需要检查每个字符。因此在包含100万个字符的字符串中,这需要一段时间!),它可能会被频繁调用,并且字符串是不可变的。
英文:
You'd have to ask your prof, that is not exactly front and center in the 'dos and donts of java' list. However, for your specific snippet, perhaps your prof is getting at a slightly different point:
There is absolutely no purpose whatsoever to what you are doing here, or, if there is, your code is broken.
There are only two options:
-
This is the one and only place in your entire codebase that refers to the
calories
field, or -
There are other places where you use that field.
option 1 - this is the only place you use it.
Then it's useless. You calculate the calories, every time someone calls getCalories()
. Java is not voodoo magic; if a method is called, each line in it is executed in order. Adding a field like this just.. also means java will store that result in that field. It doesn't mean that java will skip the calculation next time!
So, your getCalories()
call will do the calculation, store the result AND return the result. The stored result is not used anywhere, and is a total waste of space. Fix: Just.. don't store it. Delete the field. Make that method a oneliner (replace this.calories =
with return
.
option 2 - you use it elsewhere
let's say you have another method in this class:
public boolean exceedsRecommendedDaily() {
return this.calories > 2000;
}
then this code is BROKEN - if I invoke this method, then the calories field is still 0 and will remain 0 until someone calls getCalories()
. I guess we can fix it by documenting this behaviour like so:
/**
* Calculates if this food item on its own exceeds recommended daily intake.
* NB: If you haven't called `getCalories()` earlier on this object,
* this method will straight up lie to you!
*/
but I think we can all agree that means the method is idiotic.
No, why not just do it like this:
public boolean exceedsRecommendedDaily() {
return getCalories() > 2000;
}
tada. No need for silly caveats any more.
So.. is it a best practice?
No, it is not. IF the calculation takes long enough, and the result of the calculation is needed often enough, and the object is immutable (has no set methods / none of the fields can ever change after construction) or its worthwhile for every field update to also clear out a cached value, then it is a good idea to cache the value.
For example, java's very own java.lang.String
caches the hashcode, because calculating that is quite an expensive operation (it at least requires inspecting every character. So in a string of 1 million characters, that takes a while!), it can be called a ton, and strings are immutable.
答案2
得分: 0
你的函数计算卡路里并返回计算出的值。在你发布的代码中没有存储返回值的理由;它没有被使用。
public double getCalories() {
return (((this.weight * 0.12) * 9) + ((this.weight * 0.09) * 4)
+ ((this.weight * 0.02) * 4));
}
如果恰好有其他未显示的代码使用了this.calories的值,那么getCalories方法就存在另一个缺陷。它被称为'getCalories',如果它为其他方法保存了值,那么当没有先调用getCalories时,那个其他方法可能会出现故障。
英文:
Your function computes calories and returns the computed value. There's no reason in your posted code for storing a copy of the returned value; it is nowhere used.
public double getCalories() {
return (((this.weight * 0.12) * 9) + ((this.weight * 0.09) * 4)
+ ((this.weight * 0.02) * 4));
}
If it so happens that there is other code, not shown, that uses the value of this.calories, then the getCalories method has another flaw. It is called 'getCalories' and it is surprising if it saves away the value for some other method; that other method will likely malfunction if getCalories is not called first.
答案3
得分: 0
"我们不会在我们的类中存储计算出来的值",
我个人认为这个说法不够清楚,因为没有背后的上下文。我猜你的指导老师的意思是不要将冗余信息存储到全局字段中。
在创建类时,你会出于不同的原因将数据封装在其中,你可以更多地了解面向对象编程语言的概念。在你的情况下,你的类保存了一个实体的信息。
例如:
class car {
int engineVersion
String modelName
}
你看,engineVersion
和 modelName
是对象 car
的重要信息,当它被初始化时,你可以随后访问它。当你存储信息时,肯定会占用空间。然而,对于你的 getCalories
方法,假设你自己的类不需要它(即:在其他地方不使用它),你可以直接返回一个结果,这样你就可以节省资源。
英文:
"We're not going to store calculated values in our classes",
I personally think this statement is not clear, given that there is no context behind it. I guess you instructor means not storing redundant information to global field.
When creating a class, you are going to encapsulate data inside it for different reasons, you can look more at the concept of OOP language for details. In your case, your class hold information of an entity.
For example:
class car {
int engineVersion
String modelName
}
You see, engineVersion
and modelName
are important information of object car
when it is initilized, so you can access it later on. When you store information, it will certainly cost space. However, for you method getCalories
, assuming your own class doesn't need it (i.e: Not using it elsewhere), you can just directly return a result, then you can save resource.
答案4
得分: 0
你的“教授”是错误的:没有统治这个问题的“最佳实践”。
答案是“取决于情况”。
如果计算很昂贵,考虑将其存储,但你需要在设置器中设置钩子,以便在状态更改时使计算无效。这是一种缓存形式。它会使类变得有些复杂,但如果为了性能而牺牲简单性是可以接受的,就可以安全地进行,而且是合理的。
如果该类是不可变的,你就不需要担心状态的变化,因此可以计算它,理想情况下惰性地(即按需)进行一次,并在不具有任何此类钩子的情况下存储该值。
如果计算很廉价,每次计算都更清晰。
英文:
Your "professor" is wrong: There is no "best practice" governing this.
The answer is "it depends".
If the calculation is expensive, consider storing it, but you'll need hooks in setters to invalidate the calculation if state changes. This is a form of caching. It complicates the class somewhat, but can be done safely and is justified if sacrificing simplicity for performance is acceptable.
If the class is immutable, you don't need to worry about state changing, so you can calculate it, ideally lazily (ie on demand), once and store the value without having any such hooks.
If the calculation is cheap, calcualting it every time is cleaner.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论