Slide 1

Slide 1 text

Refactoring to Patterns em Java 8 Eder Ignatowicz Sr. Software Engineer JBoss by Red Hat

Slide 2

Slide 2 text

Design Patterns + Java + Programação Funcional <3

Slide 3

Slide 3 text

Antes uma história :)

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

Me tornar um World Class Developer

Slide 9

Slide 9 text

Como virar um World Class Developer?

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

:(

Slide 12

Slide 12 text

?

Slide 13

Slide 13 text

:)

Slide 14

Slide 14 text

Refactoring to Patterns em Java 8

Slide 15

Slide 15 text

Refactoring Loops to Collection Pipelines

Slide 16

Slide 16 text

public class Client { private String name; private String email; private Company company; public Client( String name, String email, Company company ) { this.name = name; this.email = email; this.company = company; } public Client( String name ) { this.name = name; } public Client( String name, String email ) { this.name = name; this.email = email; } … }

Slide 17

Slide 17 text

public class ClientRepositoryTest { private ClientRepository repo; @Before public void setup() { Company empresa = new Company( "RedHat" ); Client completo1 = new Client( "Completo1", "completo1@redhat.com", empresa ); Client completo2 = new Client( "Completo2", "completo2@redhat.com", empresa ); Client semEmpresa = new Client( "SemEmpresa", "semEmpresa@ederign.me" ); Client somenteNome = new Client( "SomenteNome" ); repo = new ClientRepository( Arrays.asList( completo1, semEmpresa, completo2, somenteNome ) ); } @Test public void getClientEmailsWithCompanyTest() { List clientMails = repo.getClientMails(); assertEquals( 2, clientMails.size() ); assertTrue( clientMails.contains( "completo1@redhat.com" ) ); assertTrue( clientMails.contains( "completo2@redhat.com" ) ); assertTrue( !clientMails.contains( "semEmpresa@ederign.me" ) ); } }

Slide 18

Slide 18 text

public List getClientMails() { ArrayList emails = new ArrayList<>(); for ( Client client : clients ) { if ( client.getCompany() != null ) { String email = client.getEmail(); if ( email != null ){ emails.add( email ); } } } return emails; }

Slide 19

Slide 19 text

public List getClientMails() { ArrayList emails = new ArrayList<>(); List pipeline = clients; for ( Client client : pipeline ) { if ( client.getCompany() != null ) { String email = client.getEmail(); if ( email != null ){ emails.add( email ); } } } return emails; } } Extract Variable

Slide 20

Slide 20 text

public List getClientMails() { ArrayList emails = new ArrayList<>(); List pipeline = clients .stream() .filter( c -> c.getCompany() != null ) .collect( Collectors.toList() ); for ( Client client : pipeline ) { if ( client.getCompany() != null ) { String email = client.getEmail(); if ( email != null ) { emails.add( email ); } } } return emails; } } Filter Operation

Slide 21

Slide 21 text

Map Operation public List getClientMails() { ArrayList emails = new ArrayList<>(); List pipeline = clients .stream() .filter( c -> c.getCompany() != null ) .map( c -> c.getEmail() ) .collect( Collectors.toList() ); for ( String mail : pipeline ) { String email = client.getEmail(); if ( mail != null ) { emails.add( mail ); } } return emails; }

Slide 22

Slide 22 text

Filter Operation public List getClientMails() { ArrayList emails = new ArrayList<>(); List pipeline = clients .stream() .filter( c -> c.getCompany() != null ) .map( c -> c.getEmail() ) .filter( m -> m != null ) .collect( Collectors.toList() ); for ( String mail : pipeline ) { if ( mail != null ) { emails.add( mail ); } } return emails; }

Slide 23

Slide 23 text

Pipeline public List getClientMails() { ArrayList emails = new ArrayList<>(); return clients .stream() .filter( c -> c.getCompany() != null ) .map( c -> c.getEmail() ) .filter( m -> m != null ) .collect( Collectors.toList() ); for ( String mail : pipeline ) { if ( mail != null ) { emails.add( mail ); } } return emails; }

Slide 24

Slide 24 text

public List getClientMails() { return clients .stream() .filter( c -> c.getCompany() != null ) .map( c -> c.getEmail() ) .filter( m -> m != null ) .collect( Collectors.toList() ); }

Slide 25

Slide 25 text

