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

PDF
Dropwizard with MongoDB and Google Cloud
PPTX
Dependency injection - the right way
PDF
MCC Scripts update
PPT
Week 8
PDF
AdWords Scripts and MCC Scripting
PDF
Pathway to Cloud-Native .NET
PPTX
Digital analytics with R - Sydney Users of R Forum - May 2015
PDF
Laravel Design Patterns
Dropwizard with MongoDB and Google Cloud
Dependency injection - the right way
MCC Scripts update
Week 8
AdWords Scripts and MCC Scripting
Pathway to Cloud-Native .NET
Digital analytics with R - Sydney Users of R Forum - May 2015
Laravel Design Patterns

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

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

More from Giovane Liberato (9)

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

Recently uploaded (20)

DOCX
The AUB Centre for AI in Media Proposal.docx
PDF
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
PDF
Electronic commerce courselecture one. Pdf
PDF
Approach and Philosophy of On baking technology
PPTX
Big Data Technologies - Introduction.pptx
PPTX
ACSFv1EN-58255 AWS Academy Cloud Security Foundations.pptx
PPTX
Programs and apps: productivity, graphics, security and other tools
PDF
The Rise and Fall of 3GPP – Time for a Sabbatical?
PPTX
MYSQL Presentation for SQL database connectivity
PDF
Dropbox Q2 2025 Financial Results & Investor Presentation
PDF
Reach Out and Touch Someone: Haptics and Empathic Computing
PDF
Spectral efficient network and resource selection model in 5G networks
PDF
Unlocking AI with Model Context Protocol (MCP)
PDF
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
PDF
gpt5_lecture_notes_comprehensive_20250812015547.pdf
PDF
Diabetes mellitus diagnosis method based random forest with bat algorithm
PDF
Network Security Unit 5.pdf for BCA BBA.
PDF
A comparative analysis of optical character recognition models for extracting...
PDF
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
PDF
Agricultural_Statistics_at_a_Glance_2022_0.pdf
The AUB Centre for AI in Media Proposal.docx
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
Electronic commerce courselecture one. Pdf
Approach and Philosophy of On baking technology
Big Data Technologies - Introduction.pptx
ACSFv1EN-58255 AWS Academy Cloud Security Foundations.pptx
Programs and apps: productivity, graphics, security and other tools
The Rise and Fall of 3GPP – Time for a Sabbatical?
MYSQL Presentation for SQL database connectivity
Dropbox Q2 2025 Financial Results & Investor Presentation
Reach Out and Touch Someone: Haptics and Empathic Computing
Spectral efficient network and resource selection model in 5G networks
Unlocking AI with Model Context Protocol (MCP)
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
gpt5_lecture_notes_comprehensive_20250812015547.pdf
Diabetes mellitus diagnosis method based random forest with bat algorithm
Network Security Unit 5.pdf for BCA BBA.
A comparative analysis of optical character recognition models for extracting...
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
Agricultural_Statistics_at_a_Glance_2022_0.pdf

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)