为两个完全不同的类创建一个比较器是不好的做法吗?

时间:2016-03-03 06:57:23

标签: java

我想知道是否可以将两个比较器组合成一个比较器,用于两个完全不同的类。对象将按字母顺序按字符串类型name属性进行排序,该属性存在于两个类中,这就是我创建单个比较器的原因。

/**
 * Comparator to sort Book or Magazine alphabetically based on
 * their String type name property.
 */    
public class BookOrMagazineComparator implements Comparator<Object> {

        @Override
        public int compare(Object o1, Object o2) {
            if (o1 != null && o2 != null) {
                if (o1 instanceof Book && o2 instanceof Book) {
                    Book b1 = (Book) o1;
                    Book b2 = (Book) o2;
                    return b1.getName().compareTo(b2.getName());
                } else if (o1 instanceof Magazine && o2 instanceof Magazine) {
                    Magazine m1 = (Magazine) o1;
                    Magazine m2 = (Magazine) o2;
                    return m1.getName().compareTo(m2.getName());
                }
            }
            return 0;
        }

    }

如果我使用上述方法会有任何副作用吗?

编辑:我想补充一点,Book / Magazine实际上是枚举。

4 个答案:

答案 0 :(得分:6)

这是基于意见的,但我称之为不好的做法,因为它打破了每个班级只应服务于一个目的的规则(AKA Single Responsibility principle)。

此外它可能已被破坏,因为如果您在MagazineBook旁边传递任何内容(例如InputStreamURL以举例说明),则会返回0意思是他们是平等的。你通过实施Comparator<Object>而放弃了类型安全,这是一种耻辱。

正如评论中所建议的,找到你关心的两个类的通用接口并为该接口实现比较器是个好主意。在这种情况下,它将是完美的意义。

建议:您说这些课程是无关的,但它们有一些共同之处 - name如果您有NamedItem接口Magazine和{{} 1}}实现你可以创建Book,你就可以了。

注意:您添加的类是枚举。没问题。 Enum can easily implement interface

答案 1 :(得分:1)

  

如果我使用上述方法会有任何副作用吗?

是。紧耦合。您的比较器类将依赖于它引用的每个类。这将是重复使用的轻微障碍,因为如果没有它引用的类,它将无法存在。

正如另一个答案所指出 - 一个班级应该只服务于一个目的。那是single responsibility principle,这种做法略微违反了这一原则。

答案 2 :(得分:1)

当你这样做时,你会失去很多Java类型的安全性。

比较类型为Comparator<Book>的比较器的一大好处是,如果我有List<DVD>,我不会意外地尝试使用该比较器对其进行排序;编译器甚至不会让我。但是根据您的方法,我的列表可以是任何的列表:书籍,DVD,船等。如果列表恰好是所有书籍的列表或所有杂志的列表在运行时间,我可能会得到令人惊讶的行为。

请注意,您的比较器会破坏传递性。如果我有一本书{Aaa},一本杂志{Bbb}和一本书{Ccc},那么:

  • 因为最后return 0
  • 而预订{Aaa} ==杂志{Bbb}
  • 杂志{Bbb} ==预订{Ccc}(同上)
  • Book {Aaa}应该= =通过及物性预订{Ccc},但事实上
  • 预订{Aaa}&lt;书{CCC}

如果你解决了这个问题(例如,通过在方法的末尾抛出异常而不是返回0),那么令人惊讶的行为是排序将崩溃,除非你有一个同类的书籍或杂志列表。如果你保留了破损的合同,那么令人惊讶的行为是未定义的,并且可能会导致一个未正确排序的列表(因为排序算法会假设它需要比较哪些元素,只有当比较器是传递的)。

答案 3 :(得分:0)

是的,不建议这样做。此外,您只是将“杂志”与“杂志”和“反对书籍”进行比较。

如果说,您只打算通过名称或类似属性比较这些不同类中的对象,您可以使用层次结构并按基类类型进行比较:

class ReadingMaterial
{
    protected String name;
    //other attributes here..
}

class Magazine extends ReadingMaterial
{

}

class Book extends ReadingMaterial
{

}

现在您只需要实现Comparator一次,而不是为所有类型实现它:

class ReadingMaterialComparator implements Comparator<ReadingMaterial>
{
    //implements comparison here..
}

使用上述方法,即使您需要添加新类型的ReadingMaterial,例如NewsPaper。您甚至不必编辑比较器,它仍然可以工作。实质上,它使您的实施 更具可扩展性 更少耦合