我正在考虑实现多个Comperator。现在我不确定如何以最有利的方式做到这一点,即仍然有能力进行依赖注入。
方法1:一个类是一个比较器(如this answer所示):
// file LexicographicComparator.java
class LexicographicComparator implements Comparator<Person> {
@Override
public int compare(Person a, Person b) {
return a.name.compareToIgnoreCase(b.name);
}
}
// file AgeComparator.java
class AgeComparator implements Comparator<Person> {
@Override
public int compare(Person a, Person b) {
return a.age < b.age ? -1 : a.age == b.age ? 0 : 1;
}
}
方法2:现在肯定可以这样做:
class PersonComperatorFactory {
public static Comparator<Person> getAgeComparator() {
return new Comparator<Person>() {
@Override
public int compare(Person a, Person b) {
return a.age < b.age ? -1 : a.age == b.age ? 0 : 1;
}
}
}
public static Comparator<Person> getLexicographicComparator() {
return new Comparator<Person>() {
@Override
public int compare(Person a, Person b) {
return a.name.compareToIgnoreCase(b.name);
}
}
}
}
第二种方法肯定会减少你的包的混乱(把它放到.domain包或.util包?或者创建一个新的.domain.comperators包?)。但这是一个好主意吗?我的意思是,除了较少的文件,这有什么好处吗?怎么样的代码重用或DependencyInjection - 它可能有缺点我还看不到?
我希望这不是“选择你最喜欢的”问题,但是为什么要使用其中一个可能有很多很好的理由。
非常感谢你。
PS:你会把它放在哪个包里(这可能是个人选择)?但这只是一个适合发表评论的问题。
答案 0 :(得分:2)
恕我直言,最好的方法是混合,因为你有一个入口点来获取所有比较器(你不需要知道每个比较器的名称:所有都在同一个java类中)你可以实例化所有的比较器作为一个豆。
public class LexicographicComparator implements Comparator<Person> {
@Override
public int compare(Person a, Person b) {
return a.name.compareToIgnoreCase(b.name);
}
}
// file AgeComparator.java
public class AgeComparator implements Comparator<Person> {
@Override
public int compare(Person a, Person b) {
return a.age < b.age ? -1 : a.age == b.age ? 0 : 1;
}
}
public class PersonComperatorFactory {
private static final Comparator<Person> NAME_COMPARATOR = new LexicographicComparator();
private static final Comparator<Person> AGE_COMPARATOR = new AgeComparator();
public static Comparator<Person> getLexicographicComparator() {
return NAME_COMPARATOR;
}
public static Comparator<Person> getAgeComparator() {
return AGE_COMPARATOR;
}
}
答案 1 :(得分:2)
在我看来,实现所需结果的最佳方法是使用包含所有静态比较器的实用程序类(将其置于.util包中)。
示例:
public class Comparators {
public static final Comparator<Person> PERSON_LEXICOGRAPHIC_COMPARATOR = new Comparator<Person>() {
@Override
public int compare(Person a, Person b) {
return a.name.compareToIgnoreCase(b.name);
}
}
public static final Comparator<Person> PERSON_AGE_COMPARATOR = new Comparator<Person>() {
@Override
public int compare(Person a, Person b) {
return a.age < b.age ? -1 : a.age == b.age ? 0 : 1;
}
}
}
或者以Java 8的方式:
public class Comparators {
public static final Comparator<Person> PERSON_LEXICOGRAPHIC_COMPARATOR = (Person a, Person b) -> a.name.compareToIgnoreCase(b.name);
public static final Comparator<Person> PERSON_AGE_COMPARATOR = (Person a, Person b) -> (a.age < b.age ? -1 : a.age == b.age ? 0 : 1);
}
我会直接使用静态实例,以免浪费内存和资源,因为它们在compare方法中不使用任何state属性。
答案 2 :(得分:1)
我可能会将比较器实现为enum
,我会将enum
放在与Person
相同的包中,或者将其嵌套在Person
类中,提供对比较字段的直接访问。
public enum PersonComparator implements Comparator<Person> {
AGE {
@Override
public int compare(Person a, Person b) {
return a.age < b.age ? -1 : a.age == b.age ? 0 : 1;
}
},
NAME {
@Override
public int compare(Person a, Person b) {
return a.name.compareToIgnoreCase(b.name);
}
}
}
答案 3 :(得分:1)
实现不可变比较器的最佳方法是将其实现为单例。根据Effective Java,第3项,首选方法是使用Enum单例:
这种方法在功能上等同于公共字段方法,除了它更简洁,免费提供序列化机制,并提供防止多个实例化的铁定保证,即使面对复杂的序列化或反射攻击。虽然这种方法尚未被广泛采用,但单元素枚举类型是实现单例的最佳方式。
对于比较器的位置,应考虑API的可发现性。如果你在一个单独的类或甚至一个单独的包中实现比较器,你的软件的用户(即其他程序员)可能只是忽略它们并且很想重新实现它们(导致代码和工作重复)。因此,为了帮助发现,创建静态方法,在Person类本身中返回比较器,或者采用convention similar to Guava并将它们放在一个名为对象复数的实用程序类中(例如Persons
)in相同的包裹。使用此约定允许另一个程序员在他们喜欢的IDE中键入Person(s).
,自动完成将显示所有可用的实用程序方法。
完整的代码如下所示:
public final class Persons {
private Persons() {}
public static Comparator<Person> getLexicographicComparator() {
return Comparators.LEXICOGRAPHIC;
}
public static Comparator<Person> getAgeComparator() {
return Comparators.AGE;
}
enum Comparators implements Comparator<Person> {
LEXICOGRAPHIC {
@Override
public int compare(Person a, Person b) {
return a.name.compareToIgnoreCase(b.name);
}
@Override
public String toString() {
return Persons.class.getName() + ".getLexicographicComparator()";
}
},
AGE {
@Override
public int compare(Person a, Person b) {
return a.age < b.age ? -1 : a.age == b.age ? 0 : 1;
}
@Override
public String toString() {
return Persons.class.getName() + ".getAgeComparator()";
}
}
}
}
请注意,由于实例化实用程序类没有意义,因此构造函数是私有的(根据Effective Java,Item 4)。此外,为了隐藏比较器实际上是枚举的事实,枚举具有包私有(默认)可访问性,并且覆盖toString()
方法以指向它们可公开访问的getter方法。
答案 4 :(得分:0)
工厂版本更加通用。
调用类不必知道真正的实现类。
此外,您可以添加单个入口点,该入口点将根据类型(此处基于公共Enumeration
)决定要返回的实现:
class PersonComperatorFactory {
public static enum Type {
AGE, LEXIC
}
public static Comparator<Person> getComparator(final Type comparatorType) {
switch (comparatorType) {
case AGE:
return getAgeComparator();
case LEXIC:
return getLexicographicComparator();
default:
return getLexicographicComparator();
}
}
private static Comparator<Person> getAgeComparator() {
return new Comparator<Person>() {
@Override
public int compare(final Person a, final Person b) {
return a.age < b.age ? -1 : a.age == b.age ? 0 : 1;
}
};
}
private static Comparator<Person> getLexicographicComparator() {
return new Comparator<Person>() {
@Override
public int compare(final Person a, final Person b) {
return a.name.compareToIgnoreCase(b.name);
}
};
}
}
答案 5 :(得分:0)
组合最有可能产生最佳效果。 选项1很好地封装了逻辑,但是直接访问这些类会产生很多混乱。你的工厂&#39;确实解决了这个问题,但我不同意这里实施逻辑的决定。
如果比较器的逻辑应该改变,这需要你改变工厂,这不是它的责任。
我建议保留您的初始类,然后从工厂返回这些类的实例。即使你有3个类,你只会使用其中的1个,所以杂乱应该是可管理的。
class PersonComperatorFactory {
public static Comparator<Person> getAgeComparator() {
return new LexicographicComparator();
}
public static Comparator<Person> getLexicographicComparator() {
return new AgeComparator();
}
}
我还提出了第三种选择,它提供零封装和无重用,但最大的灵活性。这可能不是你想要的,只是想确保它被考虑。
public class Demo {
public static void main(String[] args) {
List<Person> persons = Arrays.asList(new Person(), new Person(), new Person(), new Person());
SortPersons(persons, (p1, p2) -> p1.age < p2.age ? -1 : p1.age == p2.age ? 0 : 1);
}
private static void SortPersons(List<Person> persons, Comparator<Person> comparator){
//Or what ever logic you need the comparator for
persons.sort(comparator);
}
}
最后,如果您的.util包以任何方式(或能够)单独部署,我不会将Person的依赖项放入其中。在这种情况下,我会把它放在.default。