Strategy “Definir uma família de algoritmos, encapsular cada uma delas e torná-las intercambiáveis. Strategy permite que o algoritmo varie independentemente dos clientes que o utilizam” GAMMA, Erich et al.

Slide 26

Slide 26 text

public class ShoppingCartTest { ShoppingCart cart; @Before public void setup() { Item item1 = new Item( 10 ); Item item2 = new Item( 20 ); cart = new ShoppingCart( Arrays.asList( item1, item2 ) ); } @Test public void totalTest() { cart.pay( ShoppingCart.PaymentMethod.CREDIT ) ); } }

Slide 27

Slide 27 text

public class ShoppingCart { private List items; public ShoppingCart( List items ) { this.items = items; } public void pay( PaymentMethod method ) { int total = cartTotal(); if ( method == PaymentMethod.CREDIT ) { System.out.println( “Pay with credit “ + total); } else if ( method == PaymentMethod.MONEY ) { System.out.println( “Pay with money “ + total ); } } private int cartTotal() { return items .stream() .mapToInt( Item::getValue ) .sum(); } … }

Slide 28

Slide 28 text

public class ShoppingCart { private List items; public ShoppingCart( List items ) { this.items = items; } public void pay( PaymentMethod method ) { int total = cartTotal(); if ( method == PaymentMethod.CREDIT ) { System.out.println( “Pay with credit “ + total ); } else if ( method == PaymentMethod.MONEY ) { System.out.println( “Pay with money “ + total ); } } private int cartTotal() { return items .stream() .mapToInt( Item::getValue ) .sum(); } … }

Slide 29

Slide 29 text

public interface Payment { public void pay(int amount); } public class CreditCard implements Payment { @Override public void pay( int amount ) { System.out.println( "Pay with Credit: “ + amount); } } public class Money implements Payment { @Override public void pay( int amount ) { System.out.println( "Pay with Money: “ + amount); }

Slide 30

Slide 30 text

public class ShoppingCart { … public void pay( Payment method ) { int total = cartTotal(); method.pay( total ); } private int cartTotal() { return items .stream() .mapToInt( Item::getValue ) .sum(); } } Strategy

Slide 31

Slide 31 text

public interface Payment { public void pay(int amount); } public class CreditCard implements Payment { @Override public void pay( int amount ) { System.out.println( "make credit payment logic" ); } } public class Money implements Payment { @Override public void pay( int amount ) { System.out.println( "make money payment logic" ); } } public class DebitCard implements Payment { @Override public void pay( int amount ) { System.out.println( "make debit payment logic" ); } } public void totalTest() { assertEquals( 30, cart.pay( new CreditCard() ) ); assertEquals( 30, cart.pay( new Money() ) ); assertEquals( 30, cart.pay( new DebitCard() ) ); } }

Slide 32

Slide 32 text

No content

Slide 33

Slide 33 text

public class ShoppingCart { … public void pay( Consumer method ) { int total = cartTotal(); method.accept( total ); } … } public void pay( Payment method ) { int total = cartTotal(); method.pay( total ); }

Slide 34

Slide 34 text

public class ShoppingCart { … public void pay( Consumer method ) { int total = cartTotal(); method.accept( total ); } … } public void totalTest() { cart.pay( amount -> System.out.println( "Pay with Credit: " + amount ) ); cart.pay( amount -> System.out.println( "Pay with Money: " + amount ) ); cart.pay( amount -> System.out.println( "Pay with Debit: " + amount ) ); }

Slide 35

Slide 35 text

public class PaymentTypes { public static void money( int amount ) { System.out.println( "Pay with Money: " + amount ); } public static void debit( int amount ) { System.out.println( "Pay with Debit: " + amount ); } public static void credit( int amount ) { System.out.println( "Pay with Credit: " + amount ); } } public void totalTest() { cart.pay( PaymentTypes::credit ); cart.pay( PaymentTypes::debit ); cart.pay( PaymentTypes::money ); }

Slide 36

Slide 36 text

public class ShoppingCart { … public void pay( Consumer method ) { int total = cartTotal(); method.accept( total ); } private int cartTotal() { return items .stream() .mapToInt( Item::getValue ) .sum(); } } Strategy

Slide 37

Slide 37 text

