Designing with
Groovy Traits
Naresha K
Chief Technologist
Channel Bridge Software Labs
naresha.k@gmail.com
@naresha_k
About Me
OOP
Object Oriented Maturity Model
0
1
2
I made up the term
‘object-oriented',
and I can tell you
I didn't have that in mind
Alan Kay
iskov Substitution Principle
"Favour 'object composition'
over
'class inheritance'."
A case for
Traits
The Problem
Bird charlie = new Bird()
charlie.fly()
Butterfly aButterFly = new Butterfly()
aButterFly.fly()
Recall Interfaces
interface Flyable {
def fly()
}
class Bird implements Flyable {
def fly() {
println "Flying..."
}
}
class Butterfly implements Flyable {
def fly() {
println "Flying..."
}
}
The Smell
class Bird implements Flyable {
def fly() {
println "Flying..."
}
}
class Butterfly implements Flyable {
def fly() {
println "Flying..."
}
}
DRY
class FlyableImpl implements Flyable {
def fly() {
println 'Flying...'
}
}
class Bird implements Flyable {
def fly() {
new FlyableImpl().fly()
}
}
Making it Groovier
class DefaultFlyable implements Flyable {
def fly() {
println 'Flying...'
}
}
class Bird {
@Delegate
Flyable flyable = new DefaultFlyable()
}
Summarizing
interface Flyable {
def fly()
}
class DefaultFlyable implements Flyable {
def fly() {
println 'Flying...'
}
}
class Bird {
@Delegate
Flyable flyable = new DefaultFlyable()
}
Introducing Trait
trait Flyable {
def fly() {
println "Flying.."
}
}
class Bird implements Flyable {}
Multiple Capabilities
trait CanSing {
def sing() {
println "Singing"
}
}
trait CanDance {
def dance() {
println "Dancing"
}
}
class Person implements CanSing, CanDance {}
Person reema = new Person()
reema.sing()
reema.dance()
The Mechanics
Overriding Methods from a Trait
trait Speaker {
def speak() {
println "speaking"
}
}
class Gr8ConfSpeaker implements Speaker {
def speak() {
println "Groovy is Groovy!"
}
}
new Gr8ConfSpeaker().speak()
Traits can implement interfaces
interface Programmer {
def code()
}
trait GroovyProgrammer implements Programmer {
def code() {
println 'coding Groovy'
}
}
class Engineer implements GroovyProgrammer {}
new Engineer().code()
Traits can declare abstract methods
trait Programmer {
abstract String getLanguage()
def code() {
println "Coding ${getLanguage()}"
}
}
class GroovyProgrammer implements Programmer {
String getLanguage() { "Groovy"}
}
new GroovyProgrammer().code()
Traits can have a state
trait Programmer {
String language
def code() {
println "Coding ${language}"
}
}
class GroovyProgrammer implements Programmer {}
new GroovyProgrammer(language: 'Groovy').code()
A trait can extend another trait
trait JavaProgrammer {
def codeObjectOriented() {
println 'Coding OOP'
}
}
trait GroovyProgrammer extends JavaProgrammer {
def codeFunctional() {
println 'Coding FP'
}
}
class Engineer implements GroovyProgrammer {}
Engineer raj = new Engineer()
raj.codeFunctional()
raj.codeObjectOriented()
A trait can extend from multiple traits
trait Reader {
def read() { println 'Reading'}
}
trait Evaluator {
def eval() { println 'Evaluating'}
}
trait Printer {
def printer() { println 'Printing'}
}
trait Repl implements Reader, Evaluator, Printer {
}
The diamond problem
trait GroovyProgrammer {
def learn() { println 'Learning Traits'}
}
trait JavaProgrammer {
def learn() { println 'Busy coding'}
}
class Dev implements JavaProgrammer,
GroovyProgrammer {}
new Dev().learn()
Finer control on the diamond problem
class Dev implements JavaProgrammer,
GroovyProgrammer {
def learn() {
JavaProgrammer.super.learn()
}
}
Applying traits at run time
trait Flyable{
def fly(){
println "Flying.."
}
}
class Person {}
new Person().fly()
Applying traits at run time
def boardAPlane(Person person) {
person.withTraits Flyable
}
def passenger = boardAPlane(new Person())
passenger.fly()
More examples
Composing Behaviours
trait UserContextAware {
UserContext getUserContext(){
// implementation
}
}
class ProductApi implements UserContextAware {}
class PriceApi implements UserContextAware {}
common fields
trait Auditable {
String createdBy
String modifiedBy
Date dateCreated
Date lastUpdated
}
class Price implements Auditable {
String productCode
BigDecimal mrp
BigDecimal sellingPrice
}
common fields - a trait approach
Price groovyInActionToday = new Price(
productCode: '9789351198260',
mrp: 899,
sellingPrice: 751,
createdBy: 'admin',
modifiedBy: 'rk'
)
println groovyInActionToday.createdBy
println groovyInActionToday.modifiedBy
Chaining
interface Manager {
def approve(BigDecimal amount)
}
Chaining
trait JuniorManager implements Manager {
def approve(BigDecimal amount){
if(amount < 10000G){ println "Approved by JM” }
else{
println "Sending to SM"
super.approve()
}
}
}
trait SeniorManager implements Manager {
def approve(BigDecimal amount){
println "Approved by SM"
}
}
Chaining
class FinanceDepartment implements SeniorManager,
JuniorManager {}
FinanceDepartment finance = new FinanceDepartment()
finance.approve(3000)
finance.approve(30000)
Groovy Coding!

Designing with Groovy Traits - Gr8Conf India

