java patterns in scala
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