Decorator “Dinamicamente, agregar responsabilidades adicionais a objetos. Os Decorators fornecem uma alternativa flexível ao uso de subclasses para extensão de funcionalidades.” GAMMA, Erich et al.

Slide 38

Slide 38 text

new BufferedReader(new FileReader(new File("some.file")));

Slide 39

Slide 39 text

public class Item { private int price; public Item( int price ) { this.price = price; } public int getPrice() { return price; } } Extras Envio Impostos Embalagem

Slide 40

Slide 40 text

public interface Item { int getPrice(); } public class Book implements Item { private int price; public Book( int price ) { this.price = price; } @Override public int getPrice() { return price; } }

Slide 41

Slide 41 text

public abstract class ItemExtras implements Item { private Item item; public ItemExtras( Item item ) { this.item = item; } @Override public int getPrice() { return item.getPrice(); } }

Slide 42

Slide 42 text

public class InternationalDelivery extends ItemExtras { public InternationalDelivery( Item item ) { super( item ); } @Override public int getPrice() { return 5 + super.getPrice(); } }

Slide 43

Slide 43 text

public class GiftPacking extends ItemExtras { public GiftPacking( Item item ) { super( item ); } @Override public int getPrice() { return 15 + super.getPrice(); } }

Slide 44

Slide 44 text

