[Codemotion 2015] patrones de diseño con java8

Preview:

Citation preview

Alonso Torres @alotor

Design Patterns

“ ”

Cada patrón describe un problema recurrente en nuestro entorno, así como la solución a ese problema, de tal modo que se pueda aplicar esta solución un millón de veces, sin hacer lo mismo dos veces.

- Christopher Alexander, A Pattern Language

Los patrones definen:

soluciones a problemas frecuentes

¿ ?

Son las soluciones con Java 8 las mismas que con versiones anteriores

λ

Alonso Torres@alotor

mobro.co/alotor

El camino hacia

1996

1990 1995 2000 2005 2010 2015

1.0 1.2 1.4 5 6 7 81.3

1994

1990 1995 2000 2005 2010 2015

1.0 1.2 1.4 5 6 7 81.3

1998 - J2SE 1.2

1990 1995 2000 2005 2010 2015

1.0 1.2 1.4 5 6 7 81.3

Adopción masiva del

lenguaje

2004 - J2SE 5.0

1990 1995 2000 2005 2010 2015

1.0 1.2 1.4 5 6 7 81.3

GenéricosAnotacionesEnumeradosNueva sintaxisConcurrencia

1990 1995 2000 2005 2010 2015

1.0 1.2 1.4 5 6 7 81.3

1990 1995 2000 2005 2010 2015

1.0 1.2 1.4 5 6 7 81.3

2011 - Java SE 7

1990 1995 2000 2005 2010 2015

1.0 1.2 1.4 5 6 7 81.3

InvokeDynamicNIO 2Nueva concurrencia

2014 - Java SE 8

1990 1995 2000 2005 2010 2015

1.0 1.2 1.4 5 6 7 81.3

Java 8

● Lambda expressions

● Functional interfaces

● Default methods

● Streams

Java 8

● Lambda expressions● Functional interfaces

● Default methods

● Streams

Las lambdas son lo mejor que le ha pasado a Java desde….

Las lambdas son lo mejor que le ha pasado a Java.

1990 1995 2000 2005 2010 2015

1.0 1.2 1.4 5 6 7 81.3

C#

1990 1995 2000 2005 2010 2015

1.0 1.2 1.4 5 6 7 81.3

ActionScript Groovy Perl

C# Haskell PHP

C++ JavaScript Python

Clojure Lisp R

Curl Logtalk Racket

D Lua Ruby

Dart Mathematica Scala

Dylan Maple Scheme

Erlang Matlab Smalltalk

Elixir Maxima Standard ML

F# OCaml TypeScript

Go Object Pascal Tcl

Gosu Objective-C VisualBasic .NET

Fortran

C

Ada

Cobol

Fortran 1957

C 1969

Ada 1977

Cobol 1989

15%

20%

Features

● Lambda expressions

● Functional interfaces

● Default methods

● Streams

Lambdas: funciones anónimas

public Integer addition(Integer x, Integer y) {

return x + y;

}

public void sayHello(String name) {

System.out.println("Hello " + name);

}

public Float giveMePi() {

return 3.15f;

}

public Integer addition(Integer x, Integer y) {

return x + y;

}

public void sayHello(String name) {

System.out.println("Hello " + name);

}

public Float giveMePi() {

return 3.15f;

}

(Integer x, Integer y) -> {

return x + y;

};

(String name) -> {

System.out.println("Hello" + name);

};

() -> {

return 3.15f;

};

(Integer x, Integer y) -> x + y;

(String name) -> {

System.out.println("Hello" + name);

};

() -> 3.15f

(Integer x, Integer y) -> x + y;

(String name) -> {

System.out.println("Hello" + name);

};

() -> 3.15f

PARÁMETROS CUERPO

(parameters) -> body;

(Integer x, Integer y) -> x + y;

(String name) -> System.out.println("Hello" + name);

() -> 3.15f

Thread th = new Thread(new Runnable(){

@Override

public void run() {

System.out.println(">> Another thread"));

}

});

th.start();

Creando hilos

Thread th = new Thread(() ->

System.out.println(">> Another thread"));

