2016-12-26 2 views
3

Этот класс имеет три метода, которые делают то же самое, они ожидают три секунды загрузки страницы и возвращают true, если страница загружена.
Мой вопрос: как мне решить, какой фрагмент кода лучше?
Я знаю, что Lambda is preferred above Anon classes, но почему статический вложенный класс (pageLoaded3) не прав? Согласно this post он также должен быть подходящим.Lambda vs Anon vs Static Nested Class

public final class ExpectedConditions { 

    private ExpectedConditions() { 
    } 

    public static ExpectedCondition<Boolean> pageLoaded1() { 
     return (driver) -> { 
      try { 
       Thread.sleep(3000); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
      return !((JavascriptExecutor) driver).executeScript("return performance.timing.loadEventEnd", new Object[0]) 
        .equals("0"); 
     }; 
    } 

    public static ExpectedCondition<Boolean> pageLoaded2() { 
     return new ExpectedCondition<Boolean>() { 
      @Override 
      public Boolean apply(WebDriver driver) { 
       try { 
        Thread.sleep(3000); 
       } catch (InterruptedException e) { 
        e.printStackTrace(); 
       } 
       return !((JavascriptExecutor) driver) 
         .executeScript("return performance.timing.loadEventEnd", new Object[0]).equals("0"); 
      } 
     }; 
    } 

    public static ExpectedCondition<Boolean> pageLoaded3() { 
     return new PageLoaded(); 
    } 

    private static class PageLoaded implements ExpectedCondition<Boolean> { 
     @Override 
     public Boolean apply(WebDriver driver) { 
      try { 
       Thread.sleep(3000); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
      return !((JavascriptExecutor) driver).executeScript("return performance.timing.loadEventEnd", new Object[0]) 
        .equals("0"); 
     } 
    } 

} 

Это интерфейс типа возвращаемого из трех способов:

public interface ExpectedCondition<T> extends Function<WebDriver, T> {} 
+3

Ваши три метода функционально эквивалентны (ваш код будет делать то же самое). Что заставляет вас думать, что статический вложенный класс неуместен? – assylias

+0

_Как я могу решить, какой фрагмент кода лучше? _ Не могли бы вы попросить более конкретно то, что вы ищете? Мой общий ответ на этот вопрос - «самая читаемая версия» ... –

+0

Это мой вопрос. Я знаю все три работы, но я знаю, что в этой ситуации класс анонов не будет использоваться. Когда вы читаете о внутренних классах членов, они также явно говорят, что если внешние переменные класса не используются, используйте статический внутренний класс. Итак, существует ли правило или соглашение, в котором также говорится, что статический внутренний класс должен или не должен использоваться? Или это просто предпочтение? –

ответ

1

Как уже упоминалось в комментариях: функции эквивалентны. Итак, технически, разница предельная. Все подходы скомпилированы в аналогичный байт-код. Поэтому вопрос о том, какой из них является «лучшим», может быть дан только с учетом самого кода. И там могут быть применены различные критерии.

Прежде всего, как Питер Lawrey предложил в комментарии: Вы могли бы вытащить Thread.sleep вызов в вспомогательный метод:

private static void pause(long ms) 
{ 
    try 
    { 
     Thread.sleep(ms); 
    } 
    catch (InterruptedException e) 
    { 
     Thread.currentThread().interrupt(); 
    } 
} 

Кроме того, вы могли бы вытащить основные функциональные возможности, реализуемый там в вспомогательный метод:

private static Boolean execute(WebDriver driver) 
{ 
    pause(3000); 
    return !((JavascriptExecutor) driver) 
     .executeScript("return performance.timing.loadEventEnd", new Object[0]) 
     .equals("0"); 
} 

Используя этот метод, сходство между данными подходами становится еще более очевидным: они только разными способами вызова этого метода! И я думаю, что введение такого метода будет иметь дополнительные преимущества. Этот метод является четким «строительным блоком» с четким интерфейсом (и, надеюсь, с ясным JavaDoc).

Кроме того, этот метод будет открыть дверь для четвертой реализации - именно тот, который использует эталонный метод:

public static ExpectedCondition<Boolean> pageLoaded4() 
{ 
    return ExpectedConditions::execute; 
} 

Так теперь у вас есть четыре способа реализации желаемого functionaliy:

  1. с лямбда
  2. с анонимного класса
  3. С статического innner классом
  4. со ссылкой метода

прагматичен, можно утверждать о преимуществах и недостатках:

  • подхода 3, при статическом классе innner: Это не обязательно. Если статический внутренний класс равен оболочка вызова метода, это не служит реальной цели. Он вводит новое имя (PageLoaded в вашем случае) и новое обозначение в исходном коде - это означает, что программист должен найти класс PageLoaded, чтобы посмотреть, что он делает. В других случаях это может быть другая история. Например. если класс PageLoaded имел дополнительные методы или поля, но здесь это не так.

  • подход 2, с анонимного внутреннего класса: Можно было бы утверждать, что анонимный класс для функционального интерфейса просто «старомодным» или «предварительно Java8-путь» писать. Если тип, который должен быть реализован, является функциональным интерфейсом (т. Е. Имеет один абстрактный метод), то использование ссылки лямбда или метода является более коротким и более удобным и не имеет очевидных недостатков. (Конечно, это не было бы возможным, если тип, который должен быть реализован был несколько абстрактные методы Но опять же, это не тот случай.)

  • подход 1, с лямбда: Это стандартный способ реализации такого функционального интерфейса в Java 8. Я лично предпочитаю иметь короткие лямбда-тела. Лямбда как один в вашем вопросе

    return (driver) -> { 
        // "Many" lines of code, maybe even nested, with try-catch blocks 
    }; 
    

    ИМХО не так читаемая и лаконичная, и я обычно стараюсь держать эти тела как можно короче. В лучшем случае, { скобки } могут быть опущены - в сомнении, введя метод , который содержит лямбда-тело. И в этом случае можно часто сводить его до последнего подхода:

  • Подход 4, с ссылкой на метод. Это может быть немного субъективно, но я считаю это наиболее читаемым. Преимущество метода заключается в том, что он является «строительным блоком» с четкой функциональностью, которая может быть четко определена в JavaDocs. Более того, вы можете даже рассмотреть возможность опускания метода обертывания (например, pageLoaded4) и просто предложить метод как public. Пользователь, который раньше называли

    ExpectedCondition<Boolean> condition = 
        ExpectedConditions::pageLoaded4(); 
    

    может затем просто использовать метод непосредственно

    ExpectedCondition<Boolean> condition = 
        ExpectedConditions::execute; 
    

    Но это зависит от конкретного применения шаблона и предполагаемого интерфейса этого класса. Это может быть нежелательно, чтобы разоблачить этот метод напрямую.

+0

Очень хороший ответ, я ценю его. Моя проблема заключалась в том, что я нигде не мог прочесть, что подход 3 со статическим внутренним классом должен или не должен использоваться. И ты подвел итог. –