SlideShare a Scribd company logo
Refatorando com a API
funcional do Java
Giovane Liberato @ JUG Vale 14
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
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)
Introdução ao
Refactoring
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”
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
Refatorando com a API funcional do Java
Refatorando com a API funcional do Java
Catálogo de
Refactorings
Lembrete
Isso não é o refactoring mais grandioso do mundo,
isso é apenas um tributo
Ouça (ou leia) sua IDE
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
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();
}
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");
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();
}
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
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;
}
}
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;
}
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;
}
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() { ... }
}
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());
}
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
public class Account {
private String pushNotificationId;
public String getPushNotificationId() { … }
}
-----------------------------------------------------------------
public class Driver {
private String pushNotificationId;
public String getPushNotificationId() { … }
}
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) { … }
}
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();
}
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();
}
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();
}
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);
}
}
public class CampaignService {
CampaignFactory campaignFactory;
private AccountRepository accountRepository;
private DriversRepository driversRepository;
public Campaign createCampaignForNewUsers(Country country) { .. }
public Campaign createCampaignForAllDrivers(Country country) { .. }
// ...
}
Invertendo dependência
public class CampaignFactory {
public Campaign buildCampaign(
Supplier<List<String>> idsSupplier, Message message) {
return Campaign
.builder()
.pushNotificationIds(idsSupplier.get())
.message(message)
.build();
}
}
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);
}
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);
}
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
public class AccountToNameConverter
implements Function<Account, String> {
@Override
public String apply(Account account) {
return String.format("%s %s",
account.getFirstName(), account.getLastName());
}
public class AccountService {
private AccountToNameConverter converter =
new AccountToNameConverter();
public List<String> getEveryonesName(List<Account> accounts) {
return accounts
.stream()
.map(converter)
.collect(toList());
}
}
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());
}
}
public class AccountService {
public List<String> getEveryonesName(List<Account> accounts) {
return accounts
.stream()
.map(AccountToNameConverter::convert) // ou___
.map(AccountToNameConverter::convertLastFirst)
.collect(toList());
}
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)
Códigos disponíveis em
https://github.com/giovaneliberato/refactoring-java-8plus
Obrigado!
about.me/giovaneliberato

More Related Content

Similar to Refatorando com a API funcional do Java (20)

PDF
IAP auto renewable in practice
Hokila Jan
 
PPT
Velocity 2014: Accelerate Your User Experience With Client-side JavaScript
Intuit Inc.
 
PDF
Wave Analytics: Developing Predictive Business Intelligence Apps
Salesforce Developers
 
PDF
Audit¢rio 09 mercado envios - novas funcionalidades - bruno elia
fsolari
 
PDF
Opticon 2015 - Getting Started with the Optimizely Developer Platform
Optimizely
 