th.start();

Creando hilos

Me váis a llamar loco pero…

…en ocasiones veo patrones

Thread th = new Thread(() ->

System.out.println(">> Another thread"));

th.start();

Creando hilos

Client Invoker

Receiver

+action()

ConcreteCommand

+execute()

Command

+execute()

CommandPATRÓN

new Invoker().addCommand(new Command() {

@Override

public void execute() {

receiver.action();

}

});

new Transaction().doWithTransaction(new Command() {

@Override

public void execute() {

myBusinessLogic.calculateInterests();

}

});

new Context().doAsync(new Command() {

@Override

public void execute() {

networkManager.downloadFile();

}

});

Client Invoker

Receiver

+action()

ConcreteCommand

+execute()

Command

+execute()

Client Invoker

Receiver

+action()

ConcreteCommand

+execute()

Command

+execute()

Lambda

new Invoker().addCommand(new Command() {

@Override

public void execute() {

receiver.action();

}

});

new Invoker().addCommand(new Command() {

@Override

public void execute() {

receiver.action();

}

});

new Invoker().addCommand(

() -> receiver.action();

);

surely,we

can do

better

new Invoker().addCommand(

receiver::action

);

Method Reference

Method Reference:Transformar un método en una lambda

() -> receiver.action()

(el) -> el.toString()

() -> Runtime.getInstance()

() -> new Receiver()

() -> receiver.action()

(el) -> el.toString()

() -> Runtime.getInstance()

() -> new Receiver()

() -> receiver.action() receiver::action

(el) -> el.toString() Object::toString

() -> Runtime.getInstance() Runtime::getInstance

() -> new Receiver() Reciver::new

Instance method reference

(objeto concreto)

Instance method reference

(cualquier objeto)

Static method reference

Constructor reference

receiver::action

Object::toString

Runtime::getInstance

Reciver::new

(Integer x, Integer y) ->

receiver.action(x, y)

(Integer x, Integer y) ->

Math.max(x, y)

(String str) ->

new String(str)

(Integer x, Integer y) ->

receiver.action(x, y)receiver::action

(Integer x, Integer y) ->

Math.max(x, y)

Math::max

(String str) ->

new String(str)String::new

new Transaction().doWithTransaction(

myBusinessLogic::calculateInterests

);

new ThreadPool().doInThread(

networkManager::downloadFile

);

new Transaction().doWithTransaction(

networkManager::downloadFile

);

new ThreadPool().doInThread(

myBusinessLogic::calculateInterests

);

Client Invoker

Receiver

+action()

ConcreteCommand

+execute()

Command

+execute()

Client Invoker

Receiver

+action()

ConcreteCommand

+execute()

Command

+execute()

Client Invoker

Receiver

+action()

Command

+execute()

(Integer x, Integer y) -> x + y;

(String name) -> System.out.println("Hello" + name);

() -> 3.15f

¿Qué TIPO tienen las lambdas?

Object addition =

(Integer x, Integer y) -> x + y;

Object sayHello =

(String name) -> System.out.println("Hello" + name);

Object giveMePi =

() -> 3.15f

¿Qué TIPO tienen las lambdas?

The target type of this expression must be a

functional interface

Java 8

● Lambda expressions

● Functional interfaces

● Default methods

● Streams

(Integer x, Integer y) -> x + y;

(String name) -> System.out.println("Hello" + name);

() -> 3.15f

(Integer x, Integer y) -> x + y;

(String name) -> System.out.println("Hello" + name);

() -> 3.15f

Integer addition(Integer x, Integer y)

void sayHello(String name)

Float giveMePi()

Functional interfaces

public interface Addition {

Integer addition(Integer x, Integer y);

}

public interface SayHello {

void sayHello(String name);

}

public interface GiveMePi {

Float giveMePi();

}

Functional interfaces

public interface Addition {

public Integer addition(Integer x, Integer y);

}

1 sólo método

Functional interfaces

public interface Addition {

public Integer addition(Integer x, Integer y);

}

Addition addition =

