ComperatorFactory使用静态方法还是每个都在自己的类中?

时间:2016-01-15 10:13:52

标签: java design-patterns compare

我正在考虑实现多个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:你会把它放在哪个包里(这可能是个人选择)?但这只是一个适合发表评论的问题。

6 个答案:

答案 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。