Design Patterns
created: 14 May 2021
modified: 04 November 2022
revision: 3
OO Patterns (Linn, 2014)
Functional Interface -> Named Function
State carrying functional i-face -> closures (a function and its context at the time of its definition)
public class ComposedComparator<T> implements Comparator<T> {
private Comparator<T>[] comparators;
public ComposedComparator(Comparator<T>... comparators) {
this.comparators = comparators;
}
@Override
public int compare(T o1, T o2) {
//Iterate through comparators and call each in turn.
}
}
def makeComposedComparison(comparisons: (Person, Person) => Int) = {
(p1: Person, p2: Person) => comparisons.map(cmp => cmp(p1, p2)).find(_ != 0).getOrElse(0)
}
def firstNameComparison(p1: Person, p2: Person) = p1.firstName.compareTo(p2.firstName)
def lastNameComparison(p1: Person, p2: Person) = p1.lastName.compareTo(p2.lastName)
val firstAndLastNameComparison = makeComposedComparison(firstNameComparison, lastNameComparison)
val p1 = Person("John", "", "Adams")
val p2 = Person("John", "Quincy", "Adams")
firstAndLastNameComparison(p1, p2)
Command
Builder -> Immutable object
Iterator -> A sequence comprehension
Template
public abstract class TemplateExample{
public void anOperation(){
subOperationOne();
subOperationTwo();
}
protected abstract void subOperationOne();
protected abstract void subOperationTwo();
}
def makeAnOperation(subOperationOne: () => Unit, subOperationTwo: () => Unit) = () => {
subOperationOne()
subOperationTwo()
}
Strategy
Null object
Decorator or Wrapper
Visitor -> implicit conversion, or mix-inheritance/traits
DI
(Fatin, n.d.) Adapter Scala has built-in concept of interface adapters, expressed as implicit
classes
Functional Patterns
Tail recursion
Not supported by JVM => Scala’s @tailrec
, if the function is called recursively without being in the tail position, the compiler will generate an error.
Mutual recursion (trampoline)
Not supported by JVM => Scala’s support for trampolining hides a lot of the details of this and provides us with both a tailcall()
method to make mutually recursive calls and a done()
method to call when we’re done with the recursion.
def isOdd(n: Long): Boolean = if (n == 0) false else isEven(n - 1)
def isEven(n: Long): Boolean = if (n == 0) true else isOdd(n - 1)
// usage
isOdd(1001) // res3: Boolean = true
isOdd(100001) // java.lang.StackOverflowError
//
def isOddTrampoline(n: Long): TailRec[Boolean] = if (n == 0) done(false) else tailcall(isEvenTrampoline(n - 1))
def isEvenTrampoline(n: Long): TailRec[Boolean] = if (n == 0) done(true) else tailcall(isOddTrampoline(n - 1))
// usage
isOddTrampoline(100001).result // res4: Boolean = true, but it is O(n) (linear)
Filter-Map-Reduce
Chain of operations
Function Builder
Memoisation
Lazy sequence - Scala’s has built-in support for Lazy Sequence in its Stream library
Focused mutabilty - Good for holding a collection for indexing as it is faster than with immutable objects
References
- Fatin, P. Design Patterns in Scala. Retrieved January 21, 2021, from https://pavelfatin.com/design-patterns-in-scala/
- Linn, M. (2014). Functional programming patterns in Scala and Clojure. Pragmatic Programmers.