(Integer x, Integer y) -> x + y;

public interface Addition {

public Integer addition(Integer x, Integer y);

}

public interface Operation<T extends Number> {

public T do(T x, T y);

}

Operation<Integer> addition =

(Integer x, Integer y) -> x + y;

PROBLEMA

Sólo funciona con interfaces

● NO pueden ser clases

● NI clases abstractas

Java 8

● Lambda expressions

● Functional interfaces

● Default methods

● Streams

Default Methods

public interface Addition {

public Integer addition(Integer x, Integer y);

default Integer substraction(Integer x, Integer y) {

return addition(x, -y);

}

}

public interface Addition {

public Integer addition(Integer x, Integer y);

}

Addition addition =

(Integer x, Integer y) -> x + y;

Si tenemos que definir una “Functional Interface” por cada lambda...

¿Qué hemos ganado?

Java 8 viene con las interfaces más comunes

DE SERIE

import java.util.function.*

BiConsumer<T,U> IntBinaryOperator LongUnaryOperator

BiFunction<T,U,R> IntConsumer ObjDoubleConsumer<T>

BinaryOperator<T> IntFunction<R> ObjIntConsumer<T>

BiPredicate<T,U> IntPredicate ObjLongConsumer<T>

BooleanSupplier IntSupplier Predicate<T>

Consumer<T> IntToDoubleFunction Supplier<T>

DoubleBinaryOperator IntToLongFunction ToDoubleBiFunction<T,U>

DoubleConsumer IntUnaryOperator ToDoubleFunction<T>

DoubleFunction<R> LongBinaryOperator ToIntBiFunction<T,U>

DoublePredicate LongConsumer ToIntFunction<T>

DoubleSupplier LongFunction<R> ToLongBiFunction<T,U>

DoubleToIntFunction LongPredicate ToLongFunction<T>

DoubleToLongFunction LongSupplier UnaryOperator<T>

DoubleUnaryOperator LongToDoubleFunction

Function<T,R> LongToIntFunction

(Integer x, Integer y) -> x + y;

(String name) -> System.out.println("Hello" + name);

() -> 3.15f

¿Qué TIPO tienen?

public Integer addition(Integer x, Integer y)

public void sayHello(String name)

public Float giveMePi()

BiConsumer<T,U> IntBinaryOperator LongUnaryOperator

BiFunction<T,U,R> IntConsumer ObjDoubleConsumer<T>

BinaryOperator<T> IntFunction<R> ObjIntConsumer<T>

BiPredicate<T,U> IntPredicate ObjLongConsumer<T>

BooleanSupplier IntSupplier Predicate<T>

Consumer<T> IntToDoubleFunction Supplier<T>

DoubleBinaryOperator IntToLongFunction ToDoubleBiFunction<T,U>

DoubleConsumer IntUnaryOperator ToDoubleFunction<T>

DoubleFunction<R> LongBinaryOperator ToIntBiFunction<T,U>

DoublePredicate LongConsumer ToIntFunction<T>

DoubleSupplier LongFunction<R> ToLongBiFunction<T,U>

DoubleToIntFunction LongPredicate ToLongFunction<T>

DoubleToLongFunction LongSupplier UnaryOperator<T>

DoubleUnaryOperator LongToDoubleFunction

Function<T,R> LongToIntFunction

import java.util.function.*

BinaryOperator<Integer> addition =

(Integer x, Integer y) -> x + y;

Consumer<String> sayHi =

(String name) -> System.out.println("Hello" + name);

Supplier<Float> giveMePi =

() -> 3.15f

BinaryOperator<Integer> addition =

(x, y) -> x + y;

Consumer<String> sayHi =

(name) -> System.out.println("Hello" + name);

Supplier<Float> giveMePi =

() -> 3.15f

Thread th = new Thread(() ->

System.out.println(">> Another thread"));

th.start();

java.lang.Runnable

JButton button = new JButton("Button");

button.addActionListener((event) ->

System.out.println("Mouse: " + event);

);

java.awt.ActionListener

ObserverPATRÓN

