java - 将属性存储在实例变量中还是使用方法计算?
问题描述
我第一次遇到这种情况是这样的:一个Box
带有包含项目的列表的类。所有这些项目都有一个int weight
实例变量。现在,要获得 的权重Box
,是否应该使用实例变量weight
来跟踪它?或者你应该使用一种方法calculateWeight()
来获得重量?
public class Box {
private ArrayList<Item> list; // Item objects in the list, all Item objects have a weight
private int weight; // Should I: use an instance variable as is seen here <- ?
public int calculateWeight() { // or use a method to calculate the weight each time?
int sum = 0;
// for all items get the weight and add it together
return sum;
}
}
现在在这个例子中,使用方法而不是实例变量对我来说更有意义,因为否则weight
每次Box
添加或删除项目时都必须更新实例变量。
但现在我正在研究可读性分数计算器(JetBrains 上的项目),我发现我应该做的事情不太明显。可读性分数计算器获取文本并查找句子、单词、字符、音节等的数量,并使用简单的公式来计算可读性分数。目前,我一直在通过调用构造函数中计算每个的方法将所有数量的句子、单词、字符等存储在实例变量中,但我不确定这是否是一个好习惯(感觉有点混乱大部头书)。另一种方法是不将它们存储在实例变量中,而仅在每次需要它们时使用这些方法来获取数量。这就是代码现在的样子:
public abstract class ReadabilityScoreCalculator {
protected String name;
protected String text;
protected int wordCount;
protected int sentenceCount;
protected int charCount;
protected int syllableCount;
protected int polySyllableCount;
protected double score;
protected int age;
public ReadabilityScoreCalculator(String text, String name) {
this.text = text;
this.name = name;
this.wordCount = this.countWords();
this.sentenceCount = this.countSentences();
this.charCount = this.countCharacters();
this.syllableCount = this.countSyllables();
this.polySyllableCount = this.countPolySyllables();
this.score = this.calculateAndReturnScore();
this.age = this.getAgeGroup();
}
private int countWords() {
return this.getArrayOfWords().length;
}
private String[] getArrayOfWords() {
return this.text.split("[ ]+");
}
private int countSentences() {
return this.text.split("[.!?]").length;
}
private int countCharacters() {
String textWithoutSpaces = this.removeSpaces();
return textWithoutSpaces.length();
}
private String removeSpaces() {
return this.text.replaceAll("[ ]+", "");
}
private int countSyllables() {
String[] words = this.getArrayOfWords();
int amountOfSyllables = Arrays.stream(words)
.mapToInt(word -> this.countSyllablesInWord(word))
.reduce(0, (previousCount, amountOfSyllablesInWord) -> previousCount + amountOfSyllablesInWord);
return amountOfSyllables;
}
private int countSyllablesInWord(String word) {
int amountOfSyllablesInWord = 0;
for (int i = 0, n = word.length(); i < n; i++) {
char character = word.charAt(i);
if (this.isCharVowel(character)) {
if (this.isCharVowel(word.charAt(i - 1)) || this.areWeAtTheLastCharAndDoesItEqualE(i, word)) {
continue;
}
amountOfSyllablesInWord++;
}
}
return (amountOfSyllablesInWord == 0) ? 1 : amountOfSyllablesInWord;
}
private boolean isCharVowel(char character) {
String charAsString = String.valueOf(character);
return charAsString.matches("(?i)[aeiouy]");
}
private boolean areWeAtTheLastCharAndDoesItEqualE(int index, String word) {
int wordLength = word.length();
char currentCharacter = word.charAt(index);
return (index == (wordLength - 1) && currentCharacter == 'e');
}
private int countPolySyllables() {
String[] words = this.getArrayOfWords();
int amountOfPolySyllables = Arrays.stream(words)
.mapToInt(word -> this.countSyllablesInWord(word))
.filter(amountOfSyllablesInWord -> amountOfSyllablesInWord > 2)
.reduce(0, (previousCount, amountOfPolySyllablesInWord) -> previousCount + amountOfPolySyllablesInWord);
return amountOfPolySyllables;
}
private double calculateAndReturnScore() {
return this.calculateScore();
}
abstract double calculateScore();
public void setScore(double score) {
this.score = score;
}
public void printResults() {
System.out.println(this.name + ": " + this.score + " (about " + this.age + " year olds).");
}
public void setAgeGroup() {
this.age = this.getAgeGroup();
}
private int getAgeGroup() {
int ageGroup = 0;
switch(this.roundUpAndParseToInt(this.score)) {
case 1:
ageGroup = 6;
break;
case 2:
ageGroup = 7;
break;
case 3:
ageGroup = 9;
break;
case 4:
ageGroup = 10;
break;
case 5:
ageGroup = 11;
break;
case 6:
ageGroup = 12;
break;
case 7:
ageGroup = 13;
break;
case 8:
ageGroup = 14;
break;
case 9:
ageGroup = 15;
break;
case 10:
ageGroup = 16;
break;
case 11:
ageGroup = 17;
break;
case 12:
ageGroup = 18;
break;
case 13:
ageGroup = 24;
break;
case 14:
ageGroup = 24;
break;
}
return ageGroup;
}
public int roundUpAndParseToInt(double number) {
return (int) Math.ceil(number);
}
}
一种或另一种是否被认为是好的做法?还是真的视情况而定?我可以看到该方法的计算成本更高,但提供了更多确定性。我上面的代码的任何其他问题也可能会被调出。
编辑:这是一个抽象类,该calculateScore()
方法应该由继承自该类的类填充。因此,可以使用多个不同的公式来计算可读性分数。
解决方案
通常,省略使用基于计算的实例变量以支持检索方法。更重要的是您的代码是否简洁地记录了自身并且易于维护。
通过使用实例变量,每次修改它们各自的属性时都需要更新它们,这会混淆修改这些变量的方法的可读性。
例如,这里有一些修改list
和更新weight
成员变量的简单方法:
public void addItem(Item item) {
this.list.add(item);
this.weight += item.weight;
}
public Item removeItem(int i) {
Item item = this.list.remove(i);
this.weight -= item.weight;
return item;
}
public void setList(ArrayList<Item> list) {
this.list = list;
this.weight = this.calculateWeight();
}
因为您正在执行方法名称中未描述的附加操作,所以这些方法不是自记录的。您可以改为将它们称为addItemAndUpdateWeight()
,但是如果有更多成员变量要更新怎么办?addItemAndUpdateMemberVariables()
太模糊了,很快这些方法就会变得非常混乱且难以维护。
你可以添加一个辅助函数来更新你的成员变量,但是你仍然需要在每个这样的方法中调用它,并且每次你添加一个新的成员变量时,你也必须更新这个方法。
另外,如果这些方法很昂贵,最好只在需要时执行计算。
更好的解决方案是通过隔离方法的功能来保持简单:
public void addItem(Item item) {
this.list.add(item);
}
public Item removeItem(int i) {
return this.list.remove(i);
}
public void setList(ArrayList<Item> list) {
this.list = list;
}
马上,你就知道这些方法的作用了,而且没有什么鬼鬼祟祟的代码让你以后头疼。另外,现在您不需要在构造函数中执行这些操作。
我确信可以为更复杂的情况(如索引)提出案例,但这似乎超出了本次讨论的范围。
推荐阅读
- r - 我怎样才能最好地总结 R 中的下表数据框?
- php - 仅获取带有 IP 和地址的 URL 内容
- python - 生成器表达式使用生成器创建后分配的列表
- android - 不允许到 server.com 的明文 http 流量
- android - EditText 绑定导致 int 变量的 NULL / 0 文本
- android - Kotlin - 如何使用循环获取对象值?
- c# - 从位图的字节数组中获取像素数据
- ios - 当父 UIview 的约束减少时,如何使 swift UIview 子元素自动缩小?
- java - 在 bitbucket 中添加 spinnaker webhook,在空对象异常上无法获取属性“用户名”
- css - 继承 web.assets_backend 时出错