2013-04-15 2 views
2

Обычно компараторы являются лучшим вариантом, если вы хотите сравнивать объекты, используя разные атрибуты (см., Например, 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()); 
    } 
} 

Однако мне не очень нравится эта альтернатива, потому что в этом случае я должен предоставить еще два компонента Клиенту, когда методы сравнения сильно зависят от узлов.

Я думаю, что в этом дизайне отсутствует что-то. О чем вы думаете?

ответ

1

Вы должны взглянуть на ответ Боуна в связанной вами теме. (http://tobega.blogspot.fr/2008/05/beautiful-enums.html)

Вы можете использовать этот вид перечисления в интерфейсе ScoreNode (или в другом месте) и использовании:

ScoreNode.Order.ByCost.compare(node1, node2); 
ScoreNode.Order.ByScore.compare(node1, node2); 

Вы не должны предоставлять какие-либо другие компоненты для вашего клиента.

+0

Я видел красивый компаньон перечисления в другом посте. Хотя код элегантный, проблема в том, что мне нужно сопоставить значения с внутренним перечислением, убивая повторное использование ScoreNode. –