ConcreteObserverA

+notify()

ConcreteObserverB

+notify()

Observer

+notify()

ObservedClass

+registerObserver()+unregisterObserver()+notifyObservers()

Functional Interface

Lambdas!

@FunctionalInterface

public static interface Observer {

public void notify(Object change);

}

@FunctionalInterface

public static interface Observer {

public void notify(Object change);

}

@FunctionalInterface

public static interface Observer {

public void notify(Object change);

}

El compilador fallará si no cumple con las condiciones de una FI.

@FunctionalInterface

public static interface Observer {

public void notify(Object change);

}

El compilador fallará si no cumple con las condiciones de una FI.

Tu yo del futuro te lo agradecerá

public static class ObservedClass {

private List<Observer> observers = new LinkedList<>();

public void addObserver(Observer observer) {

observers.add(observer);

}

public void removeObserver(Observer observer) {

observers.remove(observer);

}

public void notifyChange(Object change) {

observers.forEach((o) -> o.notify(change));

}

}

public static class ObservedClass {

private List<Observer> observers = new LinkedList<>();

public void addObserver(Observer observer) {

observers.add(observer);

}

public void removeObserver(Observer observer) {

observers.remove(observer);

}

public void notifyChange(Object change) {

observers.forEach((o) -> o.notify(change));

}

}

Una estructura de datos que guarda lambdas.

public static class ObservedClass {

private List<Observer> observers = new LinkedList<>();

public void addObserver(Observer observer) {

observers.add(observer);

}

public void removeObserver(Observer observer) {

observers.remove(observer);

}

public void notifyChange(Object change) {

observers.forEach((o) -> o.notify(change));

}

}

Nuevo método de Java 8

observed.addObserver((change) -> {

new Thread(UIThread::update).start()

});

sbj.notifyChange("Change");

Hipótesis

¿Puede haber un patrón de más alto nivel?

new Invoker().addCommand(receiver::action);

observed.addObserver(receiver::action);

Pasar comportamiento como argumentos

● El comportamiento se ejecutará en otro contexto

● “Separation of concerns”

● También conocido como: High Order Functions

new Invoker().addCommand(receiver::action);

observed.addObserver(receiver::action);

List<Person> personList = new LinkedList<>();

Collections.sort(

personList,

(p1, p2) -> p1.age - p2.age

);

java.lang.Comparable<T>

List<Person> personList = new LinkedList<>();

Collections.sort(

personList,

(p1, p2) -> p1.age - p2.age

);

java.lang.Comparable<T>

DATOS

COMPORTAMIENTO

StrategyPATRÓN

Collections.sort( list, (p1, p2) -> p1.getAge() - p2.getAge());

Collections.sort( list, (p1, p2) -> p1.getName().compareTo(p2.getName()));

Collections.sort( list, (p1, p2) -> -p1.getName().compareTo(p2.getName()));

Reusing lambdas

class PersonSortCriterias {

public static int sortByAge(Person p1, Person p2) {

return p1.getAge() - p2.getName();

}

public static int sortByName(Person p1, Person p2) {

return p1.getName().compareTo(p2.getName());

}

public static int sortByNameReverse(Person p1, Person p2) {

return -p1.getName().compareTo(p2.getName());

}

}

Collections.sort( list, PersonSortCriterias::sortByAge);

Collections.sort( list, PersonSortCriterias::sortByName);

Collections.sort( list, PersonSortCriterias::sortByNameReverse);

Set<Person> = new TreeSet<>( PersonSortCriterias::sortByAge);

Person theOlder = Collections.max( list, PersonSortCriterias::sortByAge);

list.sort(PersonSortCriterias::sortByAge);

Java 8

● Lambda expressions

● Functional interfaces

● Default methods

● Streams

Java 8

● Lambda expressions

● Functional interfaces

● Default methods

● Streams

IteratorPATRÓN

for (String str : list) {

System.out.println(str);

}

for (String str : list) {

if (str.length <= 5) {

System.out.println(str);

}

}

