#java #pojo
#java #pojo
Вопрос:
У меня есть класс Java, который представляет корреляцию между двумя элементами (типичный POJO):
public class Correlation {
private final String a;
private final String b;
private double correlation;
public Correlation(String a, String b) {
this.a = a;
this.b = b;
}
public double getCorrelation() {
return correlation;
}
public void setCorrelation(double correlation) {
this.correlation = correlation;
}
}
Чтобы следовать правильной логике корреляции, если a равно b, тогда значение корреляции всегда должно быть 1.
Я мог бы добавить логику, изменяющую метод получения (игнорируя факт возможного нулевого значения для a):
public double getCorrelation() {
if (a.equals(b)) {
return 1D;
} else {
return correlation;
}
}
Что меня беспокоит, так это добавление этой логики в метод получения, должен ли я изменить имя метода или его документирование следует считать достаточным?
Комментарии:
1. Мне также не нравится этот подход, потому что в Kotlin мы используем синтаксис доступа к свойствам, а kotlin совместим с Java. Таким образом, люди также могут совершать ошибки, если в будущем код будет перенесен на kotlin с java
Ответ №1:
Еще в первые дни Java пары getter / setter использовались для идентификации свойств компонентов именно с целью обеспечения возможности определения концептуальных атрибутов, реализуемых посредством вычислений, а не простой переменной-члена.
К сожалению, с течением времени программисты все больше и больше полагаются на то, что геттеры / сеттеры являются просто средствами доступа / мутаторами для базовых атрибутов, тенденция, которая была своего рода официальной с введением термина POJO для идентификации объектов, которые имели только геттеры и, возможно, сеттеры в качестве методов.
С другой стороны, полезно отличать объекты, которые выполняют вычисления, от объектов, которые просто переносят данные; Я думаю, вам следует решить, какой тип класса вы хотите реализовать. На вашем месте я, вероятно, сделал бы корреляцию дополнительным аргументом конструктора и проверил бы его достоверность там, а не в вашем геттере. Ваш Correlation
не может быть вычислительным объектом, поскольку у него недостаточно информации для выполнения каких-либо вычислений.
Комментарии:
1. 1; это вся правда! Важно различать объекты реального объекта и немые объекты передачи данных.
2. Я согласен, за исключением последнего предложения. Я думаю, что корреляция может быть вычислительным объектом, поскольку она (по крайней мере) может вычислять корреляцию, когда a равно b .
3. @Jala: Корреляция — это не то, что равно 1, когда a и b равны и не определены в противном случае: одно дело проверять аргументы, а другое — вычислять функцию указанных аргументов.
4. Даже если логика была определена в другом классе, вы бы добавили ограничение значения для метода setter?
5. Если подумать, вероятно, нет. Эта логика относится к тому месту, где фактически вычисляется корреляция, и этот тест является очень частичным и, вероятно, избыточным. С другой стороны, если вам это нужно, это лучшее место для его размещения 😉
Ответ №2:
Побочные эффекты в геттерах и сеттерах, как правило, не очень хорошая идея, поскольку обычно они не ожидаются и могут привести к сложным ошибкам. Я бы предложил создать метод «correlate ()» или что-то еще, что в данном случае не является геттером.
Надеюсь, это поможет.
Комментарии:
1. Я не вижу побочного эффекта в его втором геттере. Одно исключение, первый получатель может выдать ошибку округления. Но это сделало бы второй геттер предпочтительнее.
2. Согласен. В общем, наилучшая практика — заставить метод делать только одно, если это возможно, и избегать побочных эффектов любой ценой. Думайте об этом как о принципе единой ответственности, применяемом к уровню метода.
3. я согласен, что помещение логики в геттеры / сеттеры обычно не является отличной идеей, но иногда вам могут понадобиться некоторые побочные эффекты — например, пользовательский объект может зашифровать / расшифровать пароль в установщике / получателе
4. @aishwarya согласился с хорошей точкой зрения, чтобы прояснить мой ответ, побочные эффекты или операции, выполняемые в сеттерах / геттерах, должны быть сопряжены, чтобы использовать ваш пример… если вы зашифруете значение в set, вы расшифруете его при get.
Ответ №3:
Имеет больше смысла применять значение во setCorrelation(...)
время . Например,
public void setCorrelation(double correlation) {
if (a.equals(b)) {
if (Math.abs(correlation - 1D) > EPSILON) {
throw new InconsistentException(...);
}
this.correlation = 1D;
} else {
this.correlation= correlation;
}
}
Я бы также подумал о том, чтобы сделать свойство correlation равным нулю, где a null
указывает, что корреляция еще не установлена.
Ответ №4:
Учитывая, что корреляция является каким-то образом / иногда «производным» значением от a и b (т. Е. Оно равно 1, если a равно b, но оно может быть вычислено каким-то оригинальным способом в зависимости от (a, b), хорошим вариантом может быть вычисление корреляции в конструкторе и выдача исключения IllegalArgumentException в setCorrelationесли vaule нарушает внутреннюю логику объекта:
public class Correlation {
private final String a;
private final String b;
private double correlation;
public Correlation(String a, String b) {
this.a = a;
this.b = b;
calculateCorrelation();
}
protected calculateCorrelation() {
// open to more complex correlation calculations depending on the input,
// overriding by subclasses, etc.
if (a.equals(b)) {
this.correlation = 1D;
} else {
this.correlation = 0;
}
}
public double getCorrelation() {
return correlation;
}
public void setCorrelation(double correlation) throws IllegalArgumentException {
if (a.equals(b) amp;amp; correlation != 1D) {
throw new IllegalArgumentException("Correlation must be 1 if a equals b");
}
this.correlation = correlation;
}
}
Следуя этой схеме, вы также можете «генерировать» свой класс корреляции.
Комментарии:
1. это хороший подход, но логика «a.равно (b) тогда 1» повторяется в классе дважды.
Ответ №5:
Я бы сказал, используйте что-то вроде GetValue()
Комментарии:
1. В конце концов, это «get» часть имени, которая меня убивает.
Ответ №6:
Если бы я использовал ваш класс, я бы ожидал, что getCorrelation() вернет корреляцию. На самом деле, я бы, вероятно, перепроектировал класс, чтобы иметь статический метод correlateElements(строка a, строка b), который возвращает double .
Ответ №7:
В случаях, когда a != b, как вычисляется корреляция?
Если корреляция вычисляется в основном из a и b, setCorrelation() следует удалить. Точка. Корреляция должна быть вычислена в конструкторе или в методе getCorrelation(). Одним из принципов ООП является группирование связанных данных и логики, поэтому вычисление корреляции в идеале должно выполняться в классе, который вы умно назвали Correlation
. Если вычисление чрезвычайно сложное, оно может быть в другом месте (например, DIP), но вызов должен быть выполнен из Correlation
.
Если корреляция не вычисляется из a и b, я действительно не понимаю класс, поэтому «это зависит». Если a равно b, и кто-то вызывает setCorrelation(0.0), заключается ли контракт в том, чтобы спокойно игнорировать вызов и оставить корреляцию на 1.0 или выдать исключение? И, если я пишу вызывающий код, я нахожусь в затруднительном положении, поскольку я не знаю, что произойдет, если я попытаюсь установить корреляцию (0.0), потому что я понятия не имею, что такое a и b, или же везде, где я делаю вызов, я вынужден идти if (getA().equals(getB()))
и т. Д. … Или перехватыватьисключение и делать, э, что??? Что нарушает DRY и именно поэтому в ООП говорится, что логика и данные должны быть сгруппированы вместе в классе.