  • 1.
    Designing with Groovy Traits NareshaK Chief Technologist Channel Bridge Software Labs naresha.k@gmail.com @naresha_k
  • 2.
  • 3.
  • 4.
  • 8.
    I made upthe term ‘object-oriented', and I can tell you I didn't have that in mind Alan Kay
  • 9.
  • 10.
  • 12.
  • 13.
    The Problem Bird charlie= new Bird() charlie.fly() Butterfly aButterFly = new Butterfly() aButterFly.fly()
  • 14.
  • 15.
    class Bird implementsFlyable { def fly() { println "Flying..." } } class Butterfly implements Flyable { def fly() { println "Flying..." } }
  • 16.
    The Smell class Birdimplements Flyable { def fly() { println "Flying..." } } class Butterfly implements Flyable { def fly() { println "Flying..." } }
  • 17.
    DRY class FlyableImpl implementsFlyable { def fly() { println 'Flying...' } } class Bird implements Flyable { def fly() { new FlyableImpl().fly() } }
  • 18.
    Making it Groovier classDefaultFlyable implements Flyable { def fly() { println 'Flying...' } } class Bird { @Delegate Flyable flyable = new DefaultFlyable() }
  • 19.
    Summarizing interface Flyable { deffly() } class DefaultFlyable implements Flyable { def fly() { println 'Flying...' } } class Bird { @Delegate Flyable flyable = new DefaultFlyable() }
  • 20.
    Introducing Trait trait Flyable{ def fly() { println "Flying.." } } class Bird implements Flyable {}
  • 21.
    Multiple Capabilities trait CanSing{ def sing() { println "Singing" } } trait CanDance { def dance() { println "Dancing" } } class Person implements CanSing, CanDance {} Person reema = new Person() reema.sing() reema.dance()
  • 22.
  • 23.
    Overriding Methods froma Trait trait Speaker { def speak() { println "speaking" } } class Gr8ConfSpeaker implements Speaker { def speak() { println "Groovy is Groovy!" } } new Gr8ConfSpeaker().speak()
  • 24.
    Traits can implementinterfaces interface Programmer { def code() } trait GroovyProgrammer implements Programmer { def code() { println 'coding Groovy' } } class Engineer implements GroovyProgrammer {} new Engineer().code()
  • 25.
    Traits can declareabstract methods trait Programmer { abstract String getLanguage() def code() { println "Coding ${getLanguage()}" } } class GroovyProgrammer implements Programmer { String getLanguage() { "Groovy"} } new GroovyProgrammer().code()
  • 26.
    Traits can havea state trait Programmer { String language def code() { println "Coding ${language}" } } class GroovyProgrammer implements Programmer {} new GroovyProgrammer(language: 'Groovy').code()
  • 27.
    A trait canextend another trait trait JavaProgrammer { def codeObjectOriented() { println 'Coding OOP' } } trait GroovyProgrammer extends JavaProgrammer { def codeFunctional() { println 'Coding FP' } } class Engineer implements GroovyProgrammer {} Engineer raj = new Engineer() raj.codeFunctional() raj.codeObjectOriented()
  • 28.
    A trait canextend from multiple traits trait Reader { def read() { println 'Reading'} } trait Evaluator { def eval() { println 'Evaluating'} } trait Printer { def printer() { println 'Printing'} } trait Repl implements Reader, Evaluator, Printer { }
  • 29.
    The diamond problem traitGroovyProgrammer { def learn() { println 'Learning Traits'} } trait JavaProgrammer { def learn() { println 'Busy coding'} } class Dev implements JavaProgrammer, GroovyProgrammer {} new Dev().learn()
  • 30.
    Finer control onthe diamond problem class Dev implements JavaProgrammer, GroovyProgrammer { def learn() { JavaProgrammer.super.learn() } }
  • 31.
    Applying traits atrun time trait Flyable{ def fly(){ println "Flying.." } } class Person {} new Person().fly()
  • 32.
    Applying traits atrun time def boardAPlane(Person person) { person.withTraits Flyable } def passenger = boardAPlane(new Person()) passenger.fly()
  • 33.
  • 34.
    Composing Behaviours trait UserContextAware{ UserContext getUserContext(){ // implementation } } class ProductApi implements UserContextAware {} class PriceApi implements UserContextAware {}
  • 35.
    common fields trait Auditable{ String createdBy String modifiedBy Date dateCreated Date lastUpdated } class Price implements Auditable { String productCode BigDecimal mrp BigDecimal sellingPrice }
  • 36.
    common fields -a trait approach Price groovyInActionToday = new Price( productCode: '9789351198260', mrp: 899, sellingPrice: 751, createdBy: 'admin', modifiedBy: 'rk' ) println groovyInActionToday.createdBy println groovyInActionToday.modifiedBy
  • 37.
    Chaining interface Manager { defapprove(BigDecimal amount) }
  • 38.
    Chaining trait JuniorManager implementsManager { def approve(BigDecimal amount){ if(amount < 10000G){ println "Approved by JM” } else{ println "Sending to SM" super.approve() } } } trait SeniorManager implements Manager { def approve(BigDecimal amount){ println "Approved by SM" } }
  • 39.
    Chaining class FinanceDepartment implementsSeniorManager, JuniorManager {} FinanceDepartment finance = new FinanceDepartment() finance.approve(3000) finance.approve(30000)
  • 40.