Обычно компараторы являются лучшим вариантом, если вы хотите сравнивать объекты, используя разные атрибуты (см., Например, How to compare objects by multiple fields). Однако в моем конкретном случае я не уверен в использовании компараторов.Сравнение объектов по различным атрибутам без использования компараторов
Проблема заключается в следующем: я определил общий интерфейс, который называется Node<S>
, который является общим различными компонентами. Существует также CostNode<S>
, который расширяет Node<S>
и ScoreNode<S>
, который простирается CostNode<S>
:
public interface Node<S> {
S getS();
// more methods...
}
public interface CostNode<S> extends Node<S> {
// This method smells really bad
int compareByCost(ComparableNode<S> node);
}
public interface ScoreNode<S> extends CostNode<S> {
// int compareByCost(CostNode<S> node) (from CostNode<S>)
int compareByScore(ScoreNode<S> node);
}
В этот момент кто-то может возразить: Вам не нужно CostNode и ScoreNode, вы можете использовать различные компараторов для сравнения узлы. Ничего страшного. Но «проблема» приходит сейчас:
У меня есть компонент, называемый Клиент, который использует ScoreNodes. Клиент требует узла завода, , предоставленный пользователем, который ответственен за создание ScoreNodes:
public class Client {
// ...
public Client(NodeFactory<S, ScoreNode<S>> nodeFactory){...}
public void process() {
while(...){
S current = get();
S old = getOld();
// ...
ScoreNode<S> next = this.nodeFactory.create(current,...));
// Comparisons performed
if (next.compareByCost(old) <=0){
//...
}
if (next.compareByScore(old) > 0){
// ...
}
}
}
}
Как вы можете видеть, поведение сравнения узлов внедренных в узлы, и тесно связана с завода (для разных узлов требуется другой завод и разные компараторы).
С другой стороны, если я использую компараторы, я должен предоставить Клиенту три компонента: CostComparator, ScoreComparator и NodeFactory. В этом случае, я могу использовать только Node<S>
и забыть о CostNode<S>
и ScoreNode<S>
:
public class ConcreteNodeCostComparator implements Comparator<Node<S>> {
public int compare(Node<S> a, Node<S> b){
return Double.compare(((ConcreteNode<S>)a).getCost(), ((ConcreteNode<S>)b).getCost());
}
}
public class ConcreteNodeScoreComparator implements Comparator<Node<S>> {
public int compare(Node<S> a, Node<S> b){
return Double.compare(((ConcreteNode<S>)a).getScore(), ((ConcreteNode<S>)b).getScore());
}
}
Однако мне не очень нравится эта альтернатива, потому что в этом случае я должен предоставить еще два компонента Клиенту, когда методы сравнения сильно зависят от узлов.
Я думаю, что в этом дизайне отсутствует что-то. О чем вы думаете?
Я видел красивый компаньон перечисления в другом посте. Хотя код элегантный, проблема в том, что мне нужно сопоставить значения с внутренним перечислением, убивая повторное использование ScoreNode. –