O slideshow foi denunciado.
Utilizamos seu perfil e dados de atividades no LinkedIn para personalizar e exibir anúncios mais relevantes. Altere suas preferências de anúncios quando desejar.

Refatorando com a API funcional do Java

21 visualizações

Publicada em

Refatorando com a api funcional do java

Publicada em: Tecnologia
  • Seja o primeiro a comentar

  • Seja a primeira pessoa a gostar disto

Refatorando com a API funcional do Java

  1. 1. Refatorando com a API funcional do Java Giovane Liberato @ JUG Vale 14
  2. 2. Agenda ● Introdução ao Refactoring ● Catálogo de Refactorings ○ Ouça (ou leia) sua IDE ○ Composição de regras com Predicates ○ Optionals - Quando (não) usar ○ Inversão de dependências com Suppliers ○ Funções idiomáticas
  3. 3. Giovane Liberato Senior Software Engineer no * Foco em arquitetura de micro serviços, desenvolvimento seguro e práticas ágeis. Interesse em privacidade digital e criptografia. about.me/giovaneliberato * Temos vagas! (E são remotas)
  4. 4. Introdução ao Refactoring
  5. 5. Refactoring is a controlled technique for improving the design of an existing code base. Its essence is applying a series of small behavior-preserving transformations, each of which "too small to be worth doing". Martin Fowler, autor do livro “Refactoring Improving the Design of Existing Code”
  6. 6. Mudanças pequenas e constantes que geram enorme benefício a longo prazo Objetivos e focados em melhoria de arquitetura, legibilidade e na redução de débitos técnicos Cercado de testes para garantir que não há quebra de comportamento Red -> Green -> Refactor Evoluir a codebase para acomodar novas features e reduzir acoplamento entre componentes do sistema Como fazer
  7. 7. Catálogo de Refactorings
  8. 8. Lembrete Isso não é o refactoring mais grandioso do mundo, isso é apenas um tributo
  9. 9. Ouça (ou leia) sua IDE
  10. 10. Composição de regras com Predicates Cenário Dado um usuário que contém uma lista de tags, definir qual o valor do cupom de desconto gerado Atores Account, VoucherService e VoucherPredicates Code smells Implementação obstruindo legibilidade, lógicas binárias encadeadas no mesmo IF
  11. 11. public Voucher getVoucherForAccount(Account account) { if (account.getTags().contains("new")) { return Voucher.of(15); } if (account.getTags().contains("lover")) { return Voucher.of(20); } if (account.getTags().contains("veg") && account.getTags().contains("new")) { return Voucher.of(25); } if (account.getTags().contains("lover") && (account.getTags().contains("pizza_lover") || account.getTags().contains("burger_lover"))) { return Voucher.of(35); } return Voucher.none(); }
  12. 12. public static BiPredicate<Account, String> containsTag = (account, tag)-> account.getTags().contains(tag); public static Predicate<Account> IS_NEW = (account) -> containsTag.test(account, "new"); public static Predicate<Account> IS_LOVER = (account) -> containsTag.test(account, "lover"); public static Predicate<Account> IS_VEG = (account) -> containsTag.test(account, "veg"); public static Predicate<Account> IS_PIZZA_LOVER = (account) -> containsTag.test(account, "pizza_lover"); public static Predicate<Account> IS_BURGER_LOVER = (account) -> containsTag.test(account, "burger_lover");
  13. 13. public Voucher getVoucherForAccount(Account account) { if (IS_NEW.test(account)) { return Voucher.of(15); } if (IS_LOVER.test(account)) { return Voucher.of(20); } if (IS_VEG.and(IS_NEW).test(account)) { return Voucher.of(25); } if (IS_LOVER.and(IS_PIZZA_LOVER.or(IS_BURGER_LOVER)).test(account)) { return Voucher.of(35); } return Voucher.none(); }
  14. 14. Optionals - Quando (não) usar Cenário Um usuário pode favoritar e bloquear restaurantes do seu menu. Ambas as listas podem ser vazias. Atores Account, Restaurant, RestaurantService e RestaurantRepository Code smells Optional como atributo de classe, chamada dos métodos .isPresent e .get, Optional representando estado domínio
  15. 15. public class Account { private Optional<List<Restaurant>> starredRestaurants; private Optional<List<Restaurant>> blockedRestaurants; public Optional<List<Restaurant>> getBlockedRestaurants() { return blockedRestaurants; } public Optional<List<Restaurant>> getStarredRestaurants() { return starredRestaurants; } }
  16. 16. public List<Restaurant> getRestaurantsForAccount(Account account) { var restaurants = RestaurantRepository.getAll(); if (account.getBlockedRestaurants().isPresent()) { var blocked = account.getBlockedRestaurants().get(); restaurants = restaurants .stream() .filter((r -> blocked.contains(r))) .collect(toList()); } if (account.getStarredRestaurants().isPresent()) { restaurants.addAll(account.getStarredRestaurants().get()); } return restaurants; }
  17. 17. Optionals - usando .orElse public List<Restaurant> getRestaurantsForAccount(Account account) { var restaurants = RestaurantRepository.getAll(); var blocked = account.getBlockedRestaurants().orElse(emptyList()); restaurants = restaurants .stream() .filter((r -> blocked.contains(r))) .collect(toList()); restaurants.addAll(account.getStarredRestaurants().orElse(emptyList())); return restaurants; }
  18. 18. Optionals - removendo das classes public class Account { private List<Restaurant> starredRestaurants; private List<Restaurant> blockedRestaurants; public List<Restaurant> getBlockedRestaurants() { return blockedRestaurants != null ? blockedRestaurants : emptyList(); } public List<Restaurant> getStarredRestaurants() { ... } }
  19. 19. public List<Restaurant> getRestaurantsForAccount(Account account) { var restaurantList = RestaurantRepository.getAll(); var blocked = account.getBlockedRestaurants(); var starred = account.getStarredRestaurants(); return Stream.concat( starred.stream(), restaurantList .stream() .filter((blocked::contains))) .collect(toList()); }
  20. 20. Inversão de dependência com suppliers Cenário Criação de objetos complexos baseado em diferentes fontes de dados Atores Account, Driver, CampaignService e CampaignFactory Code smells Inveja de funcionalidade (feature envy) e assinatura de métodos parcialmente repetidas
  21. 21. public class Account { private String pushNotificationId; public String getPushNotificationId() { … } } ----------------------------------------------------------------- public class Driver { private String pushNotificationId; public String getPushNotificationId() { … } }
  22. 22. public class CampaignFactory { private AccountRepository accountRepository; private DriversRepository driversRepository; public Campaign buildCampaignForNewUsers(Country country, Message message) { … } public Campaign buildCampaign(Country country, Message message) { … } public Campaign buildCampaign(List<Account> accounts,Message message) { … } public Campaign buildCampaignForDrivers(Country country, Message message) { … } }
  23. 23. public Campaign buildCampaignForNewUsers( Country country, Message message) { var pushIds = accountRepository .findNewUsersByCountry(country) .stream() .map(Account::getPushNotificationId) .collect(toList()); return Campaign .builder() .pushNotificationIds(pushIds) .message(message) .build(); }
  24. 24. public Campaign buildCampaignForDrivers( Country country, Message message) { var pushIds = driversRepository .findDriversByCountry(country) .stream() .map(Driver::getPushNotificationId) .collect(toList()); return Campaign .builder() .pushNotificationIds(pushIds) .message(message) .build(); }
  25. 25. public Campaign buildCampaign(List<Account> accounts, Message message) { var pushIds = accounts .stream() .map(Account::getPushNotificationId) .collect(toList()); return Campaign .builder() .pushNotificationIds(pushIds) .message(message) .build(); }
  26. 26. public class CampaignService { CampaignFactory campaignFactory; public Campaign createCampaignForNewUsers(Country country) { var message = new Message("welcome"); return campaignFactory.buildCampaignForNewUsers(country, message); } public Campaign createCampaignForAllUsers(Country country) { var message = new Message("#lanches"); return campaignFactory.buildCampaign(country, message); } public Campaign createCampaignForUsers(List<Account> accounts) { var message = new Message("#lanches"); return campaignFactory.buildCampaign(accounts, message); } public Campaign createCampaignForAllDrivers(Country country) { var message = new Message("bonus!"); return campaignFactory.buildCampaignForDrivers(country, message); } }
  27. 27. public class CampaignService { CampaignFactory campaignFactory; private AccountRepository accountRepository; private DriversRepository driversRepository; public Campaign createCampaignForNewUsers(Country country) { .. } public Campaign createCampaignForAllDrivers(Country country) { .. } // ... } Invertendo dependência
  28. 28. public class CampaignFactory { public Campaign buildCampaign( Supplier<List<String>> idsSupplier, Message message) { return Campaign .builder() .pushNotificationIds(idsSupplier.get()) .message(message) .build(); } }
  29. 29. public Campaign createCampaignForNewUsers(Country country) { var message = new Message("welcome"); Supplier<List<String>> ids = () -> accountRepository.findNewUsersByCountry(country) .stream() .map(Account::getPushNotificationId) .collect(toList()); return campaignFactory.buildCampaign(ids, message); }
  30. 30. public Campaign createCampaignForAllDrivers(Country country) { var message = new Message("bonus!"); Supplier<List<String>> ids = () -> driversRepository.findDriversByCountry(country) .stream() .map(Driver::getPushNotificationId) .collect(toList()); return campaignFactory.buildCampaign(ids, message); }
  31. 31. Funções idiomáticas Cenário Para usar funções customizadas, o contra-exemplo implementa a interface Function sem necessidade. Atores Account, AccountToNameConverter Code smells Implementando interfaces funcionais para casos simples. Múltiplas classes para contextos parecidos
  32. 32. public class AccountToNameConverter implements Function<Account, String> { @Override public String apply(Account account) { return String.format("%s %s", account.getFirstName(), account.getLastName()); }
  33. 33. public class AccountService { private AccountToNameConverter converter = new AccountToNameConverter(); public List<String> getEveryonesName(List<Account> accounts) { return accounts .stream() .map(converter) .collect(toList()); } }
  34. 34. public class AccountToNameConverter { public static String convert(Account acc) { return String.format("%s%s", acc.getFirstName(), acc.getLastName()); } public static String convertLastFirst(Account acc) { return String.format("%s %s", acc.getLastName(), acc.getFirstName()); } }
  35. 35. public class AccountService { public List<String> getEveryonesName(List<Account> accounts) { return accounts .stream() .map(AccountToNameConverter::convert) // ou___ .map(AccountToNameConverter::convertLastFirst) .collect(toList()); }
  36. 36. Referências Refactoring - Improving the Design of Existing Code (Martin Fowler) Effective Java, Third Edition Keepin' it Effective (J. Bloch) Optional - The Mother of All Bikesheds (Stuart Marks) Understanding the Economics of Refactoring (Leitch, Stroulia) The Financial Implications of Technical Debt (Erik Frederick)
  37. 37. Códigos disponíveis em https://github.com/giovaneliberato/refactoring-java-8plus
  38. 38. Obrigado! about.me/giovaneliberato

×