Replacing functional interfaces
object ReplaceDesignPatterns {
case class Person(fn: String, ln: String)
val p1 = Person("Michael", "Bevilacqua")
val p2 = Person("Pedro", "Vas")
val p3 = Person("Rober", "Aar")
val p4 = Person("Michael", "Revilacqua")
val people = Vector(p3, p2, p1, p4)
val people1 = people.sortWith((x, y) => { x.fn < y.fn })
def complicatedSort(p1: Person, p2: Person) = {
if (p1.fn != p2.fn) p1.fn < p2.fn
else if (p1.ln != p2.ln) p1.ln < p2.ln
else true
}
val people2 = people.sortWith(complicatedSort)
def makeComposedComparison(comparisons: ((Person, Person) => Int)*) = {
(p1: Person, p2: Person) =>
comparisons.view.map(cmp => cmp(p1, p2)).find(_ != 0).getOrElse(0)
}
def fnCmp(p1: Person, p2: Person) = p1.fn.compareTo(p2.fn)
def lnCmp(p1: Person, p2: Person) = p1.ln.compareTo(p2.ln)
val cmp = makeComposedComparison(fnCmp, lnCmp)
}
Replacing command pattern
import scala.collection.mutable.ArrayBuffer
object ReplaceDesignPatterns {
class CashRegister(var total: Int) {
def addCash(toAdd: Int): Unit = {
total += toAdd
}
}
def makePurchase(register: CashRegister, amount: Int) = {
() => {
println("Purchase amount: " + amount)
register.addCash(amount)
}
}
var purchases = new ArrayBuffer[() => Unit]()
val register = new CashRegister(0)
(1 to 4).foreach(x => {
purchases += makePurchase(register, x)
})
purchases.foreach(x => x())
println(register.total)
}
Replacing iterator pattern
object ReplaceDesignPatterns {
case class Person(name: String, address: Address)
case class Address(zip: Int)
def generateGreetings(people: Seq[Person]) = {
for {
Person(name, Address(zip)) <- people
if isCloseZip(zip)
} yield "%, welcome!".format(name)
}
def isCloseZip(zip: Int) = zip == 19132 || zip == 19324
}
Replacing the template pattern
object GradeReporter {
def makeGradeReporter(
numToLetter: (Double) => String,
printGradeReport: (Seq[String]) => Unit) = (grades: Seq[Double]) => {
printGradeReport(grades.map(numToLetter))
}
def fullGradeConverter(grade: Double) =
if(grade <= 5.0 && grade > 4.0) "A"
else if(grade <= 4.0 && grade > 3.0) "B"
else if(grade <= 3.0 && grade > 2.0) "C"
else if(grade <= 2.0 && grade > 0.0) "D"
else if(grade == 0.0) "F"
else "N/A"
def printHistogram(grades: Seq[String]) = {
val grouped = grades.groupBy(identity)
val counts = grouped.map((kv) => (kv._1, kv._2.size)).toSeq.sorted
for(count <- counts) {
val stars = "*" * count._2
println("%s: %s".format(count._1, stars))
}
}
val sampleGrades = Vector(5.0, 4.0, 4.4, 2.2, 3.3, 3.5)
val fullGradeReporter = makeGradeReporter(fullGradeConverter, printHistogram)
def plusMinusGradeConverter(grade: Double) =
if(grade <= 5.0 && grade > 4.7) "A"
else if(grade <= 4.7 && grade > 4.3) "A-"
else if(grade <= 4.3 && grade > 4.0) "B+"
else if(grade <= 4.0 && grade > 3.7) "B"
else if(grade <= 3.7 && grade > 3.3) "B-"
else if(grade <= 3.3 && grade > 3.0) "C+"
else if(grade <= 3.0 && grade > 2.7) "C"
else if(grade <= 2.7 && grade > 2.3) "C-"
else if(grade <= 2.3 && grade > 0.0) "D"
else if(grade == 0.0) "F"
else "N/A"
def printAllGrades(grades: Seq[String]) =
for(grade <- grades) println("Grade is: " + grade)
val plusMinusGradeReporter =
makeGradeReporter(plusMinusGradeConverter, printAllGrades)
}
Replace strategy pattern
This is such a fuss. I would just use filter
from the standard collections. Higher order function solves this problem trivially.
object PeopleExample {
case class Person(
firstName: Option[String],
middleName: Option[String],
lastName: Option[String])
def isFirstNameValid(person: Person) = person.firstName.isDefined
def isFullNameValid(person: Person) = person match {
case Person(firstName, middleName, lastName) =>
firstName.isDefined && middleName.isDefined && lastName.isDefined
}
def personCollector(isValid: (Person) => Boolean) = {
var validPeople = Vector[Person]()
(person: Person) => {
if(isValid(person)) validPeople = validPeople :+ person
validPeople
}
}
}
object Test {
import PeopleExample._
val p1 = Person(Some("John"), Some("Quincy"), Some("Adams"))
val p2 = Person(Some("Mike"), None, Some("Linn"))
val p3 = Person(None, None, None)
val people = List(p1, p2, p3)
val collector1: (Person) => Vector[Person] = personCollector(isFirstNameValid)
var validated: Vector[Person] = Vector.empty[Person]
people.foreach(x => {
validated = collector1(x)
})
validated // two persons
val collector2 = personCollector(isFullNameValid)
var validated2: Vector[Person] = Vector.empty[Person]
people.foreach(x => {
validated2 = collector2(x)
})
validated2 // only one person
}
Replacing the visitor pattern
In Scala there is the “enrich my library” pattern using implicit classes.
object Examples {
trait Person {
def fullName: String
def firstName: String
def lastName: String
def houseNum: Int
def street: String
}
class SimplePerson(val firstName: String, val lastName: String,
val houseNum: Int, val street: String) extends Person {
def fullName = firstName + " " + lastName
}
class ComplexPerson(name: Name, address: Address) extends Person {
def fullName = name.firstName + " " + name.lastName
def firstName = name.firstName
def lastName = name.lastName
def houseNum = address.houseNum
def street = address.street
}
class Address(val houseNum: Int, val street: String)
class Name(val firstName: String, val lastName: String)
implicit class ExtendedPerson(person: Person) {
def fullAddress = person.houseNum + " " + person.street
}
}
Replacing dependency injection
Since you have traits and inheritence and mixins, you can inject all sorts of things in a choose-and-compose manner:
object MovieExample {
case class Movie(movieId: String, title: String)
case class Video(movieId: String)
case class DecoratedMovie(movie: Movie, video: Video)
trait MovieDaoComponent {
trait MovieDao {
def getMovie(id: String): Movie
}
}
trait FavoritesServiceComponent {
trait FavoritesService {
def getFavoriteVideos(id: String): Vector[Video]
}
}
trait MovieDaoComponentImpl extends MovieDaoComponent {
class MovieDaoImpl extends MovieDao {
def getMovie(id: String): Movie = new Movie("42", "A Movie")
}
}
trait FavoritesServiceComponentImpl extends FavoritesServiceComponent {
class FavoritesServiceImpl extends FavoritesService {
def getFavoriteVideos(id: String): Vector[Video] = Vector(new Video("1"))
}
}
trait MovieServiceComponentImpl {
this: MovieDaoComponent with FavoritesServiceComponent =>
val favoritesService: FavoritesService
val movieDao: MovieDao
class MovieServiceImpl {
def getFavoriteDecoratedMovies(userId: String): Vector[DecoratedMovie] =
for (
favoriteVideo <- favoritesService.getFavoriteVideos(userId);
movie = movieDao.getMovie(favoriteVideo.movieId)
) yield DecoratedMovie(movie, favoriteVideo)
}
}
object ComponentRegistry extends MovieServiceComponentImpl
with FavoritesServiceComponentImpl with MovieDaoComponentImpl {
val favoritesService = new FavoritesServiceImpl
val movieDao = new MovieDaoImpl
val movieService = new MovieServiceImpl
}
trait MovieDaoComponentTestImpl extends MovieDaoComponent {
class MovieDaoTestImpl extends MovieDao {
def getMovie(id: String): Movie = new Movie("43", "A Test Movie")
}
}
trait FavoritesServiceComponentTestImpl extends FavoritesServiceComponent {
class FavoritesServiceTestImpl extends FavoritesService {
def getFavoriteVideos(id: String): Vector[Video] = Vector(new Video("2"))
}
}
object TestComponentRegistery extends MovieServiceComponentImpl
with FavoritesServiceComponentTestImpl with MovieDaoComponentTestImpl {
val favoritesService = new FavoritesServiceTestImpl
val movieDao = new MovieDaoTestImpl
val movieService = new MovieServiceImpl
}
}
0 comments:
Post a Comment