for (String str : Collections.sort(list)) {

if (str.length <= 5) {

System.out.println(str);

}

}

for (String str : Collections.sort(list).sublist(0, 10)) {

if (str.length <= 5) {

System.out.println(str);

}

}

for (String str : Collections.sort(list).sublist(0, 10)) {

if (str.length <= 5) {

System.out.println(str.toUpperCase());

}

}

Problemas

● Susceptible a errores

● Difícil de razonar

● Difícil de reutilizar

Streams

● Interfaz fluida

● Evaluación perezosa

● Iterable

● Aprovechan toda la expresividad de las lambdas

list.stream()

.forEach(System.out::println);

list.stream()

.filter(s -> s.length <= 5)

.forEach(System.out::println);

list.stream()

.filter(s -> s.length <= 5)

.sorted()

.forEach(System.out::println);

list.stream()

.filter(s -> s.length <= 5)

.sorted()

.limit(10)

.forEach(System.out::println);

list.parallelStream()

.filter(s -> s.length <= 5)

.sorted()

.limit(10)

.forEach(System.out::println);

Funciones importantes

● Map

● Filter

● Reduce

MAP - FILTER - REDUCE

list.stream()

.map(String::toUpperCase)

"banana"

"apple"

"pear"

"pineapple"

"lemon"

"mango"

"raspberry"

"melon"

"BANANA"

"APPLE"

"PEAR"

"PINEAPPLE"

"LEMON"

"MANGO"

"RASPBERRY"

"MELON"

MAP - FILTER - REDUCE

list.stream()

.filter(s -> s.length <= 5)

"banana"

"apple"

"pear"

"pineapple"

"lemon"

"mango"

"raspberry"

"melon"

"apple"

"pear"

"lemon"

"mango"

"melon"

MAP - FILTER - REDUCE

list.stream()

.reduce("", (acc, elem) -> acc + ", " + elem);

"banana"

"apple"

"pear"

"pineapple"

"lemon"

"mango"

"raspberry"

"melon"

"banana,apple,pear,pineapple,...

Chain of responsibility

PATRÓN

HANDLER HANDLER HANDLER HANDLER HANDLERrequest

HANDLER HANDLER HANDLER HANDLER HANDLERrequest

HANDLER HANDLER HANDLER HANDLER HANDLERrequest

HANDLER HANDLER HANDLER HANDLER HANDLERrequest

@FunctionalInterfacepublic interface Handler { public Request handleRequest(Request req);}

@FunctionalInterfacepublic interface Handler extends Function<Request, Request> {}

List<Handler> chain = new LinkedList<>();

chain.add(Request::session);

chain.add(Request::securityCheck);

chain.add(Request::cookies);

chain.add(Request::getParameters);

List<Handler> chain = new LinkedList<>();

chain.add(Request::session);

chain.add(Request::securityCheck);

chain.add(Request::cookies);

chain.add(Request::getParameters);

Request req = new Request();

Request processed = chain.stream() .reduce(initial value, accumulation function)

List<Handler> chain = new LinkedList<>();

chain.add(Request::session);

chain.add(Request::securityCheck);

chain.add(Request::cookies);

chain.add(Request::getParameters);

Request req = new Request();

Request processed = chain.stream() .reduce(req, accumulation function)

List<Handler> chain = new LinkedList<>();

chain.add(Request::session);

chain.add(Request::securityCheck);

chain.add(Request::cookies);

chain.add(Request::getParameters);

Request req = new Request();

Request processed = chain.stream() .reduce(req, (old, handler) -> handler.apply(old))

HANDLER HANDLER HANDLER HANDLER HANDLERrequest

HANDLER HANDLER HANDLER HANDLER HANDLERrequest

CHAIN (HANDLER)

List<Handler> filters = new LinkedList<>();

filters.add(Request::session);

filters.add(Request::securityCheck);

filters.add(Request::cookies);

filters.add(Request::getParameters);

Handler filterChain =

filters.stream()

.reduce(

initial value,

accumulation function);

List<Handler> filters = new LinkedList<>();

filters.add(Request::session);

