Ouertani Slim was born in Tunisia in 1981. Now he is a software engineer since 2004 and he is Java 6 and Spring certified with 100% and 94% respectively. He is very interested in Java technology, Scala and open source projects. He believes that programming is one of the hardest jobs and most beautiful in the world. Slim has posted 32 posts at DZone. You can read more from them at their website. View Full User Profile

Specification Pattern by Scala

11.03.2010
| 6180 views |
  • submit to reddit

 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 :
UML diag

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)

 

5
Your rating: None Average: 5 (2 votes)
Published at DZone with permission of its author, Slim Ouertani.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Fabio Strozzi replied on Wed, 2010/11/03 - 3:54pm

Hi Slim, I like this approach.

Thanks for sharing.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.