Рубрики
Без рубрики

Чистый код использования лямбды в Java 8

Что такое лямбда-функция в java? Официально (документы java 8) срок действия лямбда-выражений в основном истекает… С тегами java, lambda, качество кода.

Что такое лямбда-функция в java? Официально (документы java 8) лямбда-выражения в основном выражают экземпляры функциональных интерфейсов (интерфейс с одним абстрактным методом называется функциональным интерфейсом. Примером может служить java.lang. Работоспособный). Лямбда-выражения реализуют единственную абстрактную функцию и, следовательно, реализуют функциональные интерфейсы

Лямбда-выражения реализуют единственную абстрактную функцию и, следовательно, реализуют функциональные интерфейсы

  • Позволяет обрабатывать функциональность как аргумент метода или код как данные.
  • Функция, которая может быть создана без принадлежности к какому-либо классу.
  • Лямбда-выражение может передаваться так, как если бы оно было объектом, и выполняться по требованию. ( от GeeksforGeeks https://www.geeksforgeeks.org/lambda-expressions-java-8/ )

Прежде чем я расскажу о чистом коде при использовании java-лямбд, позвольте мне показать вам следующий код:

class Menu{
    private String menuName;
    private Integer price;
    private String menuType;
    //AllArgsConstructor
    //Getters and Setters
}
//enum type
enum MenuType{
    VEGETABLES("vegetables"),  
    FRUIT("fruit"),  
    CEREAL("cereal"),  
    MEATS("meats");  

    private final String menuTypeCode;  

    MenuType(String menuTypeCode) {  
         this.menuTypeCode = menuTypeCode; 
     }  
    public String getMenuTypeCode(){  
         return this.menuTypeCode;  
     }  
}

/// in main class
Collection menus =  new  ArrayList<>(){{
         add(new Menu("Chicken",1300,"meats"));  
         add(new Menu("Pig",2500,"meats"));  
         add(new Menu("CocoCrunch",450,"cereal"));  
         add(new Menu("Milk",500,"drink"));
}};
Collection menuNameAndPriceOfMeatsOrExpensiveMenus  =
    menus.stream().filter(menu -> {
     if (menu.getMenuType() == MenuType.MEATS) {
          return  true;
     }
     if (price > cheapPrice) {
          return  true;
     }
     return  false;})
    .map(menu -> menu.getMenuName() + " " + menu.getPrice()).collect(Collectors.toList());

Что вы думаете об этом коде?

Для меня это просто какой-то тупой код. Хотя мы можем выяснить, каково использование этой функции, прочитав построчно функцию, но, чтобы быть чистым, в ней должно быть четко объяснено назначение, ограничение и требование. Также в потоке есть какая-то уродливая условная проверка, которая как бы удлиняет его, или мы можем сказать, что это запутанный код.

Давайте немного уменьшим шум в этом коде.

Прежде всего, нам нужно очистить все объекты, которые участвуют в этой функции, если мы вернемся к функции, мы работаем с Menu, и MenuType в Menu, его menuType – String, что означает любое значение строкового типа. Нам нужно ограничить тип меню, чтобы сделать его строгим и чистым.

  • Измените тип меню Menu на тип класса MenuType:
class Menu{
    private String menuName;
    private Integer price;
    private MenuType menuType;
    //AllArgsConstructor
    //Getters and Setters
}
  • Для условной проверки мы можем добавить специальный служебный класс для выполнения этого:
class MenuTypeChecker{  

    public static boolean isMeats(Menu menu){  
        return menu.getMenuName().equals(MenuType.MEATS);  
    }  
    public static boolean isExpensive(Menu menu){  
        int cheapPrize = 1000;
        return menu.getPrice()>cheapPrize;  
    }  
}

Итак, теперь мы можем упростить это следующим образом:

Collection menus =  new  ArrayList<>(){{
       add(new Menu("Chicken",1300,MenuType.MEATS));  
       add(new Menu("Pig",2500,MenuType.MEATS));  
       add(new Menu("CocoCrunch",450,MenuType.CEREAL));  
       add(new Menu("Milk",500,MenuType.DRINK));
       }};
Collection menuNameAndPriceOfMeatsOrExpensiveMenus  = menus.stream().filter(m->{  
    if(MenuTypeChecker.isMeats(m))return true; 
    if(MenuTypeChecker.isExpensive(m))return true;  
    return false;
 }).map(m-> m.getMenuName()+" "+m.getPrice()).collect(Collectors.toList());

Это более читабельно, не так ли? Но мы все еще можем улучшить этот код. Что еще мы можем сделать? Мы можем дать имя участвующим лямбдам, чтобы сделать их более чистыми и удобочитаемыми.

В java 8 есть класс предикатов и функций, которые помогают нам справляться с очисткой использования лямбда-функций.

И снова мы.

  • Условие if в контексте фильтрации может быть объяснено с помощью предиката. Нам нужно сохранить его в предикате меню, так как мы хотим фильтровать объект по типу меню:
Predicate isMeatsOrExpensiveMenu = m->(MenuTypeChecker.isMeats(m) || MenuTypeChecker.isExpensive(m));
  • И цель сопоставления контекста можно объяснить, сохранив его в переменной с типом функции:
Function getMenuNameAndPrice = m->m.getMenuName()+" "+m.getPrice();
  • Наконец, мы можем изменить нашу основную функцию следующим образом:
menus.stream().filter(isMeatsOrExpensiveMenu).map(getMenuNameAndPrice);
  • Вот окончательный вид нашей основной функции
Collection menus =  new  ArrayList<>(){{
       add(new Menu("Chicken",1300,MenuType.MEATS));  
       add(new Menu("Pig",2500,MenuType.MEATS));  
       add(new Menu("CocoCrunch",450,MenuType.CEREAL));  
       add(new Menu("Milk",500,MenuType.DRINK));
       }};
Predicate isMeatsOrExpensiveMenu = m-> (MenuTypeChecker.isMeats(m) || MenuTypeChecker.isExpensive(m));  
Function getMenuNameAndPrice = m->m.getMenuName()+" "+m.getPrice();

Collection menuNameAndPriceOfMeatsOrExpensiveMenus  = menus.stream().filter(isMeatsOrExpensiveMenu).map(getMenuNameAndPrice).collect(Collectors.toList());

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

Спасибо, что прочитали эту статью:)

Оригинал: “https://dev.to/rizesky/clean-code-of-lambda-usages-in-java-8-3pcm”