Java patterns in Scala

Post on 14-Jul-2015

264 views 0 download

transcript

Java design patterns in Scala

Radim Pavlicek

About me

● Java/Scala developer● bogey golfer● piano player

Agenda

Part I - OO PatternsFunctional InterfaceCommandBuilderIterator

Template methodStrategyNull Object

Functional Interfaceencapsulate a bit of program logic and treated like any other first-class construct

JavaCollections.sort(people,

new Comparator<Person>() {public int compare(Person p1, Person p2) {

return p1.getFirstName().compareTo(ps.getFirstName())

}})

Javaother use-cases● runnable● callable

Java 8 has solved

Scalapeople.sortWith(

(p1, p2) => p1.firstName < p2.firstName)

CommandTurn a method invocation into an objectand execute it in a central location

Javapublic class PrintCommand implements Runnable { private final String s; PrintCommand(String s) { this.s = s; } public void run() { System.out.println(s); }}

public class Invoker { private final List<Runnable> history = new ArrayList<>(); void invoke(Runnable command) { command.run(); history.add(command); }}Invoker invoker = new Invoker();invoker.invoke(new PrintCommand("Scala"));invoker.invoke(new PrintCommand("Vienna"));

Scalaobject Invoker { private var history: Seq[() => Unit] = Seq.empty

def invoke(command: => Unit) { // by-name parameter command history :+= command _ }}

Invoker.invoke(println( "foo"))

Scala advanceddef makePurchase(register: CaschRegister, amount: Int) =

{

() = {

println("Purchase in amount: " + amount)

register.addCash(amount)

}

}

// how to create purchase functions using closure

Builderto create an immutable object using a friendly syntax

Java Issues● constructor arguments

Person(String n,String n2,String nick● Defaults (telescoping constructor problem)

Person()Person(String name)Person(String name, String name2)...

Java codepublic class ImmutablePerson {

private final String firstName;

public String getFirstName() {

return firstName;}

private ImmetablePerson(Builder builder) {

firstName = builder.firstName;

}

public static Builder newBuilder() {

return new Builder();

}

}

public static class Builder { private String firstName; public Builder withFirstName(String firstName) { this.firstName = firstName; return this; } public ImmutablePerson build () { return new ImmutablePerson(this); } }

Scala - Case Classcase class Person {

firstName: String,

LastName: String,

nick: String = "")

val p = Person(firstName = “Radim”, lastName = “Pavlicek”)

val cloneMe = Person(firstName = “Radim”, lastName = “Pavlicek”)

p.equals(cloneMe) // true

val brother = p.copy(firstName = “Libor”) // Libor Pavlicek

p.toString // Person[firstName=”Radim”, lastName= “Pavlicek”]

Scala - Tuples● for explorative development

def p = (“Radim”, “Pavlicek”)p._1 // Radimp._2 // Pavlicek

Iteratoriterate through the elements of a sequence without having to index into it

Javapublic Set<Char> vowelsCount(String s) {

Set<Char> l = new HashSet<Char>(); for (Char c : s.toLowerCase().toCharArray())

if (isVowel(c))l.add(c)

}

Scala● filter● map● reduce

Scala filterfiltering certain elements from collection

def vowelsNumber(word : String) = word.filter(isVowel).toSet

vowelsNumber(“Radim”) // Set(‘a’, ‘i’)

Scala mapfunction is applied to each element

def prependHello(names : Seq[String]) = names.map((name) => “Hello, “ + name)

Scala reducereduce sequence to a single value

def sum(sq : Seq[Int]) = if (sq.isEmpty) 0 else sq.reduce((acc,curr) => acc + curr)

Scala comprehensions● guard

for (a <- list if isEven(a))● pattern matching

for (Person(name, address) <- people)● still no mutable state

Scala for comprehensionscase class Person(name: String, addr: Address)case class Address(zip: Int)def greetings(people: Seq[Person]) =for (Person(name, address) <-

people if isCloseZip(address.zip) )yield “Hello, %s”.format(name)

Template methoddefines the program skeleton of an algorithm in a method, called template method, which defers some steps to subclasses

Javapublic abstract class Template {

public void doStuff() {beforeStuff(); afterStuff();

}protected abstract void beforeStuff();protected abstract void afterStuff();

}

Scala Function Builderdef doStuff(

beforeStuff: () => Unit,afterStuff: () => Unit) =

() = {beforeSuff()afterStuff()

}

Scala Function Builderdef beforeStuff() = Console.println(“before”)def afterStuff() = Console.println(“after”)val test= doStuff(beforeStuff, afterStuff)

scala> test

Strategydefine an algorithm in abstract terms and make it interchangeable within family

Javapublic interface Strategy { int compute(int a, int b);}

public class Add implements Strategy { public int compute(int a, int b) { return a + b; }}

public class Multiply implements Strategy { public int compute(int a, int b) { return a * b; }}

public class Context { private final Strategy strategy;

public Context(Strategy strategy) { this.strategy = strategy; }

public void use(int a, int b) { strategy.compute(a, b); }}

new Context(new Multiply()).use(2, 3);

Scaladefine type alias and use first-class functionstype Strategy = (Int, Int) => Int

class Context(computer: Strategy) { def use(a: Int, b: Int) { computer(a, b) }}

val add: Strategy = _ + _

val multiply: Strategy = _ * _

new Context(multiply).use( 2, 3)

Null Objectavoid null checks in code andcentralize logic that deals with handling the absence of a value

Javaclass NullPerson extends Person {....}public Person build(String first, String last) {

if (first == null || last == null) return new NullPerson();

return new Person (first, last);}

Scalaval nullPerson = Person() // case classdef build(first: Option[String],

last: Option[String]) = (for (f <- first; l <- last)

yield Person(f, l)).getOrElse(nullPerson)

Decoratoradd behaviour to an existing classaka “there is a bug in the API class”

Javapublic interface OutputStream { void write(byte b); void write(byte[] b);}

public class FileOutputStream implements OutputStream { /* ... */ }

public abstract class OutputStreamDecorator implements OutputStream { protected final OutputStream delegate;

protected OutputStreamDecorator(OutputStream delegate) { this.delegate = delegate; }

public void write(byte b) { delegate.write(b); } public void write(byte[] b) { delegate.write(b); }}

Scaladef add(a: Int, b: Int) = a + bdef decorateLogger(calcF: (Int, Int) => Int) =

(a: Int, b: Int) => {val result = calcF(a, b)println(“Result is: “ + result) // here it comesresult

}

Scalaval loggingAdd = decorateLogger(add) scala> loggingAdd(1,4)Result is 5

Sourceshttp://pavelfatin.com/design-patterns-in-scala