filters.add(Request::securityCheck);

filters.add(Request::cookies);

filters.add(Request::getParameters);

Handler filterChain =

filters.stream()

.reduce(

(r) -> r,

accumulation function);

List<Handler> filters = new LinkedList<>();

filters.add(Request::session);

filters.add(Request::securityCheck);

filters.add(Request::cookies);

filters.add(Request::getParameters);

Handler filterChain =

filters.stream()

.reduce(

(r) -> r,

(f1, f2) -> f1.andThen(f2));

List<Handler> filters = new LinkedList<>();

filters.add(Request::session);

filters.add(Request::securityCheck);

filters.add(Request::cookies);

filters.add(Request::getParameters);

Handler filterChain =

filters.stream()

.reduce(

Function.identity(),

Function::andThen);

System.out.println("\nProcessing request 1");

Request r1 = chain.apply(new Request());

System.out.println("\nProcessing request 2");

Request r2 = chain.apply(new Request());

Processing request 1>> process session>> process securityCheck>> process cookies>> process getParameters

Processing request 2>> process session>> process securityCheck>> process cookies>> process getParameters

¯\_(ツ)_/¯

Java SE 8

● Lambda expressions

● Functional interfaces

● Default methods

● Streams

Patrones

● Command

● Strategy

● Observer

● Iterator

● Chain of responsibility

Los patrones definen:

soluciones a problemas frecuentes

Problema

● El comportamiento y el contexto son diferentes

● Encapsular los datos

● Composición de comportamiento

Solución

● High order functions (Lambdas)

Las lambdas son lo mejor que le ha pasado a Java.

Sobre los hombros de gigantes

Mario Fusco Venkat Subramaniam Samir Talwarhttps://youtu.be/K6BmGBzIqW0 https://vimeo.com/122645679https://youtu.be/8qcHPEyLkPE

@alotor

mobro.co/alotor

¿Preguntas?

@alotor

Referencias

● https://en.wikipedia.org/wiki/Java_version_history

● http://www.studytrails.com/java/java8/Java8_Lambdas_FunctionalProgramming.jsp

● https://blog.idrsolutions.com/2015/02/java-8-method-references-explained-5-minutes/

● http://www.infoq.com/articles/Java-8-Lambdas-A-Peek-Under-the-Hood

● http://www.javaworld.com/article/2078675/core-java/design-patterns--the-big-picture--part-2--gang-

of-four-classics-revisited.html

● http://stackoverflow.com/questions/1673841/examples-of-gof-design-patterns-in-javas-core-libraries

● https://www.questers.com/sites/all/themes/questers/sec_presentation/java8lambdas.html

● http://www.slideshare.net/jaxlondon2012/lambda-a-peek-under-the-hood-brian-goetz

● http://www.infoq.com/presentations/lambda-invokedynamic?

utm_source=infoq&utm_medium=videos_homepage&utm_campaign=videos_row1

● http://www.slideshare.net/DaewonJeong/scala-is-java8next

● http://programmers.stackexchange.com/questions/173441/what-triggered-the-popularity-of-lambda-

functions-in-modern-mainstream-programmi

● http://www.infoq.com/presentations/A-Brief-History-of-the-Java-World

Referencias

● http://j2objc.org/blog/2015/08/07/lambdas-in-java-8-dysfunctional-interfaces.html

● http://gafter.blogspot.com.es/2006/08/closures-for-java.html

● http://www.javaworld.com/article/2077569/core-java/java-tip-68--learn-how-to-implement-the-

command-pattern-in-java.html

● http://www.slideshare.net/mariofusco/fp-in-java-project-lambda-and-beyond?qid=11ae9463-c2e3-

4c54-842f-b228ce109200&v=default&b=&from_search=12

● http://www.norvig.com/design-patterns/design-patterns.pdf

● http://www.techempower.com/blog/2013/03/26/everything-about-java-8/

● http://www.oracle.com/technetwork/server-storage/ts-4961-159222.pdf

● http://talks.samirtalwar.com/design-patterns-in-the-21st-century.html