public static void main( String[] args ) { Item book = new Book( 10 ); book.getPrice(); //10 Item international = new InternationalDelivery( book ); international.getPrice(); //15 }

Slide 45

Slide 45 text

public static void main( String[] args ) { Item book = new Book( 10 ); book.getPrice(); //10 Item internationalGift = new GiftPacking( new InternationalDelivery( book ) ); internationalGift.getPrice(); //30 }

Slide 46

Slide 46 text

public static void main( String[] args ) { Item book = new Book( 10 ); book.getPrice(); //10 Item internationalGiftWithTaxes = new InternacionalTaxes( new GiftPacking( new InternationalDelivery( book ); internationalGiftWithTaxes.getPrice(); //80 } }

Slide 47

Slide 47 text

public static void main( String[] args ) { Item book = new Item( 10 ); book.getPrice(); //10 Function giftPacking = value -> value + 15; giftPacking.apply( book.getPrice() ); //25 }

Slide 48

Slide 48 text

public static void main( String[] args ) { Item book = new Item( 10 ); book.getPrice(); //10 Function giftPacking = value -> value + 15; giftPacking.apply( book.getPrice() ); //25 Function intTaxes = value -> value + 50; intTaxes.apply( book.getPrice() ); //60 }

Slide 49

Slide 49 text

public static void main( String[] args ) { Item book = new Item( 10 ); book.getPrice(); //10 Function giftPacking = value -> value + 15; giftPacking.apply( book.getPrice() ); //25 Function intTaxes = value -> value + 50; intTaxes.apply( book.getPrice() ); //60 giftPacking.andThen( intTaxes ).apply( book.getPrice() ); //75 }

Slide 50

Slide 50 text

public class Item { private int price; private Function[] itemExtras = new Function[]{}; public Item( int price ) { this.price = price; } public Item( int price, Function... itemExtras) { this.price = price; this.itemExtras = itemExtras; } public int getPrice() { int priceWithExtras = price; for ( Function itemExtra : itemExtras ) { priceWithExtras = itemExtra.apply( priceWithExtras ); } return priceWithExtras; } public void setItemExtras( Function... itemExtras ) { this.itemExtras = itemExtras; } }

Slide 51

Slide 51 text

public static void main( String[] args ) { Item book = new Item( 10 ); Function giftPacking = value -> value + 15; Function intTaxes = value -> value + 50; book.setItemExtras( giftPacking, intTaxes ); book.getPrice(); //75 }

Slide 52

Slide 52 text

public static void main( String[] args ) { Item book = new Item( 10 ); Function giftPacking = value -> value + 15; Function intTaxes = value -> value + 50; book.setItemExtras( giftPacking, intTaxes ); book.getPrice(); //75 }

Slide 53

Slide 53 text

public class Packing { public static Integer giftPacking( Integer value ) { return value + 15; } //other packing options here } public class Taxes { public static Integer internacional( Integer value ) { return value + 50; } //other taxes here }

Slide 54

Slide 54 text

public static void main( String[] args ) { Item book = new Item( 10, Packing::giftPacking, Taxes::internacional ); book.getPrice(); //75 }

Slide 55

Slide 55 text

public class Item { private int price; private Function[] itemExtras = new Function[]{}; public Item( int price ) { this.price = price; } public Item( int price, Function... itemExtras) { this.price = price; this.itemExtras = itemExtras; } public int getPrice() { int priceWithExtras = price; for ( Function itemExtra : itemExtras ) { priceWithExtras = itemExtra.apply( priceWithExtras ); } return priceWithExtras; } public void setItemExtras( Function... itemExtras ) { this.itemExtras = itemExtras; } }

Slide 56

Slide 56 text

public class Item { private int price; private Function[] itemExtras = new Function[]{}; public Item( int price ) { this.price = price; } public Item( int price, Function... itemExtras ) { this.price = price; this.itemExtras = itemExtras; } public int getPrice() { Function extras = Stream.of( itemExtras ) .reduce( Function.identity(), Function::andThen ); return extras.apply( price ); } public void setItemExtras( Function... itemExtras ) { this.itemExtras = itemExtras; } }

Slide 57

Slide 57 text

Template “Definir o esqueleto de um algoritmo em uma operação, postergando alguns passos para as subclasses. Template Method permite que subclasses redefinam certos passo de um algoritmo sem mudar a estrutura do mesmo.” GAMMA, Erich et al.

Slide 58

Slide 58 text

public abstract class Banking { public void processOperation( Operation op ) { preProcessing( op ); process( op ); postProcessing( op ); } protected abstract void postProcessing( Operation op ); protected abstract void preProcessing( Operation op ); private void process( Operation op ) { //logic op.process( op ); } }

Slide 59

Slide 59 text

public class VIPBanking extends Banking { @Override protected void preProcessing( Operation op ) { //pre processing vip logic } @Override protected void postProcessing( Operation op ) { //post processing vip logic } } public class OnlineBanking extends Banking { @Override protected void preProcessing( Operation op ) { //pre processing online logic } @Override protected void postProcessing( Operation op ) { //post processing online logic } }

Slide 60

Slide 60 text

public class Banking { public void processOperation( Operation op ) { process( op ); } public void processOperation( Operation op, Consumer preProcessing, Consumer postProcessing ) { preProcessing.accept( op ); process( op ); postProcessing.accept( op ); } private void process( Operation op ) { //logic op.process( op ); } }

Slide 61

Slide 61 text

Execute Around

Slide 62

Slide 62 text

public static void main( String[] args ) throws IOException { BufferedReader br = new BufferedReader( new FileReader( "dora.txt" ) ); try { br.readLine(); } finally { br.close(); } }

Slide 63

Slide 63 text

Init / Código de preparação Task Cleanup/finalização

Slide 64

Slide 64 text

public static void main( String[] args ) throws IOException { BufferedReader br = new BufferedReader( new FileReader( "dora.txt" ) ); try { br.readLine(); } finally { br.close(); } }

Slide 65

Slide 65 text

try ( BufferedReader br = new BufferedReader( new FileReader( "dora.txt" ) ) ) { br.readLine(); }

Slide 66

Slide 66 text

@Override public ServerTemplate store( final ServerTemplate serverTemplate, final final List keys) { final Path path = buildPath( serverTemplate.getId() ); try { ioService.startBatch(path.getFileSystem()); ioService.write(path, serverTemplate); ioService.write(path, keys); } finally { ioService.endBatch(); } return serverTemplate; }

Slide 67

Slide 67 text

public void store( final ServerTemplate serverTemplate, final List keys ) { try { ioService.startBatch( path.getFileSystem() ); ioService.write( path, serverTemplate ); ioService.write( path, keys ); } finally { ioService.endBatch(); } }

Slide 68

Slide 68 text

public class IOService { … public void processInBatch( Path path, Consumer batchOp ) { try { startBatch( path.getFileSystem() ); batchOp.accept( path ); } finally { endBatch(); } } }

Slide 69

Slide 69 text

public void store( final ServerTemplate serverTemplate, final List keys ) { try { ioService.startBatch( path.getFileSystem() ); ioService.write( path, serverTemplate ); ioService.write( path, keys ); } finally { ioService.endBatch(); } }

Slide 70

Slide 70 text

public void store( final ServerTemplate serverTemplate, final List keys ) { ioService.processInBatch( path, ( path ) -> { ioService.write( path, serverTemplate ); ioService.write( path, keys ); } ); }

Slide 71

Slide 71 text

public void delete( final ServerTemplate serverTemplate, final List keys ) { ioService.processInBatch( path, ( path ) -> { ioService.delete( path, serverTemplate ); ioService.delete( path, keys ); } ); }

Slide 72

Slide 72 text

Chain of Responsibilities “Evitar o acoplamento do remetente de uma solicitação ao seu receptor, ao dar a mais de um objeto a oportunidade de tratar a solicitação. Encadear os objetos receptores, passando a solicitação ao longo da cadeia até que um objeto a trate.” GAMMA, Erich et al.

Slide 73

Slide 73 text

Client Handler 1 Request Handler 2 Request … Handler N Chain of Responsibilities

Slide 74

Slide 74 text

Client Payment Processor 1 Payment Payment … Payment Processor 2 Payment Processor n

Slide 75

Slide 75 text

public static void main( String[] args ) { PaymentProcessor paymentProcessor = getPaymentProcessor(); paymentProcessor.process( new Payment( 10 ) ); } private static PaymentProcessor getPaymentProcessor() { PaymentProcessor g = new PaymentProcessorA(); g.setNext( new PaymentProcessorB() ); g.setNext( new PaymentProcessorC() ); return g; }

Slide 76

Slide 76 text

public abstract class PaymentProcessor { private PaymentProcessor next; public void setNext( PaymentProcessor processors ) { if ( next == null ) { next = processors; } else { next.setNext( processors ); } } public Payment process( Payment p ) { handle( p ); if ( next != null ) { return next.process( p ); } else { return p; } } protected abstract void handle( Payment p ); }

Slide 77

Slide 77 text

public class PaymentProcessorA extends PaymentProcessor { @Override protected void handle( Payment p ) { System.out.println( "PaymentProcessorA for payment: " + p.getAmount() ); } } public class PaymentProcessorB extends PaymentProcessor { @Override protected void handle( Payment p ) { System.out.println( "PaymentProcessorB for payment: " + p.getAmount() ); } }

Slide 78

Slide 78 text

public static void main( String[] args ) { PaymentProcessor paymentProcessor = getPaymentProcessor(); paymentProcessor.process( new Payment( 10 ) ); //PaymentProcessorA for payment: 10 //PaymentProcessorB for payment: 10 //PaymentProcessorC for payment: 10 } private static PaymentProcessor getPaymentProcessor() { PaymentProcessor g = new PaymentProcessorA(); g.setNext( new PaymentProcessorB() ); g.setNext( new PaymentProcessorC() ); return g; }

Slide 79

Slide 79 text

Function processorA = p -> { System.out.println( "Processor A " + p.getAmount() ); return p; }; Function processorB = p -> { System.out.println( "Processor B " + p.getAmount() ); return p; }; Function processorC = p -> { System.out.println( "Processor C " + p.getAmount() ); return p; };

Slide 80

Slide 80 text

Function processorA = p -> { System.out.println( "Processor A " + p.getAmount() ); return p; }; Function processorB = p -> { System.out.println( "Processor B " + p.getAmount() ); return p; }; Function processorC = p -> { System.out.println( "Processor C " + p.getAmount() ); return p; }; Function chain = processorA.andThen( processorB ).andThen( processorC ); chain.apply( new Payment( 10 ) ); //Processor A 10 //Processor B 10 //Processor C 10

Slide 81

Slide 81 text

Observer "Define uma dependência um-para-muitos entre objetos de modo que quando um objeto muda o estado, todos seus dependentes são notificados e atualizados automaticamente. Permite que objetos interessados sejam avisados da mudança de estado ou outros eventos ocorrendo num outro objeto." GAMMA, Erich et al.

Slide 82

Slide 82 text

Servidor de Cotação Banco … Casa de Câmbio Investidor register() notify() notify() notify()

Slide 83

Slide 83 text

public interface Subject { void registerObserver( Observer observer ); } public interface Observer { void notify( Cotacao lance ); }

Slide 84

Slide 84 text

public class Banco implements Observer { @Override public void notify( Cotacao cotacao ) { //some cool stuff here System.out.println( "Banco: " + cotacao ); } } public class Investidor implements Observer { @Override public void notify( Cotacao cotacao ) { //some cool stuff here System.out.println( "Investidor: " + cotacao ); } }

Slide 85

Slide 85 text

public class ServidorCotacao implements Subject { private List observers = new ArrayList<>(); public void novaCotacao( Cotacao cotacao ) { notifyObservers( cotacao ); } @Override public void registerObserver( Observer observer ) { observers.add( observer ); } private void notifyObservers( Cotacao lanceAtual ) { observers.forEach( o -> o.notify( lanceAtual ) ); } }

Slide 86

Slide 86 text

public class Main { public static void main( String[] args ) { Banco banco = new Banco(); Investidor investidor = new Investidor(); ServidorCotacao servidorCotacao = new ServidorCotacao(); servidorCotacao.registerObserver( banco ); servidorCotacao.registerObserver( investidor ); servidorCotacao.novaCotacao( new Cotacao( "USD", 4 ) ); } } Banco: Cotacao{moeda='USD', valor=4} Investidor: Cotacao{moeda='USD', valor=4}

Slide 87

Slide 87 text

@Override public void registerObserver( Observer observer ) { observers.add( observer ); } public class Banco implements Observer { @Override public void notify( Cotacao cotacao ) { //some cool stuff here System.out.println( "Banco: " + cotacao ); } }

Slide 88

Slide 88 text

public class Main { public static void main( String[] args ) { ServidorCotacao servidorCotacao = new ServidorCotacao(); servidorCotacao.registerObserver( cotacao -> System.out.println( "Banco: " + cotacao ) ); servidorCotacao.registerObserver( cotacao -> { //some cool stuff here System.out.println( "Investidor: " + cotacao ) } ); servidorCotacao.novaCotacao( new Cotacao( "BRL", 1 ) ); } } Banco: Cotacao{moeda='BRL', valor=1} Investidor: Cotacao{moeda='BRL', valor=1}

Slide 89

Slide 89 text

Currying

Slide 90

Slide 90 text

f(x,y) = y/x

Slide 91

Slide 91 text

f(2,3) f(x,y) = y/x

Slide 92

Slide 92 text

f(2, y) = y / 2 g(y) = f(2,y) = y/2

Slide 93

Slide 93 text

g(y) = f(2,y) = y/2 g(3) = f(2,3) = 3/2

Slide 94

Slide 94 text

CtoF(x) = x * 9/5 + 32

Slide 95

Slide 95 text

static double converter( double x, double f, double b ) { return x * f + b; } public static void main( String[] args ) { Double celsius = 15.0; Double fahrenheit = converter( celsius, 9.0 / 5, 32 ); //59 F }

Slide 96

Slide 96 text

static double converter( double x, double f, double b ) { return x * f + b; } static DoubleUnaryOperator curriedConverter( double f, double b ) { return x -> x * f + b; }

Slide 97

Slide 97 text

static DoubleUnaryOperator curriedConverter( double f, double b ) { return x -> x * f + b; } public static void main( String[] args ) { DoubleUnaryOperator convertCtoF = curriedConverter( 9.0 / 5, 32 ); convertCtoF.applyAsDouble( 35 ); //95 F convertCtoF.applyAsDouble( 15 ); //59 F }

Slide 98

Slide 98 text

static DoubleUnaryOperator curriedConverter( double f, double b ) { return x -> x * f + b; } public static void main( String[] args ) { DoubleUnaryOperator convertCtoF = curriedConverter( 9.0 / 5, 32 ); convertCtoF.applyAsDouble( 35 ); //95 F DoubleUnaryOperator convertKmToMi = curriedConverter( 0.6214, 0 ); convertKmToMi.applyAsDouble( 804.672 ); //500milhas }

Slide 99

Slide 99 text

DoubleUnaryOperator convertBRLtoUSD = curriedConverter( 0.27, 0 ); double usd = convertBRLtoUSD.applyAsDouble( 100 );//27 USD DoubleUnaryOperator convertUSDtoEUR = curriedConverter( 0.89, 0 ); convertUSDtoEUR.applyAsDouble( usd ); //24.03 EUR convertBRLtoUSD.andThen( convertUSDtoEUR ).applyAsDouble( 100 ); //24.03 EUR

Slide 100

Slide 100 text

Design Patterns + Java + Programação Funcional <3

Slide 101

Slide 101 text

Obrigado Eder Ignatowicz @ederign