У меня есть два метода, тот, который считает количество объектов, которые, как полагают, имеют нижнее значение, чем данный объект и другого, который считает количество объектов, которые имеют более высокое значение, чем данный объект. Как можно, вероятно, сказать, эти два метода практически идентичны:
public int countHigher(SomeObject a){
if (a == null){
throw etc...
}
int numberHigher = 0;
for (SomeObeject b : this.listOfSomeObjects) {
if (b.compareTo(a) == 1) {
numberHigher++;
}
}
return numberHigher;
}
public int countLower(SomeObject a){
if (a == null){
throw etc...
}
int numberLower = 0;
for (SomeObeject b : this.listOfSomeObjects){
if (b.compareTo(a) == -1){
numberLower++;
}
}
return numberLower;
}
Я осуществил рефакторинг методы для вызова закрытого метода:
private int coun(SomeObject a, int comparison){
if (a == null){
throw etc...
}
int number = 0;
for (SomeObeject b : this.listOfSomeObjects){
if (b.compareTo(a) == comparison){
number++;
}
}
return number;
}
Но я не нахожу это удовлетворение решения. Было бы возможно назвать закрытый метод с недопустимым целым числом (т.е. 10), и дополнительная проверка ошибок на этот случай довольно ужасна:
if (comparison < -1 || comparison > 1)
{
throw blah
}
Используя булевскую переменную является также неудовлетворительным, с тех пор в будущем довольно возможно, что я могу хотеть считать количество объектов равного значения.
У Вас есть альтернативное решение для рефакторинга?
Удачи,
Pete
Что бы я сделал:
int compare
. Это позволит вам правильно разделить проблемы.
Я согласен - прямое сравнение с целым числом ненадежно, поскольку результаты сравнения гарантированно возвращаются, например, меньше нуля, а не конкретно -1.
Для сравнения можно использовать предикат. Это инкапсулирует оценку условия, которое вы хотите подсчитать.Например,
abstract class ComparisonPredicate
{
private SomeObject a;
// set in constructor
public abstract boolean evaluate(SomeObject b);
}
class LessThanPredicate extends ComparisonPredicate
{
public boolean evaluate(SomeObject b) {
return a.compareTo(b)<0;
}
}
И затем метод count становится:
private int count(ComparisonPredicate comparison){
int number = 0;
for (SomeObeject b : this.listOfSomeObjects){
if (comparison.evaluate(b)){
number++;
}
}
return number;
}
Затем метод вызывается как:
SomeObject a = ...;
int lessThanCount = count(new LessThanPredicate(a));
Его можно использовать, чтобы найти количество объектов для любого типа оценки - не только меньше / больше, чем сравнение , но также равенство и любые другие отношения, определенные для вашего объекта.
В качестве альтернативы вы можете нормализовать значения сравнения, чтобы они были -1, 0, 1.
Используйте Enums
public class test {
private int count(Object a, Comparison c){
if (a == null){
throw new RuntimeException();
}
int number = 0;
for (Object b : new Object[] {null, null}){
if (b.compareTo(a) == c.val()){
number++;
}
}
return number;
}
public void test() {
System.out.println(count(null, Comparison.GT));
System.out.println(count(null, Comparison.LT));
}
}
class Object implements Comparable {
public int compareTo(java.lang.Object object) {
return -1;
}
}
enum Comparison {
GT { int val() { return 1; } },
LT { int val() { return -1; } };
abstract int val();
}
Если вы хотите расширить это для проверки равенства, вы просто добавите дополнительное значение в перечисление.
Аргументом должен быть Компаратор ( http://java.sun.com/j2se/1.5.0/docs/api/java/util/Comparator.html ) Затем вы можете вызвать метод с анонимным классом, чтобы избежать явной реализации Comparator, если он вам нужен только в одном месте. Если метод становится еще более сложным, вы можете определить свой собственный интерфейс, который инкапсулирует более сложную логику, например, логику внутри самого оператора if.
Также, если вас попросят проголосовать за реализацию labmdas (замыканий) на языке Java:)
Вы можете использовать перечисление
вместо int
. Кроме того, обычно a.compareTo (b)
может возвращать произвольное отрицательное число, когда a
меньше, чем b
, и произвольное положительное число, когда a
больше, чем b
.
Вот мое решение:
private static enum CountMode {CountHigher, CountLower};
private int count(SomeObject anObject, CountMode mode) {
if (a == null) {
// handle
}
int count = 0;
for (SomeObject o : this.listOfSomeObjects) {
int compare = o.compareTo(anObject);
if (compare > 0 && mode == CountMode.CountHigher) {
count++;
} else if (compare < 0 && mode == CountMode.CountLower) {
count++;
}
}
return count;
}
А вот модифицированный код для обработки и того же случая:
private static enum CountMode {CountHigher, CountLower, CountEqual};
private int count(SomeObject anObject, CountMode mode) {
if (a == null) {
// handle
}
int count = 0;
for (SomeObject o : this.listOfSomeObjects) {
int compare = o.compareTo(anObject);
if (compare > 0 && mode == CountMode.CountHigher) {
count++;
} else if (compare < 0 && mode == CountMode.CountLower) {
count++;
} else if (compare == 0 && mode == CountMode.CountEqual) {
count++;
}
}
return count;
}
Я согласен со многими другими; объект типа Predicate
здесь правильный, но, в частности, вы хотите вызвать точку mdma, предполагающую, что использование результата int
для Comparable.compareTo ()
неверно (или, по крайней мере, хрупкое) - гарантируется только то, что Comparable
возвращает положительное, 0 или отрицательное число, конкретные значения 1 и -1 не диктуются (и многие реализации не повторяют эти ). Опять же, я думаю, что предикат
- это то, что вам нужно, но в качестве обходного пути к этой проблеме и риску того, что люди перейдут в нестандартное
int, вы можете сделать:
/**
* ...
* @param int the sign of the comparison; will count objects whose .compareTo(a)
* returns an int with the same sign as this value
*/
private int count(SomeObject a, int comparisonSign){
...
for (SomeObject b : this.listOfSomeObjects){
if (Math.signum(b.compareTo(a)) == Math.signum(comparison)){
number++;
}
}
return number;
}
использование signum
означает, что вы всегда сравниваете значения -1,0, 0 или 1,0.
Опять же, я бы не рекомендовал этот подход (думаю, что он делает API очень неочевидным для начала), но решил выложить его здесь из любопытства.
Большинство ответов здесь рассмотрели ваши варианты, но я собираюсь добавить еще один: лучшую структуру данных.
В зависимости от характера ваших данных, SortedSet
или более расширенный NavigableSet
могут лучше служить вашим целям. В частности, представляют интерес эти 3 метода:
headSet (E toElement, логическое значение включительно)
: возвращает представление части этого набора, элементы которой меньше (или равны, есливключительно
] верно)toElement
.
tailSet (E fromElement, логическое значение включительно)
: Возвращает представление части этого набора, элементы которого больше (или равны, есливключительно
истинно)fromElement
.
subSet (E fromElement, логическое fromInclusive, E toElement, логическое toInclusive)
: возвращает представление части этого набора, элементы которого находятся в диапазоне отfromElement
доtoElement
.
Вот пример использования ( см. Также на ideone.com ):
import java.util.*;
public class NavigableSetExample {
public static void main(String[] args) {
NavigableSet<String> names = new TreeSet<String>(
Arrays.asList(
"Bob", "Carol", "Alice", "Jill", "Jack", "Harry", "Sally"
)
);
System.out.println(names);
// [Alice, Bob, Carol, Harry, Jack, Jill, Sally]
// "Carol" and up
System.out.println(names.tailSet("Carol", true));
// [Carol, Harry, Jack, Jill, Sally]
// below "Elizabeth"
System.out.println(names.headSet("Elizabeth", false));
// [Alice, Bob, Carol]
// who stands between "Harry" and "Sally"?
System.out.println(names.subSet("Harry", false, "Sally", false));
// [Jack, Jill]
}
}
Конечно, есть NavigableMap
, если карта больше подходит, чем набор.
SortedSet / Map
отличается от NavigableSet / Карта
Я бы предложил обернуть частный метод и использовать шаблон объекта функции:
Интерфейс для объекта функции:
interface Filter<T>{
boolean eligible(T t);
}
Подсчитайте метод оболочки более высокого уровня, чтобы показать идею:
public int countHigher(final SomeObject a)
{
return coun(a, new Filter<SomeObject>()
{
public boolean eligible(SomeObject b){
return a.compareTo(b) == -1;
}
});
}
частный вспомогательный метод, который считает подходящие объекты
private int coun(SomeObject a, Filter<SomeObject> filter){
//...
}