11

私はグアバに少し慣れていません、そしてそれはスタイルです。私は間違いなくそれを掘り下げていますが、私がつまずき続けることの1つは、連鎖メソッドの順序です。私がこの問題を最も抱えていると思われるのは、化合物を使用する場合Orderingです。私は自分自身に次のような質問をし続ける必要があります:

  • どこへnatural行くの?
  • nullFirst(または最後の)どこに行きますか?
  • どっちnullsFirstがなに?(以下の例では、1つはホスト用、1つは姓用、もう1つは名用ですか?)

これが私が今取り組んでいたものの例です。面倒そうに見えますが、全部うまくまとめたかどうかはわかりません。私はそれをテストするためにいくつかのJUnitを持っています、そしてそれは大丈夫のようです、しかしそれらの風変わりな境界のケースは常にあります。

Ordering<Host> lastNameThenFirstNameOrdering = Ordering.natural().nullsFirst().onResultOf(new Function<Host, String>() {
    public String apply(Host host) {
        return host.getLastName();
    }}).compound(Ordering.natural().nullsFirst().onResultOf(new Function<Host, String>() {
    public String apply(Host host) {
        return host.getFirstName();
    }})).nullsFirst();

実際の質問について:これらのものがどのように実行されるかについて明確に定義されたルールはありますか?それは最後から最初のようですが、私はそれを伝えるのに苦労しています。

編集:私が置き換えようとしていた大きくて醜いコードを指摘したかっただけです:

    Ordering<Host> ordering2 = new Ordering<Host>() {
        public int compare(Host host1, Host host2) {
            if (host1 == null || host2 == null) {
                return host1 == host2 ? 0 : ((host1 == null) ? -1 : 1); 
            }
            if(host1.getLastName() != null || host2.getLastName() != null){
                if (host1.getLastName() == null) {
                    return -1;
                } else if (host2.getLastName() == null) {
                    return 1;
                }

                if (host1.getLastName().compareTo(host2.getLastName()) != 0) {
                    return host1.getLastName().compareTo(host2.getLastName());
                }
            }

            if (host1.getFirstName() == null) {
                return -1;
            } else if (host2.getFirstName() == null) {
                return 1;
            }

            return host1.getFirstName().compareTo(host2.getFirstName());
        }};
4

3 に答える 3

25

I think what you do is correct, but awfully ugly. Try this for readability:

Use an Enum

Move the functions to an enum that implements Function<Host, String>. Each of the enum items can provide it's own implementation.

enum HostFunctions implements Function<Host, String>{
    GETFIRSTNAME{

        @Override
        public String apply(final Host host){
            return host.getFirstName();
        }
    },
    GETLASTNAME{

        @Override
        public String apply(final Host host){
            return host.getLastName();
        }
    }

}

Indent your Code

Now reference those enum functions and indent your code properly. This is what it will look like:

final Ordering<Host> orderingByLastAndFirstName =
    Ordering
        .natural()
        .nullsFirst()
        .onResultOf(HostFunctions.GETLASTNAME)
        .compound(
            Ordering
                .natural()
                .nullsFirst()
                .onResultOf(HostFunctions.GETFIRSTNAME))
        .nullsFirst();

I'd say that makes everything much more understandable.

IDE Configuration

Regarding proper indentation (at least if you use Eclipse), see this question:

How to indent the fluent interface pattern “correctly” with eclipse?

Enums as Functions

Regarding the enum: this is called the enum singleton pattern. The Guava guys use it all over their code base. Read about it on wikipedia or in Effective Java, Item 3. Although those sources both talk about single-item enums, the approach is almost the same here.

于 2011-02-11T13:00:18.303 に答える
11

Each chaining call is "wrapping" the previous ordering into a new one, so you're right, the execution order can be thought of as "backwards".

I wrote and reviewed the Ordering class and I still regularly have to stop and scratch my head over the correct interleaving of nullsFirst(), and onResultOf() and reverse()!

于 2011-02-11T18:55:08.457 に答える
9

The following would be my preference for doing this, assuming you must be able to handle null hosts, first names and last names. To me, it seems like a non-null first name and last name ought to be a requirement of the Host class. And you should generally try to avoid allowing collections to contain null objects.

Ordering<Host> lastNameFirstNameOrdering = new Ordering<Host>() {
  @Override public int compare(Host left, Host right) {
    return ComparisonChain.start()
      .compare(left.getLastName(), right.getLastName(), Ordering.natural().nullsFirst())
      .compare(left.getFirstName(), right.getFirstName(), Ordering.natural().nullsFirst())
      .result();
  }
}.nullsFirst();

Alternatively, I'd take an approach similar to Sean's but break things down for readability.

Ordering<Host> lastNameOrder = Ordering.natural().nullsFirst()
    .onResultOf(Host.LAST_NAME);
Ordering<Host> firstNameOrder = Ordering.natural().nullsFirst()
    .onResultOf(Host.FIRST_NAME);
Ordering<Host> orderingByLastAndFirstName =
     lastNameOrder.compound(firstNameOrder).nullsFirst();

Keep in mind that you could also make these individual orderings static final fields of the class, allowing you to easily use them anywhere when sorting like Host.LAST_NAME_ORDER.

于 2011-02-11T20:51:00.030 に答える