Specification Pattern by Scala
Join the DZone community and get the full member experience.
Join For Free Specification
pattern is simple
and clean solution to implement your business rules. This pattern
is mixture between composite and factory Gof patterns introduced by
Eric Evans & Martin Fowler.
In this blog, we will not explain how this pattern works, but you can
refer to wikipedia and Xebia
for more details. But, we try here to translate specification pattern
using the Scala language.
Implementation :
package me.jtunisie.specifications
trait TSpecification [T]{
def ->(candidate :T ):Boolean
def ||( specification : TSpecification[T]):TSpecification[T]
def &&(specification :TSpecification[T] ):TSpecification[T]
def unary_!():TSpecification[T]
//Addition
def | ( specification : TSpecification[T]):TSpecification[T]
def & (specification :TSpecification[T] ):TSpecification[T]
}
The last two methods has been added compared to the specification. Sometimes, we need to do full check as A || B is not always equal to B || A. {Take as example true || (1/0 == 0 ). "We have encountered this problem when we validate an old embedded compiler written in C and in some platform like Solaris ( A || B ) starts by checking B and then A. }
abstract class ASpecification[T] extends TSpecification [T]{
def ->(candidate :T ):Boolean
def ||( s : TSpecification[T]):TSpecification[T] = OrSpecification(this,s)
def &&(s :TSpecification[T] ):TSpecification[T] = AndSpecification(this,s)
def unary_!():TSpecification[T] = NotSpecification(this)
// Recurssive Add-0ns
def | ( s : TSpecification[T]):TSpecification[T] = ROrSpecification(this,s)
def &(s :TSpecification[T] ):TSpecification[T] = RAndSpecification(this,s)
}
To implement recursive and not recursive behavior, we have used /: and
\: .
According to documentation :
(z /: List(a, b, c)) (op) equal to op(op(op(z, a), b), c)
and
(List(a, b, c) :\ z) (op) equal to op(a, op(b, op(c, z)))
case class AndSpecification[T]( s: TSpecification[T]* ) extends ASpecification[T]{
override def -> (candidate :T ):Boolean= (true /: s) (_ -> candidate && _ -> candidate)
}
case class OrSpecification[T]( s: TSpecification[T]* ) extends ASpecification[T]{
override def ->(candidate :T ):Boolean= (false /: s) (_ -> candidate || _ -> candidate)
}
case class NotSpecification[T]( s: TSpecification[T] ) extends ASpecification[T]{
override def ->(candidate :T )= ! (s -> candidate )
}
case class RAndSpecification[T]( s: TSpecification[T]* ) extends ASpecification[T]{
override def ->(candidate :T ):Boolean= (s :\ true) (_ -> candidate && _ -> candidate)
}
case class ROrSpecification[T]( s: TSpecification[T]* ) extends ASpecification[T]{
override def ->(candidate :T ):Boolean= (s :\ false) (_ -> candidate || _ -> candidate)
}
This code will not compile. In fact, Boolean class doesn't have ->
method (IsSatisfiedBy). By Scala, we can use open class tool ( adding
this method to Boolean class).
Here is the implicit implementation :
package me.jtunisie
package object specifications {
class MyBool(b:Boolean){
def ->(candidate :Any ):Boolean = b
}
implicit def toMyBool(b:Boolean)= new MyBool(b)
}
source code is under : http://github.com/ouertani/Rules
Mode of use ( with precedence checked ):
Object AlwaysOk extends ASpecification[Boolean] { override def ->
(b: Boolean)= true
}
object AlwaysKo extends ASpecification[Boolean] { override def ->
(b: Boolean)= false
}
object AlwaysOk extends ASpecification[Boolean] {
override def -> (b: Boolean)= true
}
object AlwaysKo extends ASpecification[Boolean] {
override def -> (b: Boolean)= false
}
false mustBe ((AlwaysOk || AlwaysKo) && AlwaysKo)-> (true)
true mustBe (AlwaysOk || (AlwaysKo && AlwaysKo))-> (true)
true mustBe ( AlwaysOk || AlwaysKo && AlwaysKo)-> (true)
Opinions expressed by DZone contributors are their own.
Comments