PPTX
Introduction to Domain driven design (LaravelBA #5)
guiwoda
 
PDF
Intershop Commerce Management with Microsoft SQL Server
Mauro Boffardi
 
PDF
How AdWords UI maps into adwords api
supergigas
 
PDF
A Practical Approach to Building a Streaming Processing Pipeline for an Onlin...
Databricks
 
PPTX
ATAGTR2017 Test Approach for Re-engineering Legacy Applications based on Micr...
Agile Testing Alliance
 
PPTX
How to Leverage APIs for SEO #TTTLive2019
Paul Shapiro
 
PPTX
Making Service Portal Widgets Work Together
TravisToulson
 
PDF
Let's your users share your App with Friends: App Invites for Android
Wilfried Mbouenda Mbogne
 
PDF
Apigee Demo: API Platform Overview
Apigee | Google Cloud
 
PPTX
Optimizing Code Reusability for SharePoint using Linq to SharePoint & the MVP...
Sparkhound Inc.
 
PDF
PHPUnit Episode iv.iii: Return of the tests
Michelangelo van Dam
 
PPTX
Portfolio - Operations by Sinisa Maricic.pptx
SinisaMaricic3
 
PPTX
SH 1 - SES 8 - Stitch_Overview_TLV.pptx
MongoDB
 
PDF
Agados-based Application Design Demo
Yongkyoo Park
 
PDF
AwReporting Update
marcwan
 
IAP auto renewable in practice
Hokila Jan
 
Velocity 2014: Accelerate Your User Experience With Client-side JavaScript
Intuit Inc.
 
Wave Analytics: Developing Predictive Business Intelligence Apps
Salesforce Developers
 
Audit¢rio 09 mercado envios - novas funcionalidades - bruno elia
fsolari
 
Opticon 2015 - Getting Started with the Optimizely Developer Platform
Optimizely
 
Introduction to Domain driven design (LaravelBA #5)
guiwoda
 
Intershop Commerce Management with Microsoft SQL Server
Mauro Boffardi
 
How AdWords UI maps into adwords api
supergigas
 
A Practical Approach to Building a Streaming Processing Pipeline for an Onlin...
Databricks
 
ATAGTR2017 Test Approach for Re-engineering Legacy Applications based on Micr...
Agile Testing Alliance
 
How to Leverage APIs for SEO #TTTLive2019
Paul Shapiro
 
Making Service Portal Widgets Work Together
TravisToulson
 
Let's your users share your App with Friends: App Invites for Android
Wilfried Mbouenda Mbogne
 
Apigee Demo: API Platform Overview
Apigee | Google Cloud
 
Optimizing Code Reusability for SharePoint using Linq to SharePoint & the MVP...
Sparkhound Inc.
 
PHPUnit Episode iv.iii: Return of the tests
Michelangelo van Dam
 
Portfolio - Operations by Sinisa Maricic.pptx
SinisaMaricic3
 
SH 1 - SES 8 - Stitch_Overview_TLV.pptx
MongoDB
 
Agados-based Application Design Demo
Yongkyoo Park
 
AwReporting Update
marcwan
 

More from Giovane Liberato (9)

PDF
Functional Python
Giovane Liberato
 
PDF
Front end para back enders: Dicas de como se virar no universo paralelo
Giovane Liberato
 
PDF
Front end pra back enders: dicas pra se virar no universo paralelo
Giovane Liberato
 
PDF
Google App Engine e NoSQL: Alta disponibilidade
Giovane Liberato
 
PDF
Sistemas de recomendação na educação
Giovane Liberato
 
PDF
A cobra vai fumar(e tomar um golinho de café) - Jython
Giovane Liberato
 
PDF
Jython - "A cobra vai fumar (e tomar um golinho de café)"
Giovane Liberato
 
PDF
Um site em 5 minutos com bottle.py
Giovane Liberato
 
PDF
OWASP Top 10 - A web security cookbook
Giovane Liberato
 
Functional Python
Giovane Liberato
 
Front end para back enders: Dicas de como se virar no universo paralelo
Giovane Liberato
 
Front end pra back enders: dicas pra se virar no universo paralelo
Giovane Liberato
 
Google App Engine e NoSQL: Alta disponibilidade
Giovane Liberato
 
Sistemas de recomendação na educação
Giovane Liberato
 
A cobra vai fumar(e tomar um golinho de café) - Jython
Giovane Liberato
 
Jython - "A cobra vai fumar (e tomar um golinho de café)"
Giovane Liberato
 
Um site em 5 minutos com bottle.py
Giovane Liberato
 
OWASP Top 10 - A web security cookbook
Giovane Liberato
 
Ad

Recently uploaded (20)

PPTX
Extensions Framework (XaaS) - Enabling Orchestrate Anything
ShapeBlue
 
PDF
Persuasive AI: risks and opportunities in the age of digital debate
Speck&Tech
 
PDF
Empowering Cloud Providers with Apache CloudStack and Stackbill
ShapeBlue
 
PPTX
Darren Mills The Migration Modernization Balancing Act: Navigating Risks and...
AWS Chicago
 
PDF
Chris Elwell Woburn, MA - Passionate About IT Innovation
Chris Elwell Woburn, MA
 
PDF
Women in Automation Presents: Reinventing Yourself — Bold Career Pivots That ...
DianaGray10
 
PDF
Empower Inclusion Through Accessible Java Applications
Ana-Maria Mihalceanu
 
PPTX
MSP360 Backup Scheduling and Retention Best Practices.pptx
MSP360
 
PDF
Smart Air Quality Monitoring with Serrax AQM190 LITE
SERRAX TECHNOLOGIES LLP
 
PDF
CIFDAQ Token Spotlight for 9th July 2025
CIFDAQ
 
PDF
Complete JavaScript Notes: From Basics to Advanced Concepts.pdf
haydendavispro
 
PPTX
Top iOS App Development Company in the USA for Innovative Apps
SynapseIndia
 
PDF
Windsurf Meetup Ottawa 2025-07-12 - Planning Mode at Reliza.pdf
Pavel Shukhman
 
PDF
Ampere Offers Energy-Efficient Future For AI And Cloud
ShapeBlue
 
PPTX
Building and Operating a Private Cloud with CloudStack and LINBIT CloudStack ...
ShapeBlue
 
PDF
CloudStack GPU Integration - Rohit Yadav
ShapeBlue
 
PDF
CIFDAQ Weekly Market Wrap for 11th July 2025
CIFDAQ
 
PDF
Français Patch Tuesday - Juillet
Ivanti
 
PDF
SFWelly Summer 25 Release Highlights July 2025
Anna Loughnan Colquhoun
 
PDF
Meetup Kickoff & Welcome - Rohit Yadav, CSIUG Chairman
ShapeBlue
 
Extensions Framework (XaaS) - Enabling Orchestrate Anything
ShapeBlue
 
Persuasive AI: risks and opportunities in the age of digital debate
Speck&Tech
 
Empowering Cloud Providers with Apache CloudStack and Stackbill
ShapeBlue
 
Darren Mills The Migration Modernization Balancing Act: Navigating Risks and...
AWS Chicago
 
Chris Elwell Woburn, MA - Passionate About IT Innovation
Chris Elwell Woburn, MA
 
Women in Automation Presents: Reinventing Yourself — Bold Career Pivots That ...
DianaGray10
 
Empower Inclusion Through Accessible Java Applications
Ana-Maria Mihalceanu
 
MSP360 Backup Scheduling and Retention Best Practices.pptx
MSP360
 
Smart Air Quality Monitoring with Serrax AQM190 LITE
SERRAX TECHNOLOGIES LLP
 
CIFDAQ Token Spotlight for 9th July 2025
CIFDAQ
 
Complete JavaScript Notes: From Basics to Advanced Concepts.pdf
haydendavispro
 
Top iOS App Development Company in the USA for Innovative Apps
SynapseIndia
 
Windsurf Meetup Ottawa 2025-07-12 - Planning Mode at Reliza.pdf
Pavel Shukhman
 
Ampere Offers Energy-Efficient Future For AI And Cloud
ShapeBlue
 
Building and Operating a Private Cloud with CloudStack and LINBIT CloudStack ...
ShapeBlue
 
CloudStack GPU Integration - Rohit Yadav
ShapeBlue
 
CIFDAQ Weekly Market Wrap for 11th July 2025
CIFDAQ
 
Français Patch Tuesday - Juillet
Ivanti
 
SFWelly Summer 25 Release Highlights July 2025
Anna Loughnan Colquhoun
 
Meetup Kickoff & Welcome - Rohit Yadav, CSIUG Chairman
ShapeBlue
 
Ad

Refatorando com a API funcional do Java

  • 1. Refatorando com a API funcional do Java Giovane Liberato @ JUG Vale 14
  • 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. 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)
  • 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. 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
  • 10. Lembrete Isso não é o refactoring mais grandioso do mundo, isso é apenas um tributo
  • 11. Ouça (ou leia) sua IDE
  • 12. 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
  • 13. 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(); }
  • 14. 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");
  • 15. 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(); }
  • 16. 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
  • 17. 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; } }
  • 18. 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; }
  • 19. 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; }
  • 20. 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() { ... } }
  • 21. 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()); }
  • 22. 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
  • 23. public class Account { private String pushNotificationId; public String getPushNotificationId() { … } } ----------------------------------------------------------------- public class Driver { private String pushNotificationId; public String getPushNotificationId() { … } }
  • 24. 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) { … } }
  • 25. 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(); }
  • 26. 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(); }
  • 27. 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(); }
  • 28. 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); } }
  • 29. public class CampaignService { CampaignFactory campaignFactory; private AccountRepository accountRepository; private DriversRepository driversRepository; public Campaign createCampaignForNewUsers(Country country) { .. } public Campaign createCampaignForAllDrivers(Country country) { .. } // ... } Invertendo dependência
  • 30. public class CampaignFactory { public Campaign buildCampaign( Supplier<List<String>> idsSupplier, Message message) { return Campaign .builder() .pushNotificationIds(idsSupplier.get()) .message(message) .build(); } }
  • 31. 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); }
  • 32. 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); }
  • 33. 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
  • 34. public class AccountToNameConverter implements Function<Account, String> { @Override public String apply(Account account) { return String.format("%s %s", account.getFirstName(), account.getLastName()); }
  • 35. public class AccountService { private AccountToNameConverter converter = new AccountToNameConverter(); public List<String> getEveryonesName(List<Account> accounts) { return accounts .stream() .map(converter) .collect(toList()); } }
  • 36. 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()); } }
  • 37. public class AccountService { public List<String> getEveryonesName(List<Account> accounts) { return accounts .stream() .map(AccountToNameConverter::convert) // ou___ .map(AccountToNameConverter::convertLastFirst) .collect(toList()); }
  • 38. 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)