SlideShare a Scribd company logo
Developer Practices for
      Dynamic Languages
     “Unlearning Java/C#”
Dr Paul King, Director
ASERT, Australia
paulk@asert.com.au
Topics
                    Introduction
                    • Design patterns
                    • Refactoring
                    • Polyglot programming
                    • SOLID principles
© ASERT 2006-2010




                    • Other topics
                    • More Info




                                              ESDC 2010 - 2
Introduction …
                    • Developer practices
                      – Well understood and documented for
                        traditional languages like Java, C++ and C#
                      – But dynamic languages like Groovy, Ruby,
                        Python, Boo, JavaScript and others,
                        change the ground rules
© ASERT 2006-2010




                      – Many of the rules and patterns we have
                        been taught must be adapted or adjusted;
                        some no longer apply at all
...Introduction...
                    • What does Immutability mean?
                      – When even constants can be changed
                    • What does encapsulation mean?
                      – When I can peek at internal state or
                        when using languages without state
                    • How can I devise tests
© ASERT 2006-2010




                      at development time?
                      – When my system can change in
                        unknown ways at runtime
                    • How can IDEs help me?
                      – If I no longer spoon feed static-type information to my
                        IDE, what level of support can it give me in terms of
                        code completion and error checking
… Introduction
                    • Traditional developer practice guidelines
                      – Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides
                        (1995). Design Patterns: Elements of Reusable Object-
                        Oriented Software. Addison-Wesley.
                      – Martin Fowler (1999). Refactoring: Improving the Design of
                        Existing Code. Addison-Wesley.
                      – Joshua Bloch (2001). Effective Java Programming
© ASERT 2006-2010




                        Language Guide. Prentice Hall.
                      – Robert C Martin (2002), Agile Software Development,
                        Principles, Patterns, and Practices. Prentice Hall.
                      – Robert C Martin (2006), Agile Principles, Patterns, and
                        Practices in C#. Prentice Hall.
                    • In the dynamic language world, are the
                      guidelines in these books FACT or MYTH?
                    • But first let’s look at what we mean by
                      dynamic languages and dynamic typing
What do I mean by Dynamic Language?
                    • I prefer a flexible
                      definition
                    • One or more of:
                      – Dynamic typing
                         • Greater polymorphism
                      – Metaprogramming
© ASERT 2006-2010




                         • Allow language itself to be dynamically changed
                         • Allow hooks into object lifecycle and method calls
                         • Open classes/monkey patching
                      – Work with code as easily as data
                         • Closures
                         • Higher-order programming
                      – Escape hatches
                         • Hooks for polyglot programming
… Static vs Dynamic Typing …
                    • MYTH or TRUTH?
                     Static typing is just spoon feeding the
                     compiler/IDE. It represents the old-school
                     way of thinking and requires extra work
                     while providing no real value.
© ASERT 2006-2010




                                Static VS Dynamic
Static vs Dynamic Typing …
                    • Static: the type of each variable
                      (or expression) must be known
                      at compile time
© ASERT 2006-2010




                     dynamic advocates: like programming
                     wearing a straight-jacket?
                     Unnecessary complexity
…Static vs Dynamic Typing …
                    • Static Typing Pros
                      – Errors are often detected earlier and with better error
                        messages
                      – Code can sometimes be clearer – you don’t need to infer
                        the types to understand the code – especially when
                        revisiting the code later
© ASERT 2006-2010




                      – Safer because certain kinds of injection hacks don’t apply
                      – Code can be more declarative
                      – Better IDE support: refactoring, editing and other forms
                        of source processing support is often possible
                      – Better optimisations are often possible
                      – Often easier to understand a system from the outside
                        (“self-documenting” statically-typed APIs and interfaces)
                      – With generics support you can start to nail down even
                        complex cases
…Static vs Dynamic Typing …
                    • Dynamic: type information is known only
                     at runtime
© ASERT 2006-2010




                    static advocates: like tightrope walking
                    with no net?
… Static vs Dynamic Typing …
                    • Dynamic Typing Pros
                      – Speed development through duck-typing
                        and less boiler-plate code
                      – Clearer more concise code is easier to
                        read and maintain
                      – Allow more expressiveness through DSLs
© ASERT 2006-2010




                      – You should have comprehensive tests anyway, why not
                        cover off types as part of those tests
                      – Enforced healthy practices:
                         • Static language developers may get a false sense of
                           security and not design/test for runtime issues
                         • Less likely to neglect good documentation and/or good
                           coding conventions on the grounds that your static
                           types make everything “inherently” clear
…Static vs Dynamic Typing …
                    • Strong vs weak typing
                     – Strong: List<Integer> myList
                     – Weak: Object myList
                    • Type safety
                     – How is this provided if at all?
© ASERT 2006-2010




                    • Type inference
                     – Is this supported?
…Static and Dynamic Typing…
© ASERT 2006-2010




             Source: http://www.slideshare.net/pcalcado/one-or-two-things-you-may-not-know-about-typesystems
Correctness?
© ASERT 2006-2010




             Source: http://www.slideshare.net/pcalcado/one-or-two-things-you-may-not-know-about-typesystems
Typing Approaches…

                    interface Duck {
                       def waddle()
                       def quack()
                    }

                    class DuckImpl implements Duck {
© ASERT 2006-2010




                       def waddle() { println "waddle" }
                       def quack() { println "quack" }
                    }

                    class Goose {
                       def waddle() { println "Goose waddle" }
                       def quack() { println "Goose quack" }
                    }
…Typing Approaches…
                    • Inheritance hierarchies
                      – Very clear intent but use sparingly
                    • Interface-oriented design
                      – Use if it adds clarity & your language supports it
                      – If you do use it, stick to fine-grained interfaces
                    • Dynamic interface-oriented design
© ASERT 2006-2010




                                                                              Source: Rick DeNatale
                      – If your language doesn’t support it natively you               © David Friel

                        can use a guard: is_a?, kind_of?, instanceof
                    • Chicken typing
                      – Use a guard: responds_to?, respondsTo
                    • Duck typing
                      – Use when flexibility is important but have appropriate tests in
                        place; e.g. you don’t want to violate the Liskov Substitution
                        Principal[15] by not considering a refused bequest[13].
                         • AKA roll your own type safety
…Typing Approaches
                    • Implicit vs Explicit interfaces
                       – Inheritance too restrictive?
                       – Duck-typing too flexible?                                                       Menu
                                                                                                         set_sides()

                      Shape             <<interface>>          <<interface>>                         Rectangle
                      draw()            Shape                  RegularPolygon                        draw()
                                        draw()                 set_side()                            set_sides()
© ASERT 2006-2010




                     Rectangle                                                                           Square
                     draw()                                                                              draw()
                     set_sides()    Rectangle           Square            EquilateralTriangle            set_side()
                                    draw()              draw()            draw()
                                    set_sides()         set_side()        set_side()

                      Square                                                                              Pistol
                      draw()                                                                              draw()
                      set_sides()
                                             I tend to use Explicit types
                                              for major boundaries and                      EquilateralTriangle
                                                implicit types internally.                  draw()
                                                                                            set_side()

             Adapted from Interface-Oriented Design [2]
… Static vs Dynamic Typing …
                    • MYTH
                     Removing static typing always leads to
                     more concise and readable code.
© ASERT 2006-2010




                         X incorrect
… Static vs Dynamic Typing …
                    • An example    interface Reversible {
                                        def reverse()
                                         }

                                         class ReversibleString implements Reversible {
                                             def reverse() { /* */ }
                                         }

                                         class ReversibleArray implements Reversible {
                                             def reverse() { /* */ }
                                         }
© ASERT 2006-2010




                                         Reversible[] things = [
                                             new ReversibleString(), new ReversibleArray()
                                         ]

                                         for (i in 0..<things.size()) {
                                             things[i].reverse()
                                         }



                     def things   = ["abc", [1, 2 ,3]]
                     def expected = ["cba", [3, 2, 1]]
                     assert things*.reverse() == expected
… Static vs Dynamic Typing ...
                                             interface Reversible {
                    With dynamically             def reverse()
                    typed languages,         }
                    there is no need to      class ReversibleString implements Reversible {
                    explicitly declare the       def reverse() { /* */ }
                                             }
                    types of variables or
                    the “protocols”          class ReversibleArray implements Reversible {
                                                 def reverse() { /* */ }
                    observed by our          }
© ASERT 2006-2010




                    objects:
                     Less code              Reversible[] things = [
                                                 new ReversibleString(), new ReversibleArray()
                     Less declarative       ]
                     Less IDE support       for (i in 0..<things.size()) {
                     More testing               things[i].reverse()
                     Less Robust?           }



                     def things   = ["abc", [1, 2 ,3]]
                     def expected = ["cba", [3, 2, 1]]
                     assert things*.reverse() == expected
… Static vs Dynamic Typing …
                    • MYTH
                     Dynamic typing means the IDE can’t
                     provide support for completion and early
                     syntax error checks.
© ASERT 2006-2010




                         X incorrect
… Static vs Dynamic Typing ...
                    • Consider Groovy in Intellij




                    • And Eclipse
© ASERT 2006-2010




                       Eclipse example: http://contraptionsforprogramming.blogspot.com/
Typing approaches and IDEs…
                    • Class A has a bit of duplication

                               class A {
                                   def helper
                                   def make() {
                                       helper.invoke('create')
                                   }
                                   def get() {
© ASERT 2006-2010




                                       helper.invoke('read')
                                   }
                                   def change() {
                                       helper.invoke('update')
                                   }
                                   def remove() {
                                       helper.invoke('delete')
                                   }
                               }
… Typing approaches and IDEs …
                    • No problems, we can refactor out the dup
                                class B {
                                    def helper
                                    def make() {
                                        invoke('create')
                                    }
                                    def get() {
© ASERT 2006-2010




                                        invoke('read')
                                    }
                                    def change() {
                                        invoke('update')
                                    }
                                    def remove() {
                                        invoke('delete')
                                    }
                                    private invoke(cmd) {
                                        helper.invoke(cmd)
                                    }
                                }
… Typing approaches and IDEs …
                    • But we can do more using a dynamic
                      language by leveraging metaprogramming
                               class C {
                                   def helper
                                   def commands = [
                                       make:   'create',
© ASERT 2006-2010




                                       get:    'read',
                                       change: 'update',
                                       remove: 'delete'
                                   ]
                                   def invokeMethod(String name, ignoredArgs) {
                                       helper.invoke(commands[name])
                                   }
                               }


                    • Which is a whole lot nicer?
                    • At the expense of IDE completion? …                         ...
… Typing approaches and IDEs …


                    class Dumper {
                        def name
                        def invokeMethod(String methodName, args) {
                            println "$name: called $methodName with $args"
                        }
                    }
© ASERT 2006-2010




                    for (x in [A, B, C]) {
                        def o = x.newInstance()
                        o.helper = new Dumper(name: "$x.name's helper")
                        o.make()
                        o.get()
                        o.change()
                        o.remove()
                    }
… Typing approaches and IDEs
                    • … At the expense of IDE completion?
© ASERT 2006-2010




                            But remember:
                        “clearly express intent”
… Static vs Dynamic Typing …
© ASERT 2006-2010




                    • MYTH
                     Static typing means runtime errors are a
                     thing of the past.


                        X incorrect
Source: http://www.slideshare.net/pcalcado/one-or-two-things-you-may-not-know-about-typesystems (phillip calçado)
… Static vs Dynamic Typing ...
                    • Consider Lift (based on Scala)
                         <lift:surround with="default" at="content">
                            <h2>Welcome to your project!</h2>
                            <p><lift:hellWorld.howdy /></p>
                         </lift:surround>                      Result: No error but
                                                                       empty home page
© ASERT 2006-2010




                         <lift:surrond with="default" at="content">
                           <h2>Welcome to your project!</h2>
                           <p><lift:hellWorld.howdy /></p>
                         </lift:surround>




                    Source: http://zef.me/2371/when-scala-dsls-fail
Static and Dynamic Strong Typing
© ASERT 2006-2010




                    Source: http://www.slideshare.net/pcalcado/one-or-two-things-you-may-not-know-about-typesystems
Static vs Dynamic Typing

                                         Static       Dynamic

                      Syntax bugs
                      Optimisation
© ASERT 2006-2010




                    Arithmetic bugs
                    Logic bugs
                                       approx same   approx same
                    Resource bugs
                    Concurrency bugs
                       Power
                       Flexibility
Static vs Dynamic Typing Verdict
                    • MYTH or TRUTH?

                     Static typing is just spoon feeding the
                     compiler. It represents the old-school way
                     of thinking and requires extra work while
                     providing no real value.
© ASERT 2006-2010




                       ...but not a total lie either...

                       ...dynamic languages certainly assist
                       with removing duplication, clutter and
                       boilerplate code...
An open debate
© ASERT 2006-2010




             Source: http://www.slideshare.net/pcalcado/one-or-two-things-you-may-not-know-about-typesystems
Topics
                    • Introduction
                    Design patterns
                    • Refactoring
                    • Polyglot programming
                    • SOLID principles
© ASERT 2006-2010




                    • Other topics
                    • More Info
Language features instead of Patterns
                    • So called "Design Patterns" are merely
                      hacks to overcome the limitations of your
                      language
                      – You call that a
                        language?
© ASERT 2006-2010




                      – This is a language


                    • "Design Patterns" are really anti-patterns
                      you must sometimes put up with because
                      your language is so archaic!
                    • In my superior language, that would be
                      built-in, simply a library, so easy, ...
Language features instead of Patterns
                    • So called "Design Patterns" are merely
                      hacks to overcome the limitations of your
                      language
                      – You call that a
                        language?
© ASERT 2006-2010




                      – This is a language


                    • "Design Patterns" are really anti-patterns
                      you must sometimes put up with because
                      your language is so archaic!
                    • In my superior language, that would be
                      built-in, simply a library, so easy, ...
Adapter Pattern…
                    class RoundPeg {
                      def radius
                      String toString() { "RoundPeg with radius $radius" }
                    }

                    class RoundHole {
                      def radius
                      def pegFits(peg) { peg.radius <= radius }
                      String toString() { "RoundHole with radius $radius" }
                    }
© ASERT 2006-2010




                    def pretty(hole, peg) {
                      if (hole.pegFits(peg)) println "$peg fits in $hole"
                      else println "$peg does not fit in $hole"
                    }

                    def hole = new RoundHole(radius:4.0)
                    (3..6).each { w -> pretty(hole, new RoundPeg(radius:w)) }


                    RoundPeg with radius   3 fits in RoundHole with radius 4.0
                    RoundPeg with radius   4 fits in RoundHole with radius 4.0
                    RoundPeg with radius   5 does not fit in RoundHole with radius 4.0
                    RoundPeg with radius   6 does not fit in RoundHole with radius 4.0
…Adapter Pattern…
                    class SquarePeg {
                      def width
                      String toString() { "SquarePeg with width $width" }
                    }

                    class SquarePegAdapter {
                      def peg
                      def getRadius() { Math.sqrt(((peg.width/2) ** 2)*2) }
                      String toString() {
                        "SquarePegAdapter with width $peg.width (and notional radius $radius)"
© ASERT 2006-2010




                      }
                    }

                    def hole = new RoundHole(radius:4.0)

                    (4..7).each { w ->
                        pretty(hole, new SquarePegAdapter(peg: new SquarePeg(width: w))) }

                    SquarePegAdapter with width 4 (and notional radius 2.8284271247461903)
                    fits in RoundHole with radius 4.0
                    SquarePegAdapter with width 5 (and notional radius 3.5355339059327378)
                    fits in RoundHole with radius 4.0
                    SquarePegAdapter with width 6 (and notional radius 4.242640687119285)
                    does not fit in RoundHole with radius 4.0
                    SquarePegAdapter with width 7 (and notional radius 4.949747468305833)
                    does not fit in RoundHole with radius 4.0
…Adapter Pattern

                    SquarePeg.metaClass.getRadius =
                        { Math.sqrt(((delegate.width/2)**2)*2) }

                    (4..7).each { w -> pretty(hole, new SquarePeg(width:w)) }
© ASERT 2006-2010




                                                                          Adapter Pattern
                                                                          Do I create a whole new class
                                                                          or just add the method I need
                                                                          on the fly?
                                                                          Consider the Pros and Cons!

                      SquarePeg with width 4 fits in RoundHole with radius 4.0
                      SquarePeg with width 5 fits in RoundHole with radius 4.0
                      SquarePeg with width 6 does not fit in RoundHole with radius 4.0
                      SquarePeg with width 7 does not fit in RoundHole with radius 4.0


                     Further reading: James Lyndsay, Agile is Groovy, Testing is Square
Adapter Pattern Verdict
                    • Dynamic languages can make it easier to
                      apply the adapter pattern to the extent that
                      its use may not even be apparent:
                      –   Express intent more clearly and improves readability
                      –   Aids refactoring
                      –
© ASERT 2006-2010




                          Can help with test creation
                      –   Avoids class proliferation
                           • At the expense of class pollution?
                      – But you still need testing
Immutable Pattern...
                    • Java Immutable Class                                                             boilerplate
                      – As per Joshua Bloch                                   // ...
                                                                              @Override
                        Effective Java                                        public boolean equals(Object obj) {
                                                                                  if (this == obj)
                      public final class Punter {                                     return true;
                          private final String first;                             if (obj == null)
                          private final String last;                                  return false;
                                                                                  if (getClass() != obj.getClass())
                         public String getFirst() {                                   return false;
                             return first;                                        Punter other = (Punter) obj;
                         }                                                        if (first == null) {
© ASERT 2006-2010




                                                                                      if (other.first != null)
                         public String getLast() {                                        return false;
                             return last;                                         } else if (!first.equals(other.first))
                         }                                                            return false;
                                                                                  if (last == null) {
                         @Override                                                    if (other.last != null)
                         public int hashCode() {                                          return false;
                             final int prime = 31;                                } else if (!last.equals(other.last))
                             int result = 1;                                          return false;
                             result = prime * result + ((first == null)           return true;
                                 ? 0 : first.hashCode());                     }
                             result = prime * result + ((last == null)
                                 ? 0 : last.hashCode());                      @Override
                             return result;                                   public String toString() {
                         }                                                        return "Punter(first:" + first
                                                                                      + ", last:" + last + ")";
                         public Punter(String first, String last) {           }
                             this.first = first;
                             this.last = last;                            }
                         }
                         // ...
                                                                                                               QCON 2010 - 41
...Immutable Pattern



                    @Immutable class Punter {
                        String first, last
© ASERT 2006-2010




                    }




                                                QCON 2010 - 42
Visitor Pattern
                    abstract class Shape {}

                    class Rectangle extends Shape {
                      def x, y, width, height                                 Visitor Pattern                       abstract class Shape {
                        Rectangle(x, y, width, height) {
                                                                                                                        def accept(Closure yield) { yield(this) }
                        }                                                    without closures
                          this.x = x; this.y = y; this.width = width; this.height = height

                                                                                                                    }
                        def union(rect) {
                          if (!rect) return this
                          def minx = [rect.x, x].min()
                                                                              with closures                         class Rectangle extends Shape {
                          def maxx = [rect.x + width, x + width].max()
                          def miny = [rect.y, y].min()
                          def maxy = [rect.y + height, y + height].max()                                                def x, y, w, h
                          new Rectangle(minx, miny, maxx - minx, maxy - miny)
                        }                                                                                               def bounds() { this }
                        def accept(visitor) {                                                                           def union(rect) {
                          visitor.visit_rectangle(this)
                        }                                                                                                   if (!rect) return this
                    }
                                                                                                                            def minx = [rect.x, x].min()
                    class Line extends Shape {
                      def x1, y1, x2, y2                                                                                    def maxx = [rect.x + w, x + w].max()
                        Line(x1, y1, x2, y2) {                                                                              def miny = [rect.y, y].min()
                          this.x1 = x1; this.y1 = y1; this.x2 = x2; this.y2 = y2
                        }                                                                                                   def maxy = [rect.y + h, y + h].max()
                        def accept(visitor) {                                                                               new Rectangle(x:minx, y:miny, w:maxx - minx, h:maxy - miny)
                          visitor.visit_line(this)
                        }                                                                                               }
© ASERT 2006-2010




                    }
                                                                                                                    }
                    class Group extends Shape {
                      def shapes = []

                        def add(shape) { shapes += shape }                                                          class Line extends Shape {
                        def remove(shape) { shapes -= shape }                                                           def x1, y1, x2, y2
                        def accept(visitor) {                                                                           def bounds() {
                          visitor.visit_group(this)
                        }                                                                                                   new Rectangle(x:x1, y:y1, w:x2-y1, h:x2-y2)
                    }
                                                                                                                        }
                    class BoundingRectangleVisitor {
                      def bounds                                                                                    }
                        def visit_rectangle(rectangle) {
                          if (bounds)
                            bounds = bounds.union(rectangle)                                                        class Group {
                          else
                            bounds = rectangle                                                                          def shapes = []
                        }
                                                                                                                        def leftShift(shape) { shapes += shape }
                        def visit_line(line) {
                          def line_bounds = new Rectangle(line.x1, line.y1, line.x2 - line.y1, line.x2 - line.y2)       def accept(Closure yield) { shapes.each{it.accept(yield)} }
                          if (bounds)
                            bounds = bounds.union(line_bounds)                                                      }
                          else
                            bounds = line_bounds
                        }
                                                                                                                    def group = new Group()
                        def visit_group(group) {
                          group.shapes.each {shape -> shape.accept(this) }                                          group << new Rectangle(x:100, y:40, w:10, h:5)
                        }
                    }                                                                                               group << new Rectangle(x:100, y:70, w:10, h:5)
                    def group = new Group()                                                                         group << new Line(x1:90, y1:30, x2:60, y2:5)
                    group.add(new Rectangle(100, 40, 10, 5))
                    group.add(new Rectangle(100, 70, 10, 5))                                                        def bounds
                    group.add(new Line(90, 30, 60, 5))
                    def visitor = new BoundingRectangleVisitor()                                                    group.accept{ bounds = it.bounds().union(bounds) }
                    group.accept(visitor)
                    bounding_box = visitor.bounds                                                                   println bounds.dump()
                    println bounding_box.dump()


                                                                                                                         See also Ruby Visitor
Visitor Pattern Verdict
                    • Dynamic languages can make it easier to
                      apply the visitor pattern to the extent that
                      its use may not even be apparent:
                      –   Express intent more clearly and improves readability
                      –   Aids refactoring
                      –
© ASERT 2006-2010




                          Avoids class proliferation
                      –   But you still need testing
Strategy Pattern
© ASERT 2006-2010




   Source: http://nealford.com/
Language features instead of Patterns…
                    interface Calc {
                      def execute(n, m)                           Strategy Pattern
                    }
                                                                   with interfaces
                    class CalcByMult implements Calc {             with closures
                      def execute(n, m) { n * m }
                    }
                                                              def multiplicationStrategies = [
                    class CalcByManyAdds implements Calc {
                      def execute(n, m) {
                                                                { n, m -> n * m },
                        def result = 0                          { n, m ->
                        n.times {                                 def total = 0; n.times{ total += m }; total },
                          result += m                           { n, m -> ([m] * n).sum() }
                        }                                     ]
© ASERT 2006-2010




                        return result
                      }
                    }
                                                              def sampleData = [
                                                                [3, 4, 12],
                    def sampleData = [                          [5, -5, -25]
                        [3, 4, 12],                           ]
                        [5, -5, -25]
                    ]                                         sampleData.each{ data ->
                    Calc[] multiplicationStrategies = [
                                                                multiplicationStrategies.each{ calc ->
                        new CalcByMult(),                         assert data[2] == calc(data[0], data[1])
                        new CalcByManyAdds()                    }
                    ]                                         }

                    sampleData.each {data ->
                      multiplicationStrategies.each {calc ->
                        assert data[2] == calc.execute(data[0], data[1])
                      }
                    }
Strategy Pattern Verdict
                    • Dynamic languages can make it easier to
                      apply the strategy pattern to the extent
                      that its use may not even be apparent:
                      – Express intent more clearly and improves readability
                      – Closures open up whole new possibilities for solving
                        problems
© ASERT 2006-2010




                      – Aids refactoring
                      – Can help with test creation
                      – Avoids class proliferation
                      – But you still need testing
Builder Pattern: MarkupBuilder…
                     • Builder pattern from the GoF at the syntax-level
                     • Represents easily any nested tree-structured data


                    import groovy.xml.*               • Create new builder
                    def b = new MarkupBuilder()
                    b.html {                          • Call pretended methods
© ASERT 2006-2010




                      head { title 'Hello' }            (html, head, ...)
                      body {                          • Arguments are Closures
                        ul {
                          for (count in 1..5) {       • Builder code looks very
                             li "world $count"          declarative but is ordinary
                    } } } }                             Groovy program code and
                                                        can contain any kind of
                        NodeBuilder, DomBuilder,        logic
                        SwingBuilder, AntBuilder, …
...Builder Pattern: MarkupBuilder


                                                  <html>
                                                    <head>
                    import groovy.xml.*               <title>Hello</title>
                    def b = new MarkupBuilder()     </head>
                    b.html {                        <body>
© ASERT 2006-2010




                      head { title 'Hello' }          <ul>
                      body {                             <li>world 1</li>
                        ul {                             <li>world 2</li>
                          for (count in 1..5) {          <li>world 3</li>
                             li "world $count"           <li>world 4</li>
                    } } } }                              <li>world 5</li>
                                                      </ul>
                                                    </body>
                                                  </html>
Builder Pattern: SwingBuilder
                    import java.awt.FlowLayout
                    builder = new groovy.swing.SwingBuilder()
                    langs = ["Groovy", "Ruby", "Python", "Pnuts"]

                    gui = builder.frame(size: [290, 100],
                              title: 'Swinging with Groovy!’) {
                        panel(layout: new FlowLayout()) {
                            panel(layout: new FlowLayout()) {
                                for (lang in langs) {
© ASERT 2006-2010




                                    checkBox(text: lang)
                                }
                            }
                            button(text: 'Groovy Button', actionPerformed: {
                                builder.optionPane(message: 'Indubitably Groovy!').
                                    createDialog(null, 'Zen Message').show()
                            })
                            button(text: 'Groovy Quit',
                                    actionPerformed: {System.exit(0)})
                        }
                    }
                    gui.show()
                     Source: http://www.ibm.com/developerworks/java/library/j-pg04125/
Builder Pattern: JavaFX Script

                    Frame {
                       title: "Hello World F3"
                       width: 200
                       content: Label {
                          text: "Hello World"
© ASERT 2006-2010




                       }
                       visible: true
                    }
Builder Pattern: Cheri::Swing




                    # requires JRuby
                    require 'rubygems'
© ASERT 2006-2010




                    require 'cheri/swing'
                    include Cheri::Swing

                    @frame = swing.frame('Hello') {
                      size 500,200
                      flow_layout
                      on_window_closing {|event| @frame.dispose}
                      button('Hit me') {
                        on_click { puts 'button clicked' }
                      }
                    }
                    @frame.show
Builder Pattern: AntBuilder
                    def ant = new AntBuilder()

                    ant.echo("hello") // let's just call one task

                    // create a block of Ant using the builder pattern
                    ant.sequential {
                        myDir = "target/test/"
                        mkdir(dir: myDir)
© ASERT 2006-2010




                        copy(todir: myDir) {
                            fileset(dir: "src/test") {
                                include(name: "**/*.groovy")
                            }
                        }
                        echo("done")
                    }

                    // now let's do some normal Groovy again
                    file = new File("target/test/AntTest.groovy")
                    assert file.exists()
Builder Pattern Verdict
                    • The builder pattern in combination with
                      dynamic languages helps me:
                      –   Express intent more clearly and improves readability
                      –   Aids refactoring
                      –   Can help with test creation
                      –   Tests are still important
© ASERT 2006-2010
Delegation Pattern ...
                    • Traditional approach to creating a class that is an
                      extension of another class is to use inheritance
                       – Clearest intent & simplest, clearest code for simple cases

                            class Person {
                               private name, age
                               Person(name, age) {
                                  this.name = name
                                  this.age = age
© ASERT 2006-2010




                               }
                               def haveBirthday() { age++ }
                               String toString() { "$name is $age years old" }
                            }

                            class StaffMemberUsingInheritance extends Person {
                               private salary
                               StaffMemberUsingInheritance(name, age, salary) {
                                  super(name, age)
                                  this.salary = salary
                               }
                               String toString() {
                                  super.toString() + " and has a salary of $salary"
                               }
                            }
… Delegation Pattern ...
                    • Most common alternative is to use delegation
                       – Intention less clear (can be helped with interfaces)
                       – Overcomes multiple inheritance issues & inheritance abuse


                        class StaffMemberUsingDelegation {
                           private delegate
                           private salary
                           StaffMemberUsingDelegation(name, age, salary) {
© ASERT 2006-2010




                              delegate = new Person(name, age)
                              this.salary = salary
                           }
                           def haveBirthday() {
                              delegate.haveBirthday()
                           }
                           String toString() {
                              delegate.toString() + " and has a salary of $salary"
                           }
                        }
… Delegation Pattern …
                    • Downside of delegation is maintenance issues
                      – Refactoring overhead if we change the base class
                      – Meta-programming allows us to achieve inheritance
                        like behavior by intercepting missing method calls
                        (invokeMethod or method_missing)
                      – You could take this further with Groovy using named
© ASERT 2006-2010




                        parameters rather than the traditional positional
                        parameters shown here (future versions of Ruby may
                        have this too)
… Delegation Pattern …
                    class StaffMemberUsingMOP {
                       private delegate
                       private salary
                       StaffMemberUsingMOP(name, age, salary) {
                          delegate = new Person(name, age)
                          this.salary = salary
                       }
                       def invokeMethod(String name, args) {
                          delegate.invokeMethod name, args
                       }
© ASERT 2006-2010




                       String toString() {
                          delegate.toString() + " and has a salary of $salary"
                       }
                    }

                    def p1 = new StaffMemberUsingInheritance("Tom", 20, 1000)
                    def p2 = new StaffMemberUsingDelegation("Dick", 25, 1100)
                    def p3 = new StaffMemberUsingMOP("Harry", 30, 1200)
                    p1.haveBirthday()
                    println p1
                    p2.haveBirthday()                 Tom is 21 years old and has a salary of 1000
                    println p2                        Dick is 26 years old and has a salary of 1100
                    p3.haveBirthday()
                                                      Harry is 31 years old and has a salary of 1200
                    println p3
… Delegation Pattern
                    • Going Further
                      –The example shown (on the previous slide) codes the
                       delegate directly but both Groovy and Ruby let you
                       encapsulate the delegation pattern as a library:
                          • Groovy: Delegator, Injecto; Ruby: forwardable, delegate
                      –But only if I don’t want to add logic as I delegate
                          • E.g. If I wanted to make haveBirthday() increment salary
© ASERT 2006-2010




                      class StaffMemberUsingLibrary {
                          private salary
                          private person
                          StaffMemberUsingLibrary(name, age, salary) {
                              person = new Person(name, age)
                              this.salary = salary
                              def delegator = new Delegator(StaffMemberUsingLibrary, person)
                              delegator.delegate haveBirthday
                          }
                          String toString() {
                              person.toString() + " and has a salary of $salary"
                          }
                      }
Better Design Patterns: Delegate…
                                                              public Date getWhen() {
                    import java.util.Date;
                                                                 return when;
                                                              }
                    public class Event {
                       private String title;
                                                              public void setWhen(Date when) {
                       private String url;
                                                                 this.when = when;
                       private Date when;
                                                              }
                       public String getUrl() {
                                                              public boolean before(Date other) {
                          return url;
                                                                 return when.before(other);
                       }
© ASERT 2006-2010




                                                              }
                       public void setUrl(String url) {
                                                              public void setTime(long time) {
                          this.url = url;
                                                                 when.setTime(time);
                       }
                                                              }
                       public String getTitle() {
                                                              public long getTime() {
                          return title;
                                                                 return when.getTime();
                       }
                                                              }
                       public void setTitle(String title) {
                                                              public boolean after(Date other) {
                          this.title = title;
                                                                 return when.after(other);
                       }
                                                              }
                       // ...
                                                              // ...
                                                                                          QCON 2010 - 60
…Better Design Patterns: Delegate…
                                                              public Date getWhen() {
                    import java.util.Date;
                                                                 return when;
                                               boilerplate    }
                    public class Event {
                       private String title;
                                                              public void setWhen(Date when) {
                       private String url;
                                                                 this.when = when;
                       private Date when;
                                                              }
                       public String getUrl() {
                                                              public boolean before(Date other) {
                          return url;
                                                                 return when.before(other);
                       }
© ASERT 2006-2010




                                                              }
                       public void setUrl(String url) {
                                                              public void setTime(long time) {
                          this.url = url;
                                                                 when.setTime(time);
                       }
                                                              }
                       public String getTitle() {
                                                              public long getTime() {
                          return title;
                                                                 return when.getTime();
                       }
                                                              }
                       public void setTitle(String title) {
                                                              public boolean after(Date other) {
                          this.title = title;
                                                                 return when.after(other);
                       }
                                                              }
                       // ...
                                                              // ...
                                                                                          QCON 2010 - 61
…Better Design Patterns: Delegate

                    class Event {
                        String title, url
                        @Delegate Date when
                    }
© ASERT 2006-2010




                    def gr8conf = new Event(title: "GR8 Conference",
                            url: "http://www.gr8conf.org",
                            when: Date.parse("yyyy/MM/dd", "2009/05/18"))

                    def javaOne = new Event(title: "JavaOne",
                            url: "http://java.sun.com/javaone/",
                            when: Date.parse("yyyy/MM/dd", "2009/06/02"))

                    assert gr8conf.before(javaOne.when)

                                                                        QCON 2010 - 62
Delegation Pattern Verdict
                    • The delegation pattern can be expressed
                      more succinctly with dynamic languages:
                      – Express intent more clearly and improves readability
                      – Aids refactoring
                      – But don’t forget the testing implications
© ASERT 2006-2010
Singleton Pattern…
                    • Pattern Intent              •    Static language discussion
                                                       points
                    – Ensure that only one            – Need exactly one instance of a class
                      instance of a class is            and a well-known controlled access
                      created                           point
                                                        • Allows for lazy creation of instance
                    – Provide a global point of       – More flexible than static class
                      access to the object              variables and methods alone
                                                        • Permits refinement of operations and
                    – Allow multiple instances            representation through subclassing
© ASERT 2006-2010




                      in the future without           – Reduces name space clutter
                      affecting a singleton             • Compared to using static approach
                                                      – Multi-threading implications
                      class's clients
                                                      – Serializable implications
                                                        • need to have readResolve() method to
                                                          avoid spurious copies
                                                      – Garbage collection implications
                                                        • May need "sticky" static self-reference
                                                      – Need to be careful subclassing
                                                        • Parent may already create instance or be
                                                          final or constructor may be hidden
…Singleton Pattern…
                    • The details quickly get messy …
                    public final class Singleton {
                        private static final class SingletonHolder {
                            static final Singleton singleton = new Singleton();
                        }
                        private Singleton() {}
                        public static Singleton getInstance() {
                            return SingletonHolder.singleton;
© ASERT 2006-2010




                        }
                    }

                     public class Singleton implements java.io.Serializable {
                         public static Singleton INSTANCE = new Singleton();
                         protected Singleton() {
                             // Exists only to thwart instantiation.
                         }
                         private Object readResolve() {
                             return INSTANCE;
                         }
                     }
…Singleton Pattern…
                    • State of the art approach in Java?
                      – Use an IoC framework, e.g. Spring or Guice
                    import com.google.inject.*

                    @ImplementedBy(CalculatorImpl)
                    interface Calculator {
                        def add(a, b)
                    }
© ASERT 2006-2010




                    @Singleton
                    class CalculatorImpl implements Calculator {
                        private total = 0
                        def add(a, b) { total++; a + b }
                        def getTotalCalculations() { 'Total Calculations: ' + total }
                        String toString() { 'Calc: ' + hashCode()}
                    }

                    class Client {
                        @Inject Calculator calc
                        // ...
                    }

                    def injector = Guice.createInjector()
…Singleton Pattern…
                    • But it is easy using meta-programming
                      – Old style


                     class Calculator {
                         private total = 0
                         def add(a, b) { total++; a + b }
© ASERT 2006-2010




                         def getTotalCalculations() { 'Total Calculations: ' + total }
                         String toString() { 'Calc: ' + hashCode()}
                     }

                     class CalculatorMetaClass extends MetaClassImpl {
                         private final static INSTANCE = new Calculator()
                         CalculatorMetaClass() { super(Calculator) }
                         def invokeConstructor(Object[] arguments) { return INSTANCE }
                     }

                     def registry = GroovySystem.metaClassRegistry
                     registry.setMetaClass(Calculator, new CalculatorMetaClass())
…Singleton Pattern…
                    • But it is easy using meta-programming
                      class Calculator {
                          def total = 0
                          def add(a, b) { total++; a + b }
                      }

                      def INSTANCE = new Calculator()
© ASERT 2006-2010




                      Calculator.metaClass.constructor = { -> INSTANCE }

                      def c1 = new Calculator()
                      def c2 = new Calculator()

                      assert c1.add(1, 2) == 3
                      assert c2.add(3, 4) == 7

                      assert c1.is(c2)
                      assert [c1, c2].total == [2, 2]
…Singleton Pattern…
                    • Or annotations

                              @Singleton(lazy=true)
                              class X {
                                 def getHello () {
                                   "Hello, World!"
© ASERT 2006-2010




                                 }
                              }

                              println X.instance.hello
…Singleton Pattern…
                    • And again with Ruby
                    class Aardvark                         class Aardvark
                      private_class_method :new              private_class_method :new
                        @@instance = new                     def Aardvark.instance
                        def Aardvark.instance                  @@instance = new if not @@instance
                          @@instance                           @@instance
                      end                                    end
                    end                                    end
© ASERT 2006-2010




                          module ThreadSafeSingleton
                            def self.append_features(clazz)
                              require 'thread'
                              clazz.module_eval {
                                private_class_method :new
                                @instance_mutex = Mutex.new
                                def self.instance
                                  @instance_mutex.synchronize {
                                    @instance = new if not (@instance)
                                    @instance
                                  }
                                end
                              }
                            end
                          end                        Source: http://c2.com/cgi/wiki?RubySingleton
…Singleton Pattern
                      • Or for Python
                            – Classic class version (pre 2.2)
                                   class Borg:
                                           _shared_state = {}
                                           def __init__(self):
                                               self.__dict__ = self._shared_state


                            – Non-classic class version
© ASERT 2006-2010




                                   class Singleton (object):
                                       instance = None
                                       def __new__(cls, *args, **kargs):
                                           if cls.instance is None:
                                               cls.instance = object.__new__(cls, *args, **kargs)
                                           return cls.instance

                                   # Usage
                                   mySingleton1 = Singleton()
                                   mySingleton2 = Singleton()
                                   assert mySingleton1 is mySingleton2

                    Source: [10] and wikipedia
Singleton Pattern Verdict
                    • The singleton pattern can be expressed in
                      better ways with dynamic languages:
                      – Express intent more clearly and improves readability
                      – Aids refactoring
                      – But don’t forgot testing implications
© ASERT 2006-2010
Pattern Summary
                    • Patterns can be replaced by language
                      features and libraries
© ASERT 2006-2010




                    • So patterns aren’t important any more!




                                                               ...
Topics
                    • Introduction
                    • Design patterns
                    Refactoring
                    • Polyglot programming
                    • SOLID principles
© ASERT 2006-2010




                    • Other topics
                    • More Info
Refactoring Refactoring
                    • Out with the Old
                      – Some refactorings no longer make sense
                    • In with the New
                      – There are some new refactorings
                    • Times … they are a changin’
© ASERT 2006-2010




                      – Some refactorings are done differently
Encapsulate Downcast Refactoring
                    • Description
                      – Context: A method returns an object that
                        needs to be downcasted by its callers
                      – Solution: Move the downcast to within the method
                    • Is there a point in a dynamic language?
                      – Maybe but not usually
© ASERT 2006-2010




                              // Before refactoring
                              Object lastReading() {
                                 return readings.lastElement()
                              }


                              // After refactoring
                              Reading lastReading() {
                                 return (Reading) readings.lastElement()
                              }
Introduce Generics Refactoring
                    • Description
                      – Context: Casting is a runtime hack that allows
                        JVM to clean up a mess caused by a compiler
                        that couldn’t infer intent
                      – Solution: Use Generics to reveal intent to compiler

                    • Is there a point in a dynamic language?
© ASERT 2006-2010




                      – Maybe but not usually
                           // Traditional Java style
                           List myIntList = new LinkedList()
                           myIntList.add(new Integer(0))
                           Integer result = (Integer) myIntList.iterator().next()

                           // Java generified style
                           List<Integer> myIntList2 = new LinkedList<Integer>()
                           myIntList2.add(new Integer(0))
                           Integer result2 = myIntList2.iterator().next()

                           // Groovier style
                           def myIntList3 = [0]
                           def result3 = myIntList3.iterator().next()
Enabling a functional style …
                    • Consider the Maximum Segment Sum
                      (MSS) problem
                      – Take a list of integers; the MSS is the maximum of the sums of
                        any number of adjacent integers

                    • Imperative solution:
                            def numbers = [31,-41,59,26,-53,58,97,-93,-23,84]
© ASERT 2006-2010




                            def size = numbers.size()
                            def max = null
                            (0..<size).each { from ->
                              (from..<size).each { to ->
                                def sum = numbers[from..to].sum()
                                if (max == null || sum > max) max = sum
                              }
                            }

                            println "Maximum Segment Sum of $numbers is $max"
… Enabling a functional style …
                    • A first attempt at a more functional style:



                        def numbers = [31,-41,59,26,-53,58,97,-93,-23,84]
© ASERT 2006-2010




                        def size = numbers.size()
                        def max = [0..<size, 0..<size].combinations().collect{
                          numbers[it[0]..it[1]].sum()
                        }.max()

                        println "Maximum Segment Sum of $numbers is $max"
… Enabling a functional style …
                    • An even more functional style
                      – A known solution using functional composition:

                          mss = max º sum* º (flatten º tails* º inits)

                      – Where inits and tails are defined as follows:
© ASERT 2006-2010




                                  letters = ['a', 'b', 'c', 'd']


                      assert letters.inits() == [   assert letters.tails() == [
                        ['a'],                                       ['d'],
                        ['a', 'b'],                             ['c', 'd'],
                        ['a', 'b', 'c'],                   ['b', 'c', 'd'],
                        ['a', 'b', 'c', 'd']          ['a', 'b', 'c', 'd']
                      ]                             ]
… Enabling a functional style
                    • An even more functional style
                                mss = max º sum* º (flatten º tails* º inits)
                      def segs = { it.inits()*.tails().sum() }

                      def solve = { segs(it)*.sum().max() }

                      def numbers = [31,-41,59,26,-53,58,97,-93,-23,84]
© ASERT 2006-2010




                      println "Maximum Segment Sum of $numbers is ${solve numbers}"

                       Notes:
                       – sum() is one-level flatten in Groovy, flatten() is recursive
                       – Metaprogramming allowed us to enhance all Lists
                       List.metaClass {
                         inits{ (0..<delegate.size()).collect{ delegate[0..it] } }
                         tails{ delegate.reverse().inits() }
                       }
                     Source: http://hamletdarcy.blogspot.com/2008/07/groovy-vs-f-showdown-side-by-side.html
Refactoring recipes with a curry base
                    • Static: Replace parameter with method
                      – Refactoring [13]: Chapter 10
                    • Context
                      – An object invokes a method, then passes the result as
                        a parameter for a method. The receiver can also
                        invoke this method.
© ASERT 2006-2010




                    • Solution
                      – Remove the parameter and let the receiver invoke the
                        method.
                    • Dynamic solution
                      – Partial Application: Currying
Replace parameter with method …
                    class Order {
                      private int quantity, itemPrice                           Let's explore the
                      Order(q, p) {quantity = q; itemPrice = p}
                                                                             traditional refactoring
                        double getPrice() {
                          int basePrice = quantity * itemPrice
                          int discountLevel
                          if (quantity > 100) discountLevel = 2
                          else discountLevel = 1
© ASERT 2006-2010




                          double finalPrice = discountedPrice(basePrice, discountLevel)
                          return finalPrice
                        }

                        private double discountedPrice(int basePrice, int discountLevel) {
                          if (discountLevel == 2) return basePrice * 0.8
                          return basePrice * 0.9
                        }
                    }

                    println new Order(120, 5).price // => 480.0
… Replace parameter with method …
                    class Order {
                      private int quantity, itemPrice
                      Order(q, p) {quantity = q; itemPrice = p}

                        double getPrice() {
                          int basePrice = quantity * itemPrice
                          int discountLevel
                          if (quantity > 100) discountLevel = 2
                          else discountLevel = 1
© ASERT 2006-2010




                          double finalPrice = discountedPrice(basePrice, discountLevel)
                          return finalPrice
                        }

                        private double discountedPrice(int basePrice, int discountLevel) {
                          if (discountLevel == 2) return basePrice * 0.8
                          return basePrice * 0.9
                        }
                    }

                    println new Order(120, 5).price // => 480.0
… Replace parameter with method …
                    class Order {
                      private int quantity, itemPrice
                      Order(q, p) {quantity = q; itemPrice = p}

                      double getPrice() {
                        int basePrice = quantity * itemPrice
                        double finalPrice = discountedPrice(basePrice)
                        return finalPrice
                      }
© ASERT 2006-2010




                      private double discountedPrice(int basePrice) {
                        if (getDiscountLevel() == 2) return basePrice * 0.8
                        return basePrice * 0.9
                      }

                      private int getDiscountLevel() {
                        if (quantity > 100) return 2
                        return 1
                      }
                    }
                    println new Order(120, 5).price // => 480.0
… Replace parameter with method …
                    class Order {
                      private int quantity, itemPrice
                      Order(q, p) {quantity = q; itemPrice = p}

                      double getPrice() {
                        int basePrice = quantity * itemPrice
                        double finalPrice = discountedPrice(basePrice)
                        return finalPrice
                      }
© ASERT 2006-2010




                      private double discountedPrice(int basePrice) {
                        if (getDiscountLevel() == 2) return basePrice * 0.8
                        return basePrice * 0.9
                      }

                      private int getDiscountLevel() {
                        if (quantity > 100) return 2
                        return 1
                      }
                    }
                    println new Order(120, 5).price // => 480.0
… Replace parameter with method …
                    class Order {
                      private int quantity, itemPrice
                      Order(q, p) {quantity = q; itemPrice = p}

                      double getPrice() {
                        return discountedPrice(getBasePrice())
                      }

                      private double discountedPrice(int basePrice) {
© ASERT 2006-2010




                        if (getDiscountLevel() == 2) return basePrice * 0.8
                        return basePrice * 0.9
                      }

                      private int getBasePrice() {
                        quantity * itemPrice
                      }

                      private int getDiscountLevel() {
                        if (quantity > 100) return 2
                        return 1
                      }
                    }
                    println new Order(120, 5).price // => 480.0
… Replace parameter with method …
                    class Order {
                      private int quantity, itemPrice
                      Order(q, p) {quantity = q; itemPrice = p}

                      double getPrice() {
                        return discountedPrice(getBasePrice())
                      }

                      private double discountedPrice(int basePrice) {
© ASERT 2006-2010




                        if (getDiscountLevel() == 2) return basePrice * 0.8
                        return basePrice * 0.9
                      }

                      private int getBasePrice() {
                        quantity * itemPrice
                      }

                      private int getDiscountLevel() {
                        if (quantity > 100) return 2
                        return 1
                      }
                    }
                    println new Order(120, 5).price // => 480.0
… Replace parameter with method …
                    class Order {
                      private int quantity, itemPrice
                      Order(q, p) {quantity = q; itemPrice = p}

                      double getPrice() {
                        return discountedPrice()
                      }

                      private double discountedPrice() {
© ASERT 2006-2010




                        if (getDiscountLevel() == 2) return getBasePrice() * 0.8
                        return getBasePrice() * 0.9
                      }

                      private int getBasePrice() {
                        quantity * itemPrice
                      }

                      private int getDiscountLevel() {
                        if (quantity > 100) return 2
                        return 1
                      }
                    }
                    println new Order(120, 5).price // => 480.0
… Replace parameter with method …
                    class Order {
                      private int quantity, itemPrice
                      Order(q, p) {quantity = q; itemPrice = p}

                      double getPrice() {
                        return discountedPrice()
                      }

                      private double discountedPrice() {
© ASERT 2006-2010




                        if (getDiscountLevel() == 2) return getBasePrice() * 0.8
                        return getBasePrice() * 0.9
                      }

                      private int getBasePrice() {
                        quantity * itemPrice
                      }

                      private int getDiscountLevel() {
                        if (quantity > 100) return 2
                        return 1
                      }
                    }
                    println new Order(120, 5).price // => 480.0
… Replace parameter with method
                    class Order {
                      private int quantity, itemPrice
                      Order(q, p) {quantity = q; itemPrice = p}

                      double getPrice() {
                        if (getDiscountLevel() == 2) return getBasePrice() * 0.8
                        return getBasePrice() * 0.9
                      }
© ASERT 2006-2010




                      private getBasePrice() {
                        quantity * itemPrice
                      }

                      private getDiscountLevel() {
                        if (quantity > 100) return 2
                                                                             Note the now small
                        return 1                                              parameter lists
                      }
                    }
                    println new Order(120, 5).price // => 480.0
Some functional style …
                    class Order {
                      private int quantity, itemPrice
                      Order(q, p) {quantity = q; itemPrice = p}

                      def discountedPrice = { basePrice, discountLevel ->
                        discountLevel == 2 ? basePrice * 0.8 : basePrice * 0.9 }

                      def price = {
                        int basePrice = quantity * itemPrice
© ASERT 2006-2010




                        def discountLevel = (quantity > 100) ? 2 : 1
                        discountedPrice(basePrice, discountLevel) }

                    }
                    println new Order(120, 5).price() // => 480.0




                                                                         Traditional refactoring
                                                                       still applicable if we used
                                                                    closures rather than methods...
… Some functional style …
                    class Order {
                      private int quantity, itemPrice
                      Order(q, p) {quantity = q; itemPrice = p}

                      def basePrice = { quantity * itemPrice }

                      def discountLevel = { quantity > 100 ? 2 : 1 }

                      def price = {
© ASERT 2006-2010




                        discountLevel() == 2 ? basePrice() * 0.8 : basePrice() * 0.9 }

                    }
                    println new Order(120, 5).price() // => 480.0




                                                                       ... as we see here
… Some functional style …
                    class Order {
                      private int quantity, itemPrice
                      Order(q, p) {quantity = q; itemPrice = p}

                      def basePrice = { quantity * itemPrice }

                      def discountLevel = { quantity > 100 ? 2 : 1 }

                      def discountedPrice = { basePrice, discountLevel ->
© ASERT 2006-2010




                        discountLevel == 2 ? basePrice * 0.8 : basePrice * 0.9 }

                      def price = {
                        discountedPrice.curry(basePrice()).curry(discountLevel()).call() }

                    }
                    println new Order(120, 5).price() // => 480.0



                                                                    But we can also use currying
… Some functional style
                    class Order {
                      private int quantity, itemPrice
                      Order(q, p) {quantity = q; itemPrice = p}

                      def basePrice = { quantity * itemPrice }

                      def discountLevel = { quantity > 100 ? 2 : 1 }

                      def discountedPrice(basePrice, discountLevel) {
© ASERT 2006-2010




                        discountLevel == 2 ? basePrice * 0.8 : basePrice * 0.9
                      }

                      def price = {
                        this.&discountedPrice.curry(basePrice()).curry(discountLevel()).call()
                      }

                    }
                    println new Order(120, 5).price() // => 480.0



                                                        We can also use currying with methods
Closure Refactoring …
                    • Complex code involving closures


                    // Before refactoring
                    def phrase = "The quick brown fox jumps over the lazy dog"
                    def result = phrase.toLowerCase().toList().
                      findAll{ it in "aeiou".toList() }.    // like WHERE ...
© ASERT 2006-2010




                      groupBy{ it }.                        // like GROUP BY ...
                      findAll{ it.value.size() > 1 }.       // like HAVING ...
                      sort{ it.key }.reverse().             // like ORDER BY ...
                      collect{ "$it.key:${it.value.size()}" }.
                      join(", ")
                    println result
… Closure Refactoring …
                    • Possible Refactoring

                    // Refactored helper closures
                    def lowercaseLetters = phrase.toLowerCase()
                    def vowels = { it in "aeiou".toList() }
                    def occursMoreThanOnce = { it.value.size() > 1 }
                    def byReverseKey = { a, b -> b.key <=> a.key }
                    def self = { it }
© ASERT 2006-2010




                    def entriesAsPrettyString = { "$it.key:${it.value.size()}" }
                    def withCommaDelimiter = ", "

                    // Refactored main closure
                    println lowercaseLetters.
                        findAll(vowels).
                        groupBy(self).
                        findAll(occursMoreThanOnce).
                        sort(byReverseKey).
                        collect(entriesAsPrettyString).
                        join(withCommaDelimiter)
… Closure Refactoring
                    # Add group_by to the Array class
                    class Array
                      def group_by
                        group_hash = {}
                        uniq.each do |e|
                          group_hash[e] = select { |i| i == e }.size
                        end
                        group_hash
                      end
                    end

                    # Before refactoring
                    phrase = "The quick brown fox jumps over the lazy dog"
© ASERT 2006-2010




                    puts phrase.downcase.
                      scan(/[aeiou]/).                      # like WHERE ...
                      group_by.                             # like GROUP BY ...
                      select { |key, value| value > 1 }.    # like HAVING ...
                      sort.reverse.                         # like ORDER BY ... DESC
                      collect{ |key, value| "#{key}:#{value}" }.join(', ')

                    # Refactored version
                    lowercase_letters = phrase.downcase
                    vowels = /[aeiou]/
                    occurs_more_than_once = lambda { |key,value| value > 1 }
                    entries_as_pretty_string = lambda { |key, value| "#{key}:#{value}" }

                    puts lowercase_letters.
                      scan(vowels).
                      group_by.
                      select(&occurs_more_than_once).
                      sort.reverse.
                      collect(&entries_as_pretty_string).join(', ')
Unnecessary Complexity Refactoring
                    • Dynamic Code Creation
                          – What to look for: Code uses eval,
                            class_eval or module_eval to build new
                            code dynamically
                          – Issues: harder to read, fluid abstractions
                            are harder to understand, harder to test
© ASERT 2006-2010




                            and debug
                          – What to do:
                                • move string form of eval to block forms or use
                                  define_method
                                • move method_missing to use class_eval
                                  (example of Replace Dynamic Receptor with
                                  Dynamic Method Definition)
                                • consider using Move Eval from Run-time to
                                  Parse-time to overcome bottlenecks
                    Source: Dynamic Code Creation in Chapter 7: Unnecessary Complexity (Refactoring in Ruby)
Topics
                    • Introduction
                    • Design patterns
                    • Refactoring
                    Polyglot programming
                    • SOLID principles
© ASERT 2006-2010




                    • Other topics
                    • More Info
Programming Paradigms...
                                   • Named state
                                     (imperative style –
                                     leads to modularity)
                                     vs unnamed state




                                                             http://www.info.ucl.ac.be/~pvr/paradigms.html
                                     (functional and logic
                                     style)
                                   • Deterministic vs
© ASERT 2006-2010




                                     observable
                                     nondeterminism
                                     (threads, guards)
                                   • Sequential vs
                                     concurrent
                                     (message passing
                                     and shared state
                                     styles)
© ASERT 2006-2010

                                                ...Programming Paradigms




http://www.info.ucl.ac.be/~pvr/paradigms.html
Polyglot Programming…
                    • Groovy calling clojure
                        @Grab('org.clojure:clojure:1.0.0')
                        import clojure.lang.Compiler
                        import clojure.lang.RT

                        def src = new File('temp.clj')
                        src.text = '''
© ASERT 2006-2010




                        (ns groovy)
                        (defn factorial [n]
                            (if (< n 2)
                                1
                                (* n (factorial (- n 1)))))
                        '''
                        src.withReader { reader ->
                             Compiler.load reader
                        }
                        def fac = RT.var('groovy', 'factorial')
                        println fac.invoke(5)
…Polyglot Programming
                    • C# calling F#

                    // F# Code

                    type FCallback = delegate of int*int -> int;;
                    type FCallback =
                      delegate of int * int -> int
© ASERT 2006-2010




                    let f3 (f:FCallback) a b = f.Invoke(a,b);;
                    val f3 : FCallback -> int -> int -> int

                    // C# Code

                    // method gets converted to the delegate automatically in C#
                    int a = Module1.f3(Module1.f2, 10, 20);
Topics
                    • Introduction
                    • Design patterns
                    • Refactoring
                    • Polyglot programming
                    SOLID principles
© ASERT 2006-2010




                    • Other topics
                    • More Info
Source: http://www.lostechies.com/content/pablo_ebook.aspx (Derick Bailey)
© ASERT 2006-2010
SOLID Principles

                    •   Single Responsibility Principle
                    •   Open/Closed Principle
                    •   Liskov Substitution Principle
© ASERT 2006-2010




                    •   Interface Segregation Principle
                    •   Dependency Inversion Principle
Source: http://www.lostechies.com/content/pablo_ebook.aspx
© ASERT 2006-2010
Open-Closed Principle...
                    • Fundamental rule to make
                      your software flexible
                      – Many other OOP principles, methodologies and
                        conventions revolve around this principle
                    • Open-Closed Principle (OCP) states:
                      • Software entities should be open for
© ASERT 2006-2010




                        extension, but closed for modification
                    • References
                      – Bertrand Meyer, Object Oriented Software
                        Construction (88, 97)
                      – Robert C Martin, The Open-Closed Principle
                      – Craig Larman, Protected Variation: The Importance of
                        Being Closed
                                               Picture source: http://www.vitalygorn.com
...Open-Closed Principle...
                    • Following the Rules
                      – Encapsulation: Make anything that shouldn’t be seen
                        private
                      – Polymorphism: Force things to be handled using
                        abstract classes or interfaces
                    • When making class hierarchies:
© ASERT 2006-2010




                      – Make anything that shouldn’t be open final
                      – Polymorphism: Always follow weaker pre stronger
                        post (object substitutability in the static world)
                    • When making changes that might break
                      existing clients
                      – Add a new class into the hierarchy
                      – No compilation of existing code! No breakages!
...Open-Closed Principle...
                    • Part I: If I violate the Open part of OCP in
                      static languages
                       – I can’t make the future enhancements I need
                    • Part II: If I violate the Closed part of OCP
                       – Client applications using my libraries might
© ASERT 2006-2010




                         break or require recompilation in the future

                                                Class A    Extendible   Class A
                               Interface         User       Class A      User

                                                Class A’                Class A’
                                                 User       Class A’
                                                                         User

                                                Optional                Optional
                     Class A         Class A’
                                                Factory                 Factory
                                                                                   ...
...Open-Closed Principle...
                     • Part I: Consider Java’s String class
                         – Has methods to convert to upper or
                           lower case but no swapCase() method?
                         – Traditionally, consider creating an
                           EnhancedString class using inheritance?
                         – I can’t: String is immutable and final
© ASERT 2006-2010




                             • In OCP terms, it is not open for extension

                     • Dynamic language solution: open classes
                                                                         String.metaClass.swapCase = {
                    #light                                                 delegate.collect{ c ->
                    open String
                    type System.String with                                  c in 'A'..'Z' ?
                      member x.swapCase =                                      c.toLowerCase() :
                        seq { for letter in x.ToCharArray() do
                                if (System.Char.IsLower(letter))               c.toUpperCase()
                                then yield System.Char.ToUpper(letter)     }.join()
                                else yield System.Char.ToLower(letter)
                        }                                                }
                    printfn "result: %A" "Foo".swapCase                  assert "Foo".swapCase() == "fOO"
                                                                                                            ...
...Open-Closed Principle...
                    • Part II: Violating OCP (see [15])
                      class Square {
                          def side
                      }
                      class Circle {
                          def radius
                      }
© ASERT 2006-2010




                      class AreaCalculator {
                          double area(shape) {
                              switch (shape) {
                                  case Square:
                                      return shape.side * shape.side
                                  case Circle:
                                      return Math.PI * shape.radius ** 2
                              }
                          }
                      }
...Open-Closed Principle...
                      def shapes = [
                          new Square(side: 3),
                          new Square(side: 2),
                          new Circle(radius: 1.5)
                      ]

                      def calc = new AreaCalculator()
                      shapes.sort().each {s ->
                          println "Area of $s.class.name is ${calc.area(s)}"
© ASERT 2006-2010




                      }


                    • What’s wrong
                      – If we wanted to introduce a Triangle, the
                        AreaCalculator would need to be recompiled
                      – If we wanted to change the order the shape
                        information was displayed, there might be many
                        changes to make
...Open-Closed Principle...
                                                     * Our abstractions never designed sorting to be
                    • Dynamic shapes                 one of the things open for extension. See [15].

                      – No issue with adding Triangle but sorting is an issue *
                    class Square {                                            Note: Duck-type
                        private side                                          polymorphism
                        double area() { side ** 2 }                           instead of
                    }                                                         inheritance
                    class Circle {                                            polymorphism,
© ASERT 2006-2010




                        private radius                                        i.e. no base Shape
                        double area() { Math.PI * radius ** 2 }               (abstract) class or
                    }                                                         interface.
                                                                              Hmm… what are
                    def shapes = [                                            the testing
                        new Square(side:3),                                   implications when
                        new Square(side:2),                                   I add Triangle?
                        new Circle(radius:1.5)
                                                            Area of Square is 9.0
                    ]
                                                            Area of Square is 4.0
                    // unsorted                             Area of Circle is 7.0685834705770345
                    def prettyPrint = { s ->
                        println "Area of $s.class.name is   ${s.area()}" }
                    shapes.each(prettyPrint)
                                                                                                   ...
...Open-Closed Principle...
                    • Dynamic sorting using Closures
                       – As long as we are happy having our sort “code”
                         within a closure we have complete freedom
                       – Sometimes representing our abstractions within
                         classes is appropriate; many times closures will do
                                                             Area of Square is 4.0
© ASERT 2006-2010




                                                             Area of Circle is 7.0685834705770345
                    // sorted by area                        Area of Square is 9.0
                    def byArea = { s -> s.area() }
                    shapes.sort(byArea).each(prettyPrint)                    Note: Make sure your
                                                                             closures are testable.

                    // sorted circles before squares but otherwise by area
                    def byClassNameThenArea = { sa, sb ->
                        sa.class.name == sb.class.name ?          Area of Circle is 7.06858...
                            sa.area() <=> sb.area() :             Area of Square is 4.0
                                                                  Area of Square is 9.0
                            sa.class.name <=> sb.class.name
                    }
                    shapes.sort(byClassNameThenArea).each(prettyPrint)
                                                                                                      ...
...Open-Closed Principle...
                    • Instead of worrying about
                      – Rigidity
                      – Fragility
                      – Immobility
                      (Because they can be easily gotten
                        around even if you don’t try to apply OCP)
© ASERT 2006-2010




                    • We must worry about
                      – Duplication
                      – Harder refactoring or testing
                      – Feature interaction
                    • And of course OCP then leads to ...
                      – Liskov Substitution Principle, Single Responsibility
                        Principle, Dependency Inversion Principle, ...
...Open-Closed Principle...
                    • “Clean code” [23] states it this way:
                      – Procedural code (i.e. using data structures) makes it
                        easy to add new functions without changing existing
                        data structures but when new data structures are
                        added, all existing procedures may need to change
                      – OO code makes it easy to add new classes without
© ASERT 2006-2010




                        changing existing functions but when new functions
                        are added, all classes must change
                    • Recommendation?
                      – Choose procedural or OO approach based on
                        whether anticipated evolution of system involves
                        functions or data
                      – Use Visitor (dual dispatch) Pattern if you think both
                        functions and data might change
...Open-Closed Principle...
                    class Square {
                      double side
                    }

                    class Rectangle {
                      double height, width
                    }

                    class Circle {
© ASERT 2006-2010




                      double radius
                    }

                    class Geometry {
                      def area(shape) {
                        switch (shape) {
                          case Square:    return shape.side ** 2
                          case Rectangle: return shape.height * shape.width
                          case Circle:    return PI * shape.radius ** 2
                        }
                      }
                                        Can add perimeter() here without shape classes changing but if we
                    }
                                              added a Triangle, area(), perimeter() etc. would need to change.
...Open-Closed Principle...
                    interface Shape {                   If we add perimeter() here, each
                      double area()                   shape class must change but we can
                    }                                   add new shapes with no changes

                    class Square implements Shape {
                      double side
                      double area() { side ** 2 }
                    }
© ASERT 2006-2010




                    class Rectangle implements Shape {
                      double height, width
                      double area() { height * width }
                    }

                    class Circle implements Shape {
                      double radius
                      double area() { PI * radius ** 2 }
                    }
...Open-Closed Principle...
                    class Square {
                      double side
                      double area() { side ** 2   }
                    }                                    We can easily add perimeter() here
                                                         but for any code requiring the perimeter()
                                                         method to exist, we should test that code
                    class Rectangle {                    with all shapes.
                      double height, width
                      double area() { height * width }
                    }
© ASERT 2006-2010




                    class Circle {
                      double radius
                      double area() { PI * radius ** 2 }
                    }
...Open-Closed Principle...
                    • “Clean code” [23] recommendation:
                      – Choose procedural or OO approach or Visitor
                    • Agile variation:
                      – Defer moving to complicated solutions, e.g. Visitor
                        Pattern, but have in place sufficient tests so that you
                        can confidently refactor to use one later if needed
© ASERT 2006-2010




                    • Dynamic language variation:
                      – You won’t need an explicit visitor (more on this later)
                      – Duck typing lets you add functions or data without
                        changing existing classes at the expense of static
                        type safety
                      – If you add a function you might need additional tests
                        for each class associated with that function
                      – If you add a new class you might need additional
                        tests for each function associated with that class
Topics
                    • Introduction
                    • Design patterns
                    • Refactoring
                    • Polyglot programming
                    • SOLID principles
© ASERT 2006-2010




                    Other topics
                    • More Info
Other topics
                    •   The need for Dependency Injection
                    •   The need for Mocking frameworks
                    •   Concurrency
                    •   Feature interaction
                    •   Writing DSLs
© ASERT 2006-2010
Topics
                    • Introduction
                    • Design patterns
                    • Refactoring
                    • Polyglot programming
                    • SOLID principles
© ASERT 2006-2010




                    • Other topics
                    More Info
Further Information…
                    •   [1] Dynamic vs. Static Typing — A Pattern-Based Analysis, Pascal Costanza,
                        University of Bonn, 2004
                        http://p-cos.net/documents/dynatype.pdf
                    •   [2] Interface-Oriented Design, Ken Pugh, Pragmatic Programmers, 2006
                    •   [3] Bruce Eckel, Does Java need Checked Exceptions?
                        www.mindview.net/Etc/Discussions/CheckedExceptions
                    •   [4] Null Object, Kevlin Henney, Proceedings EuroPLoP 2002
                    •   [5] Design Patterns in Dynamic Programming, Peter Norvig, March 1998
                        http://www.norvig.com/design-patterns/
                    •   [6] Advanced Programming Language Features and Software Engineering:
© ASERT 2006-2010




                        Friend or Foe?, Greg Sullivan, April 2002
                        http://people.csail.mit.edu/gregs/proglangsandsofteng.pdf
                    •   [7] JunGL: a Scripting Language for Refactoring, Mathieu Verbaere et al, May
                        2006
                        http://progtools.comlab.ox.ac.uk/publications/icse06jungl
                    •   [8] Rails for Java Developers, Halloway et al, Pragmatic Bookshelf, 2007,
                        Chapter 3, Ruby Eye for the Java Guy
                    •   [9] Building DSLs in Static & Dynamic languages
                        http://www.nealford.com/downloads/conferences/canonical/Neal_Ford-
                        Building_DSLs_in_Static_and_Dynamic_Languages-handouts.pdf
                    •   [10] Five Easy Pieces: Simple Python Non-Patterns, Alex Martelli
                        http://www.aleax.it/5ep.html
                    •   [11] Emergent Design, Scott L. Bain, 2008
…Further Information
                    •   [12] Design Patterns: Elements of Reusable Object-Oriented Software, Erich
                        Gamma, Richard Helm, Ralph Johnson, John Vlissides, 1995
                    •   [13] Refactoring: Improving the Design of Existing Code, Martin Fowler, 1999
                    •   [14] Effective Java Programming Language Guide, Erich Gamma, Joshua Bloch, 2001
                    •   [15] Agile Software Development, Principles, Patterns, and Practices, Robert C
                        Martin, 2002
                    •   [16] Composing Features and Resolving Interactions, Jonathan Hay and Joanne
                        Atlee, University of Waterloo
                    •   [17] Handling Feature Interactions in the Language for End System Services,
© ASERT 2006-2010




                        Xiaotao Wua and Henning Schulzrinne, January 2007
                    •   [18] FAQ Sheet on Feature Interaction, Pamela Zave
                        http://www.research.att.com/~pamela/faq.html
                    •   [19] Liskov Substitution Principle and the Ruby Core Libraries, Dean Wampler
                        http://blog.objectmentor.com/articles/tag/liskovsubstitutionprinciple
                    •   [20] Liskov Substitution in Dynamic Languages, Michael Feathers
                        http://butunclebob.com/ArticleS.MichaelFeathers.LiskovSubstitutionInDynamicLanguages
                    •   [21] Domain-Specific Languages: An Annotated Bibliography, van Deursen et al
                        http://homepages.cwi.nl/~arie/papers/dslbib/
                    •   [22] Agile Principles, Patterns, and Practices in C#, Martin C. Robert et al, 2006
                    •   [23] Clean Code, Robert C. Martin, 2008
                    •   [24] The Craftsman: 51, Ruby Visitor, Robert C Martin, August 2007
                        http://www.objectmentor.com/resources/articles/Craftsman51.pdf
                    •   [25] http://www.info.ucl.ac.be/~pvr/paradigms.html
                    •   [26] http://p-cos.net/documents/dynatype.pdf

More Related Content

PDF
Make Your Builds More Groovy
PDF
GroovyDSLs
PDF
make builds groovy
PDF
Challenges in Maintaining a High Performance Search Engine Written in Java
PDF
FraSCAti@rivieradev
PPTX
EdgefxKits Presentation
PDF
09J1_ACG_Prospect
PPTX
Principles and Practices for Teaching English as an International Language: T...
Make Your Builds More Groovy
GroovyDSLs
make builds groovy
Challenges in Maintaining a High Performance Search Engine Written in Java
FraSCAti@rivieradev
EdgefxKits Presentation
09J1_ACG_Prospect
Principles and Practices for Teaching English as an International Language: T...

Similar to Dynamic Language Practices (20)

PDF
Paulking dlp
PDF
DTS s03e04 Typing
DOC
Maintenance of Dynamically vs. Statically typed Languages
PDF
(ThoughtWorks Away Day 2009) one or two things you may not know about typesys...
PDF
Groovy Tutorial
PDF
LectureNotes-01-DSA
PDF
PDF
PDF
PDF
Orientation - Java
PDF
Dynamic Languages Are The Future
PPT
Trends in Programming Technology you might want to keep an eye on af Bent Tho...
PPTX
C# .NET - Um overview da linguagem
PDF
Static vs dynamic types
PPTX
Overview Of .Net 4.0 Sanjay Vyas
PDF
Tales from the Workshops
PDF
Understanding Typing. Understanding Ruby.
PDF
Generic Programming
PDF
Ruby World
DOC
10266 developing data access solutions with microsoft visual studio 2010
Paulking dlp
DTS s03e04 Typing
Maintenance of Dynamically vs. Statically typed Languages
(ThoughtWorks Away Day 2009) one or two things you may not know about typesys...
Groovy Tutorial
LectureNotes-01-DSA
Orientation - Java
Dynamic Languages Are The Future
Trends in Programming Technology you might want to keep an eye on af Bent Tho...
C# .NET - Um overview da linguagem
Static vs dynamic types
Overview Of .Net 4.0 Sanjay Vyas
Tales from the Workshops
Understanding Typing. Understanding Ruby.
Generic Programming
Ruby World
10266 developing data access solutions with microsoft visual studio 2010
Ad

More from Paul King (19)

PDF
awesome groovy
PDF
groovy databases
PPTX
groovy transforms
PPTX
concurrency gpars
PDF
tictactoe groovy
PDF
groovy rules
PDF
functional groovy
PDF
Make Testing Groovy
PDF
Agile Testing Practices
PDF
groovy DSLs from beginner to expert
PDF
concurrency with GPars
PDF
groovy and concurrency
PDF
Atlassian Groovy Plugins
PDF
Groovy Power Features
PDF
Make Your Testing Groovy
PDF
Groovy Testing Sep2009
PDF
Craig Smith & Paul King Agile Tool Hacking Taking Your Agile Development ...
PDF
Industrial Strength Groovy - Tools for the Professional Groovy Developer: Pau...
PDF
XML and Web Services with Groovy
awesome groovy
groovy databases
groovy transforms
concurrency gpars
tictactoe groovy
groovy rules
functional groovy
Make Testing Groovy
Agile Testing Practices
groovy DSLs from beginner to expert
concurrency with GPars
groovy and concurrency
Atlassian Groovy Plugins
Groovy Power Features
Make Your Testing Groovy
Groovy Testing Sep2009
Craig Smith & Paul King Agile Tool Hacking Taking Your Agile Development ...
Industrial Strength Groovy - Tools for the Professional Groovy Developer: Pau...
XML and Web Services with Groovy
Ad

Recently uploaded (20)

PDF
7 ChatGPT Prompts to Help You Define Your Ideal Customer Profile.pdf
PDF
Diabetes mellitus diagnosis method based random forest with bat algorithm
PDF
Unlocking AI with Model Context Protocol (MCP)
PPTX
PA Analog/Digital System: The Backbone of Modern Surveillance and Communication
PPT
“AI and Expert System Decision Support & Business Intelligence Systems”
PDF
Mobile App Security Testing_ A Comprehensive Guide.pdf
PPTX
Cloud computing and distributed systems.
PDF
Dropbox Q2 2025 Financial Results & Investor Presentation
PDF
Network Security Unit 5.pdf for BCA BBA.
PDF
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
PDF
Encapsulation theory and applications.pdf
PDF
Encapsulation_ Review paper, used for researhc scholars
PPTX
MYSQL Presentation for SQL database connectivity
PDF
Review of recent advances in non-invasive hemoglobin estimation
PPTX
A Presentation on Artificial Intelligence
PPTX
VMware vSphere Foundation How to Sell Presentation-Ver1.4-2-14-2024.pptx
PDF
cuic standard and advanced reporting.pdf
PDF
Advanced methodologies resolving dimensionality complications for autism neur...
PDF
Modernizing your data center with Dell and AMD
PDF
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
7 ChatGPT Prompts to Help You Define Your Ideal Customer Profile.pdf
Diabetes mellitus diagnosis method based random forest with bat algorithm
Unlocking AI with Model Context Protocol (MCP)
PA Analog/Digital System: The Backbone of Modern Surveillance and Communication
“AI and Expert System Decision Support & Business Intelligence Systems”
Mobile App Security Testing_ A Comprehensive Guide.pdf
Cloud computing and distributed systems.
Dropbox Q2 2025 Financial Results & Investor Presentation
Network Security Unit 5.pdf for BCA BBA.
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
Encapsulation theory and applications.pdf
Encapsulation_ Review paper, used for researhc scholars
MYSQL Presentation for SQL database connectivity
Review of recent advances in non-invasive hemoglobin estimation
A Presentation on Artificial Intelligence
VMware vSphere Foundation How to Sell Presentation-Ver1.4-2-14-2024.pptx
cuic standard and advanced reporting.pdf
Advanced methodologies resolving dimensionality complications for autism neur...
Modernizing your data center with Dell and AMD
How UI/UX Design Impacts User Retention in Mobile Apps.pdf

Dynamic Language Practices

  • 1. Developer Practices for Dynamic Languages “Unlearning Java/C#” Dr Paul King, Director ASERT, Australia paulk@asert.com.au
  • 2. Topics Introduction • Design patterns • Refactoring • Polyglot programming • SOLID principles © ASERT 2006-2010 • Other topics • More Info ESDC 2010 - 2
  • 3. Introduction … • Developer practices – Well understood and documented for traditional languages like Java, C++ and C# – But dynamic languages like Groovy, Ruby, Python, Boo, JavaScript and others, change the ground rules © ASERT 2006-2010 – Many of the rules and patterns we have been taught must be adapted or adjusted; some no longer apply at all
  • 4. ...Introduction... • What does Immutability mean? – When even constants can be changed • What does encapsulation mean? – When I can peek at internal state or when using languages without state • How can I devise tests © ASERT 2006-2010 at development time? – When my system can change in unknown ways at runtime • How can IDEs help me? – If I no longer spoon feed static-type information to my IDE, what level of support can it give me in terms of code completion and error checking
  • 5. … Introduction • Traditional developer practice guidelines – Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides (1995). Design Patterns: Elements of Reusable Object- Oriented Software. Addison-Wesley. – Martin Fowler (1999). Refactoring: Improving the Design of Existing Code. Addison-Wesley. – Joshua Bloch (2001). Effective Java Programming © ASERT 2006-2010 Language Guide. Prentice Hall. – Robert C Martin (2002), Agile Software Development, Principles, Patterns, and Practices. Prentice Hall. – Robert C Martin (2006), Agile Principles, Patterns, and Practices in C#. Prentice Hall. • In the dynamic language world, are the guidelines in these books FACT or MYTH? • But first let’s look at what we mean by dynamic languages and dynamic typing
  • 6. What do I mean by Dynamic Language? • I prefer a flexible definition • One or more of: – Dynamic typing • Greater polymorphism – Metaprogramming © ASERT 2006-2010 • Allow language itself to be dynamically changed • Allow hooks into object lifecycle and method calls • Open classes/monkey patching – Work with code as easily as data • Closures • Higher-order programming – Escape hatches • Hooks for polyglot programming
  • 7. … Static vs Dynamic Typing … • MYTH or TRUTH? Static typing is just spoon feeding the compiler/IDE. It represents the old-school way of thinking and requires extra work while providing no real value. © ASERT 2006-2010 Static VS Dynamic
  • 8. Static vs Dynamic Typing … • Static: the type of each variable (or expression) must be known at compile time © ASERT 2006-2010 dynamic advocates: like programming wearing a straight-jacket? Unnecessary complexity
  • 9. …Static vs Dynamic Typing … • Static Typing Pros – Errors are often detected earlier and with better error messages – Code can sometimes be clearer – you don’t need to infer the types to understand the code – especially when revisiting the code later © ASERT 2006-2010 – Safer because certain kinds of injection hacks don’t apply – Code can be more declarative – Better IDE support: refactoring, editing and other forms of source processing support is often possible – Better optimisations are often possible – Often easier to understand a system from the outside (“self-documenting” statically-typed APIs and interfaces) – With generics support you can start to nail down even complex cases
  • 10. …Static vs Dynamic Typing … • Dynamic: type information is known only at runtime © ASERT 2006-2010 static advocates: like tightrope walking with no net?
  • 11. … Static vs Dynamic Typing … • Dynamic Typing Pros – Speed development through duck-typing and less boiler-plate code – Clearer more concise code is easier to read and maintain – Allow more expressiveness through DSLs © ASERT 2006-2010 – You should have comprehensive tests anyway, why not cover off types as part of those tests – Enforced healthy practices: • Static language developers may get a false sense of security and not design/test for runtime issues • Less likely to neglect good documentation and/or good coding conventions on the grounds that your static types make everything “inherently” clear
  • 12. …Static vs Dynamic Typing … • Strong vs weak typing – Strong: List<Integer> myList – Weak: Object myList • Type safety – How is this provided if at all? © ASERT 2006-2010 • Type inference – Is this supported?
  • 13. …Static and Dynamic Typing… © ASERT 2006-2010 Source: http://www.slideshare.net/pcalcado/one-or-two-things-you-may-not-know-about-typesystems
  • 14. Correctness? © ASERT 2006-2010 Source: http://www.slideshare.net/pcalcado/one-or-two-things-you-may-not-know-about-typesystems
  • 15. Typing Approaches… interface Duck { def waddle() def quack() } class DuckImpl implements Duck { © ASERT 2006-2010 def waddle() { println "waddle" } def quack() { println "quack" } } class Goose { def waddle() { println "Goose waddle" } def quack() { println "Goose quack" } }
  • 16. …Typing Approaches… • Inheritance hierarchies – Very clear intent but use sparingly • Interface-oriented design – Use if it adds clarity & your language supports it – If you do use it, stick to fine-grained interfaces • Dynamic interface-oriented design © ASERT 2006-2010 Source: Rick DeNatale – If your language doesn’t support it natively you © David Friel can use a guard: is_a?, kind_of?, instanceof • Chicken typing – Use a guard: responds_to?, respondsTo • Duck typing – Use when flexibility is important but have appropriate tests in place; e.g. you don’t want to violate the Liskov Substitution Principal[15] by not considering a refused bequest[13]. • AKA roll your own type safety
  • 17. …Typing Approaches • Implicit vs Explicit interfaces – Inheritance too restrictive? – Duck-typing too flexible? Menu set_sides() Shape <<interface>> <<interface>> Rectangle draw() Shape RegularPolygon draw() draw() set_side() set_sides() © ASERT 2006-2010 Rectangle Square draw() draw() set_sides() Rectangle Square EquilateralTriangle set_side() draw() draw() draw() set_sides() set_side() set_side() Square Pistol draw() draw() set_sides() I tend to use Explicit types for major boundaries and EquilateralTriangle implicit types internally. draw() set_side() Adapted from Interface-Oriented Design [2]
  • 18. … Static vs Dynamic Typing … • MYTH Removing static typing always leads to more concise and readable code. © ASERT 2006-2010 X incorrect
  • 19. … Static vs Dynamic Typing … • An example interface Reversible { def reverse() } class ReversibleString implements Reversible { def reverse() { /* */ } } class ReversibleArray implements Reversible { def reverse() { /* */ } } © ASERT 2006-2010 Reversible[] things = [ new ReversibleString(), new ReversibleArray() ] for (i in 0..<things.size()) { things[i].reverse() } def things = ["abc", [1, 2 ,3]] def expected = ["cba", [3, 2, 1]] assert things*.reverse() == expected
  • 20. … Static vs Dynamic Typing ... interface Reversible { With dynamically def reverse() typed languages, } there is no need to class ReversibleString implements Reversible { explicitly declare the def reverse() { /* */ } } types of variables or the “protocols” class ReversibleArray implements Reversible { def reverse() { /* */ } observed by our } © ASERT 2006-2010 objects:  Less code Reversible[] things = [ new ReversibleString(), new ReversibleArray()  Less declarative ]  Less IDE support for (i in 0..<things.size()) {  More testing things[i].reverse()  Less Robust? } def things = ["abc", [1, 2 ,3]] def expected = ["cba", [3, 2, 1]] assert things*.reverse() == expected
  • 21. … Static vs Dynamic Typing … • MYTH Dynamic typing means the IDE can’t provide support for completion and early syntax error checks. © ASERT 2006-2010 X incorrect
  • 22. … Static vs Dynamic Typing ... • Consider Groovy in Intellij • And Eclipse © ASERT 2006-2010 Eclipse example: http://contraptionsforprogramming.blogspot.com/
  • 23. Typing approaches and IDEs… • Class A has a bit of duplication class A { def helper def make() { helper.invoke('create') } def get() { © ASERT 2006-2010 helper.invoke('read') } def change() { helper.invoke('update') } def remove() { helper.invoke('delete') } }
  • 24. … Typing approaches and IDEs … • No problems, we can refactor out the dup class B { def helper def make() { invoke('create') } def get() { © ASERT 2006-2010 invoke('read') } def change() { invoke('update') } def remove() { invoke('delete') } private invoke(cmd) { helper.invoke(cmd) } }
  • 25. … Typing approaches and IDEs … • But we can do more using a dynamic language by leveraging metaprogramming class C { def helper def commands = [ make: 'create', © ASERT 2006-2010 get: 'read', change: 'update', remove: 'delete' ] def invokeMethod(String name, ignoredArgs) { helper.invoke(commands[name]) } } • Which is a whole lot nicer? • At the expense of IDE completion? … ...
  • 26. … Typing approaches and IDEs … class Dumper { def name def invokeMethod(String methodName, args) { println "$name: called $methodName with $args" } } © ASERT 2006-2010 for (x in [A, B, C]) { def o = x.newInstance() o.helper = new Dumper(name: "$x.name's helper") o.make() o.get() o.change() o.remove() }
  • 27. … Typing approaches and IDEs • … At the expense of IDE completion? © ASERT 2006-2010 But remember: “clearly express intent”
  • 28. … Static vs Dynamic Typing … © ASERT 2006-2010 • MYTH Static typing means runtime errors are a thing of the past. X incorrect Source: http://www.slideshare.net/pcalcado/one-or-two-things-you-may-not-know-about-typesystems (phillip calçado)
  • 29. … Static vs Dynamic Typing ... • Consider Lift (based on Scala) <lift:surround with="default" at="content"> <h2>Welcome to your project!</h2> <p><lift:hellWorld.howdy /></p> </lift:surround> Result: No error but empty home page © ASERT 2006-2010 <lift:surrond with="default" at="content"> <h2>Welcome to your project!</h2> <p><lift:hellWorld.howdy /></p> </lift:surround> Source: http://zef.me/2371/when-scala-dsls-fail
  • 30. Static and Dynamic Strong Typing © ASERT 2006-2010 Source: http://www.slideshare.net/pcalcado/one-or-two-things-you-may-not-know-about-typesystems
  • 31. Static vs Dynamic Typing Static Dynamic Syntax bugs Optimisation © ASERT 2006-2010 Arithmetic bugs Logic bugs approx same approx same Resource bugs Concurrency bugs Power Flexibility
  • 32. Static vs Dynamic Typing Verdict • MYTH or TRUTH? Static typing is just spoon feeding the compiler. It represents the old-school way of thinking and requires extra work while providing no real value. © ASERT 2006-2010 ...but not a total lie either... ...dynamic languages certainly assist with removing duplication, clutter and boilerplate code...
  • 33. An open debate © ASERT 2006-2010 Source: http://www.slideshare.net/pcalcado/one-or-two-things-you-may-not-know-about-typesystems
  • 34. Topics • Introduction Design patterns • Refactoring • Polyglot programming • SOLID principles © ASERT 2006-2010 • Other topics • More Info
  • 35. Language features instead of Patterns • So called "Design Patterns" are merely hacks to overcome the limitations of your language – You call that a language? © ASERT 2006-2010 – This is a language • "Design Patterns" are really anti-patterns you must sometimes put up with because your language is so archaic! • In my superior language, that would be built-in, simply a library, so easy, ...
  • 36. Language features instead of Patterns • So called "Design Patterns" are merely hacks to overcome the limitations of your language – You call that a language? © ASERT 2006-2010 – This is a language • "Design Patterns" are really anti-patterns you must sometimes put up with because your language is so archaic! • In my superior language, that would be built-in, simply a library, so easy, ...
  • 37. Adapter Pattern… class RoundPeg { def radius String toString() { "RoundPeg with radius $radius" } } class RoundHole { def radius def pegFits(peg) { peg.radius <= radius } String toString() { "RoundHole with radius $radius" } } © ASERT 2006-2010 def pretty(hole, peg) { if (hole.pegFits(peg)) println "$peg fits in $hole" else println "$peg does not fit in $hole" } def hole = new RoundHole(radius:4.0) (3..6).each { w -> pretty(hole, new RoundPeg(radius:w)) } RoundPeg with radius 3 fits in RoundHole with radius 4.0 RoundPeg with radius 4 fits in RoundHole with radius 4.0 RoundPeg with radius 5 does not fit in RoundHole with radius 4.0 RoundPeg with radius 6 does not fit in RoundHole with radius 4.0
  • 38. …Adapter Pattern… class SquarePeg { def width String toString() { "SquarePeg with width $width" } } class SquarePegAdapter { def peg def getRadius() { Math.sqrt(((peg.width/2) ** 2)*2) } String toString() { "SquarePegAdapter with width $peg.width (and notional radius $radius)" © ASERT 2006-2010 } } def hole = new RoundHole(radius:4.0) (4..7).each { w -> pretty(hole, new SquarePegAdapter(peg: new SquarePeg(width: w))) } SquarePegAdapter with width 4 (and notional radius 2.8284271247461903) fits in RoundHole with radius 4.0 SquarePegAdapter with width 5 (and notional radius 3.5355339059327378) fits in RoundHole with radius 4.0 SquarePegAdapter with width 6 (and notional radius 4.242640687119285) does not fit in RoundHole with radius 4.0 SquarePegAdapter with width 7 (and notional radius 4.949747468305833) does not fit in RoundHole with radius 4.0
  • 39. …Adapter Pattern SquarePeg.metaClass.getRadius = { Math.sqrt(((delegate.width/2)**2)*2) } (4..7).each { w -> pretty(hole, new SquarePeg(width:w)) } © ASERT 2006-2010 Adapter Pattern Do I create a whole new class or just add the method I need on the fly? Consider the Pros and Cons! SquarePeg with width 4 fits in RoundHole with radius 4.0 SquarePeg with width 5 fits in RoundHole with radius 4.0 SquarePeg with width 6 does not fit in RoundHole with radius 4.0 SquarePeg with width 7 does not fit in RoundHole with radius 4.0 Further reading: James Lyndsay, Agile is Groovy, Testing is Square
  • 40. Adapter Pattern Verdict • Dynamic languages can make it easier to apply the adapter pattern to the extent that its use may not even be apparent: – Express intent more clearly and improves readability – Aids refactoring – © ASERT 2006-2010 Can help with test creation – Avoids class proliferation • At the expense of class pollution? – But you still need testing
  • 41. Immutable Pattern... • Java Immutable Class boilerplate – As per Joshua Bloch // ... @Override Effective Java public boolean equals(Object obj) { if (this == obj) public final class Punter { return true; private final String first; if (obj == null) private final String last; return false; if (getClass() != obj.getClass()) public String getFirst() { return false; return first; Punter other = (Punter) obj; } if (first == null) { © ASERT 2006-2010 if (other.first != null) public String getLast() { return false; return last; } else if (!first.equals(other.first)) } return false; if (last == null) { @Override if (other.last != null) public int hashCode() { return false; final int prime = 31; } else if (!last.equals(other.last)) int result = 1; return false; result = prime * result + ((first == null) return true; ? 0 : first.hashCode()); } result = prime * result + ((last == null) ? 0 : last.hashCode()); @Override return result; public String toString() { } return "Punter(first:" + first + ", last:" + last + ")"; public Punter(String first, String last) { } this.first = first; this.last = last; } } // ... QCON 2010 - 41
  • 42. ...Immutable Pattern @Immutable class Punter { String first, last © ASERT 2006-2010 } QCON 2010 - 42
  • 43. Visitor Pattern abstract class Shape {} class Rectangle extends Shape { def x, y, width, height Visitor Pattern abstract class Shape { Rectangle(x, y, width, height) { def accept(Closure yield) { yield(this) } } without closures this.x = x; this.y = y; this.width = width; this.height = height } def union(rect) { if (!rect) return this def minx = [rect.x, x].min() with closures class Rectangle extends Shape { def maxx = [rect.x + width, x + width].max() def miny = [rect.y, y].min() def maxy = [rect.y + height, y + height].max() def x, y, w, h new Rectangle(minx, miny, maxx - minx, maxy - miny) } def bounds() { this } def accept(visitor) { def union(rect) { visitor.visit_rectangle(this) } if (!rect) return this } def minx = [rect.x, x].min() class Line extends Shape { def x1, y1, x2, y2 def maxx = [rect.x + w, x + w].max() Line(x1, y1, x2, y2) { def miny = [rect.y, y].min() this.x1 = x1; this.y1 = y1; this.x2 = x2; this.y2 = y2 } def maxy = [rect.y + h, y + h].max() def accept(visitor) { new Rectangle(x:minx, y:miny, w:maxx - minx, h:maxy - miny) visitor.visit_line(this) } } © ASERT 2006-2010 } } class Group extends Shape { def shapes = [] def add(shape) { shapes += shape } class Line extends Shape { def remove(shape) { shapes -= shape } def x1, y1, x2, y2 def accept(visitor) { def bounds() { visitor.visit_group(this) } new Rectangle(x:x1, y:y1, w:x2-y1, h:x2-y2) } } class BoundingRectangleVisitor { def bounds } def visit_rectangle(rectangle) { if (bounds) bounds = bounds.union(rectangle) class Group { else bounds = rectangle def shapes = [] } def leftShift(shape) { shapes += shape } def visit_line(line) { def line_bounds = new Rectangle(line.x1, line.y1, line.x2 - line.y1, line.x2 - line.y2) def accept(Closure yield) { shapes.each{it.accept(yield)} } if (bounds) bounds = bounds.union(line_bounds) } else bounds = line_bounds } def group = new Group() def visit_group(group) { group.shapes.each {shape -> shape.accept(this) } group << new Rectangle(x:100, y:40, w:10, h:5) } } group << new Rectangle(x:100, y:70, w:10, h:5) def group = new Group() group << new Line(x1:90, y1:30, x2:60, y2:5) group.add(new Rectangle(100, 40, 10, 5)) group.add(new Rectangle(100, 70, 10, 5)) def bounds group.add(new Line(90, 30, 60, 5)) def visitor = new BoundingRectangleVisitor() group.accept{ bounds = it.bounds().union(bounds) } group.accept(visitor) bounding_box = visitor.bounds println bounds.dump() println bounding_box.dump() See also Ruby Visitor
  • 44. Visitor Pattern Verdict • Dynamic languages can make it easier to apply the visitor pattern to the extent that its use may not even be apparent: – Express intent more clearly and improves readability – Aids refactoring – © ASERT 2006-2010 Avoids class proliferation – But you still need testing
  • 45. Strategy Pattern © ASERT 2006-2010 Source: http://nealford.com/
  • 46. Language features instead of Patterns… interface Calc { def execute(n, m) Strategy Pattern } with interfaces class CalcByMult implements Calc { with closures def execute(n, m) { n * m } } def multiplicationStrategies = [ class CalcByManyAdds implements Calc { def execute(n, m) { { n, m -> n * m }, def result = 0 { n, m -> n.times { def total = 0; n.times{ total += m }; total }, result += m { n, m -> ([m] * n).sum() } } ] © ASERT 2006-2010 return result } } def sampleData = [ [3, 4, 12], def sampleData = [ [5, -5, -25] [3, 4, 12], ] [5, -5, -25] ] sampleData.each{ data -> Calc[] multiplicationStrategies = [ multiplicationStrategies.each{ calc -> new CalcByMult(), assert data[2] == calc(data[0], data[1]) new CalcByManyAdds() } ] } sampleData.each {data -> multiplicationStrategies.each {calc -> assert data[2] == calc.execute(data[0], data[1]) } }
  • 47. Strategy Pattern Verdict • Dynamic languages can make it easier to apply the strategy pattern to the extent that its use may not even be apparent: – Express intent more clearly and improves readability – Closures open up whole new possibilities for solving problems © ASERT 2006-2010 – Aids refactoring – Can help with test creation – Avoids class proliferation – But you still need testing
  • 48. Builder Pattern: MarkupBuilder… • Builder pattern from the GoF at the syntax-level • Represents easily any nested tree-structured data import groovy.xml.* • Create new builder def b = new MarkupBuilder() b.html { • Call pretended methods © ASERT 2006-2010 head { title 'Hello' } (html, head, ...) body { • Arguments are Closures ul { for (count in 1..5) { • Builder code looks very li "world $count" declarative but is ordinary } } } } Groovy program code and can contain any kind of NodeBuilder, DomBuilder, logic SwingBuilder, AntBuilder, …
  • 49. ...Builder Pattern: MarkupBuilder <html> <head> import groovy.xml.* <title>Hello</title> def b = new MarkupBuilder() </head> b.html { <body> © ASERT 2006-2010 head { title 'Hello' } <ul> body { <li>world 1</li> ul { <li>world 2</li> for (count in 1..5) { <li>world 3</li> li "world $count" <li>world 4</li> } } } } <li>world 5</li> </ul> </body> </html>
  • 50. Builder Pattern: SwingBuilder import java.awt.FlowLayout builder = new groovy.swing.SwingBuilder() langs = ["Groovy", "Ruby", "Python", "Pnuts"] gui = builder.frame(size: [290, 100], title: 'Swinging with Groovy!’) { panel(layout: new FlowLayout()) { panel(layout: new FlowLayout()) { for (lang in langs) { © ASERT 2006-2010 checkBox(text: lang) } } button(text: 'Groovy Button', actionPerformed: { builder.optionPane(message: 'Indubitably Groovy!'). createDialog(null, 'Zen Message').show() }) button(text: 'Groovy Quit', actionPerformed: {System.exit(0)}) } } gui.show() Source: http://www.ibm.com/developerworks/java/library/j-pg04125/
  • 51. Builder Pattern: JavaFX Script Frame { title: "Hello World F3" width: 200 content: Label { text: "Hello World" © ASERT 2006-2010 } visible: true }
  • 52. Builder Pattern: Cheri::Swing # requires JRuby require 'rubygems' © ASERT 2006-2010 require 'cheri/swing' include Cheri::Swing @frame = swing.frame('Hello') { size 500,200 flow_layout on_window_closing {|event| @frame.dispose} button('Hit me') { on_click { puts 'button clicked' } } } @frame.show
  • 53. Builder Pattern: AntBuilder def ant = new AntBuilder() ant.echo("hello") // let's just call one task // create a block of Ant using the builder pattern ant.sequential { myDir = "target/test/" mkdir(dir: myDir) © ASERT 2006-2010 copy(todir: myDir) { fileset(dir: "src/test") { include(name: "**/*.groovy") } } echo("done") } // now let's do some normal Groovy again file = new File("target/test/AntTest.groovy") assert file.exists()
  • 54. Builder Pattern Verdict • The builder pattern in combination with dynamic languages helps me: – Express intent more clearly and improves readability – Aids refactoring – Can help with test creation – Tests are still important © ASERT 2006-2010
  • 55. Delegation Pattern ... • Traditional approach to creating a class that is an extension of another class is to use inheritance – Clearest intent & simplest, clearest code for simple cases class Person { private name, age Person(name, age) { this.name = name this.age = age © ASERT 2006-2010 } def haveBirthday() { age++ } String toString() { "$name is $age years old" } } class StaffMemberUsingInheritance extends Person { private salary StaffMemberUsingInheritance(name, age, salary) { super(name, age) this.salary = salary } String toString() { super.toString() + " and has a salary of $salary" } }
  • 56. … Delegation Pattern ... • Most common alternative is to use delegation – Intention less clear (can be helped with interfaces) – Overcomes multiple inheritance issues & inheritance abuse class StaffMemberUsingDelegation { private delegate private salary StaffMemberUsingDelegation(name, age, salary) { © ASERT 2006-2010 delegate = new Person(name, age) this.salary = salary } def haveBirthday() { delegate.haveBirthday() } String toString() { delegate.toString() + " and has a salary of $salary" } }
  • 57. … Delegation Pattern … • Downside of delegation is maintenance issues – Refactoring overhead if we change the base class – Meta-programming allows us to achieve inheritance like behavior by intercepting missing method calls (invokeMethod or method_missing) – You could take this further with Groovy using named © ASERT 2006-2010 parameters rather than the traditional positional parameters shown here (future versions of Ruby may have this too)
  • 58. … Delegation Pattern … class StaffMemberUsingMOP { private delegate private salary StaffMemberUsingMOP(name, age, salary) { delegate = new Person(name, age) this.salary = salary } def invokeMethod(String name, args) { delegate.invokeMethod name, args } © ASERT 2006-2010 String toString() { delegate.toString() + " and has a salary of $salary" } } def p1 = new StaffMemberUsingInheritance("Tom", 20, 1000) def p2 = new StaffMemberUsingDelegation("Dick", 25, 1100) def p3 = new StaffMemberUsingMOP("Harry", 30, 1200) p1.haveBirthday() println p1 p2.haveBirthday() Tom is 21 years old and has a salary of 1000 println p2 Dick is 26 years old and has a salary of 1100 p3.haveBirthday() Harry is 31 years old and has a salary of 1200 println p3
  • 59. … Delegation Pattern • Going Further –The example shown (on the previous slide) codes the delegate directly but both Groovy and Ruby let you encapsulate the delegation pattern as a library: • Groovy: Delegator, Injecto; Ruby: forwardable, delegate –But only if I don’t want to add logic as I delegate • E.g. If I wanted to make haveBirthday() increment salary © ASERT 2006-2010 class StaffMemberUsingLibrary { private salary private person StaffMemberUsingLibrary(name, age, salary) { person = new Person(name, age) this.salary = salary def delegator = new Delegator(StaffMemberUsingLibrary, person) delegator.delegate haveBirthday } String toString() { person.toString() + " and has a salary of $salary" } }
  • 60. Better Design Patterns: Delegate… public Date getWhen() { import java.util.Date; return when; } public class Event { private String title; public void setWhen(Date when) { private String url; this.when = when; private Date when; } public String getUrl() { public boolean before(Date other) { return url; return when.before(other); } © ASERT 2006-2010 } public void setUrl(String url) { public void setTime(long time) { this.url = url; when.setTime(time); } } public String getTitle() { public long getTime() { return title; return when.getTime(); } } public void setTitle(String title) { public boolean after(Date other) { this.title = title; return when.after(other); } } // ... // ... QCON 2010 - 60
  • 61. …Better Design Patterns: Delegate… public Date getWhen() { import java.util.Date; return when; boilerplate } public class Event { private String title; public void setWhen(Date when) { private String url; this.when = when; private Date when; } public String getUrl() { public boolean before(Date other) { return url; return when.before(other); } © ASERT 2006-2010 } public void setUrl(String url) { public void setTime(long time) { this.url = url; when.setTime(time); } } public String getTitle() { public long getTime() { return title; return when.getTime(); } } public void setTitle(String title) { public boolean after(Date other) { this.title = title; return when.after(other); } } // ... // ... QCON 2010 - 61
  • 62. …Better Design Patterns: Delegate class Event { String title, url @Delegate Date when } © ASERT 2006-2010 def gr8conf = new Event(title: "GR8 Conference", url: "http://www.gr8conf.org", when: Date.parse("yyyy/MM/dd", "2009/05/18")) def javaOne = new Event(title: "JavaOne", url: "http://java.sun.com/javaone/", when: Date.parse("yyyy/MM/dd", "2009/06/02")) assert gr8conf.before(javaOne.when) QCON 2010 - 62
  • 63. Delegation Pattern Verdict • The delegation pattern can be expressed more succinctly with dynamic languages: – Express intent more clearly and improves readability – Aids refactoring – But don’t forget the testing implications © ASERT 2006-2010
  • 64. Singleton Pattern… • Pattern Intent • Static language discussion points – Ensure that only one – Need exactly one instance of a class instance of a class is and a well-known controlled access created point • Allows for lazy creation of instance – Provide a global point of – More flexible than static class access to the object variables and methods alone • Permits refinement of operations and – Allow multiple instances representation through subclassing © ASERT 2006-2010 in the future without – Reduces name space clutter affecting a singleton • Compared to using static approach – Multi-threading implications class's clients – Serializable implications • need to have readResolve() method to avoid spurious copies – Garbage collection implications • May need "sticky" static self-reference – Need to be careful subclassing • Parent may already create instance or be final or constructor may be hidden
  • 65. …Singleton Pattern… • The details quickly get messy … public final class Singleton { private static final class SingletonHolder { static final Singleton singleton = new Singleton(); } private Singleton() {} public static Singleton getInstance() { return SingletonHolder.singleton; © ASERT 2006-2010 } } public class Singleton implements java.io.Serializable { public static Singleton INSTANCE = new Singleton(); protected Singleton() { // Exists only to thwart instantiation. } private Object readResolve() { return INSTANCE; } }
  • 66. …Singleton Pattern… • State of the art approach in Java? – Use an IoC framework, e.g. Spring or Guice import com.google.inject.* @ImplementedBy(CalculatorImpl) interface Calculator { def add(a, b) } © ASERT 2006-2010 @Singleton class CalculatorImpl implements Calculator { private total = 0 def add(a, b) { total++; a + b } def getTotalCalculations() { 'Total Calculations: ' + total } String toString() { 'Calc: ' + hashCode()} } class Client { @Inject Calculator calc // ... } def injector = Guice.createInjector()
  • 67. …Singleton Pattern… • But it is easy using meta-programming – Old style class Calculator { private total = 0 def add(a, b) { total++; a + b } © ASERT 2006-2010 def getTotalCalculations() { 'Total Calculations: ' + total } String toString() { 'Calc: ' + hashCode()} } class CalculatorMetaClass extends MetaClassImpl { private final static INSTANCE = new Calculator() CalculatorMetaClass() { super(Calculator) } def invokeConstructor(Object[] arguments) { return INSTANCE } } def registry = GroovySystem.metaClassRegistry registry.setMetaClass(Calculator, new CalculatorMetaClass())
  • 68. …Singleton Pattern… • But it is easy using meta-programming class Calculator { def total = 0 def add(a, b) { total++; a + b } } def INSTANCE = new Calculator() © ASERT 2006-2010 Calculator.metaClass.constructor = { -> INSTANCE } def c1 = new Calculator() def c2 = new Calculator() assert c1.add(1, 2) == 3 assert c2.add(3, 4) == 7 assert c1.is(c2) assert [c1, c2].total == [2, 2]
  • 69. …Singleton Pattern… • Or annotations @Singleton(lazy=true) class X { def getHello () { "Hello, World!" © ASERT 2006-2010 } } println X.instance.hello
  • 70. …Singleton Pattern… • And again with Ruby class Aardvark class Aardvark private_class_method :new private_class_method :new @@instance = new def Aardvark.instance def Aardvark.instance @@instance = new if not @@instance @@instance @@instance end end end end © ASERT 2006-2010 module ThreadSafeSingleton def self.append_features(clazz) require 'thread' clazz.module_eval { private_class_method :new @instance_mutex = Mutex.new def self.instance @instance_mutex.synchronize { @instance = new if not (@instance) @instance } end } end end Source: http://c2.com/cgi/wiki?RubySingleton
  • 71. …Singleton Pattern • Or for Python – Classic class version (pre 2.2) class Borg: _shared_state = {} def __init__(self): self.__dict__ = self._shared_state – Non-classic class version © ASERT 2006-2010 class Singleton (object): instance = None def __new__(cls, *args, **kargs): if cls.instance is None: cls.instance = object.__new__(cls, *args, **kargs) return cls.instance # Usage mySingleton1 = Singleton() mySingleton2 = Singleton() assert mySingleton1 is mySingleton2 Source: [10] and wikipedia
  • 72. Singleton Pattern Verdict • The singleton pattern can be expressed in better ways with dynamic languages: – Express intent more clearly and improves readability – Aids refactoring – But don’t forgot testing implications © ASERT 2006-2010
  • 73. Pattern Summary • Patterns can be replaced by language features and libraries © ASERT 2006-2010 • So patterns aren’t important any more! ...
  • 74. Topics • Introduction • Design patterns Refactoring • Polyglot programming • SOLID principles © ASERT 2006-2010 • Other topics • More Info
  • 75. Refactoring Refactoring • Out with the Old – Some refactorings no longer make sense • In with the New – There are some new refactorings • Times … they are a changin’ © ASERT 2006-2010 – Some refactorings are done differently
  • 76. Encapsulate Downcast Refactoring • Description – Context: A method returns an object that needs to be downcasted by its callers – Solution: Move the downcast to within the method • Is there a point in a dynamic language? – Maybe but not usually © ASERT 2006-2010 // Before refactoring Object lastReading() { return readings.lastElement() } // After refactoring Reading lastReading() { return (Reading) readings.lastElement() }
  • 77. Introduce Generics Refactoring • Description – Context: Casting is a runtime hack that allows JVM to clean up a mess caused by a compiler that couldn’t infer intent – Solution: Use Generics to reveal intent to compiler • Is there a point in a dynamic language? © ASERT 2006-2010 – Maybe but not usually // Traditional Java style List myIntList = new LinkedList() myIntList.add(new Integer(0)) Integer result = (Integer) myIntList.iterator().next() // Java generified style List<Integer> myIntList2 = new LinkedList<Integer>() myIntList2.add(new Integer(0)) Integer result2 = myIntList2.iterator().next() // Groovier style def myIntList3 = [0] def result3 = myIntList3.iterator().next()
  • 78. Enabling a functional style … • Consider the Maximum Segment Sum (MSS) problem – Take a list of integers; the MSS is the maximum of the sums of any number of adjacent integers • Imperative solution: def numbers = [31,-41,59,26,-53,58,97,-93,-23,84] © ASERT 2006-2010 def size = numbers.size() def max = null (0..<size).each { from -> (from..<size).each { to -> def sum = numbers[from..to].sum() if (max == null || sum > max) max = sum } } println "Maximum Segment Sum of $numbers is $max"
  • 79. … Enabling a functional style … • A first attempt at a more functional style: def numbers = [31,-41,59,26,-53,58,97,-93,-23,84] © ASERT 2006-2010 def size = numbers.size() def max = [0..<size, 0..<size].combinations().collect{ numbers[it[0]..it[1]].sum() }.max() println "Maximum Segment Sum of $numbers is $max"
  • 80. … Enabling a functional style … • An even more functional style – A known solution using functional composition: mss = max º sum* º (flatten º tails* º inits) – Where inits and tails are defined as follows: © ASERT 2006-2010 letters = ['a', 'b', 'c', 'd'] assert letters.inits() == [ assert letters.tails() == [ ['a'], ['d'], ['a', 'b'], ['c', 'd'], ['a', 'b', 'c'], ['b', 'c', 'd'], ['a', 'b', 'c', 'd'] ['a', 'b', 'c', 'd'] ] ]
  • 81. … Enabling a functional style • An even more functional style mss = max º sum* º (flatten º tails* º inits) def segs = { it.inits()*.tails().sum() } def solve = { segs(it)*.sum().max() } def numbers = [31,-41,59,26,-53,58,97,-93,-23,84] © ASERT 2006-2010 println "Maximum Segment Sum of $numbers is ${solve numbers}" Notes: – sum() is one-level flatten in Groovy, flatten() is recursive – Metaprogramming allowed us to enhance all Lists List.metaClass { inits{ (0..<delegate.size()).collect{ delegate[0..it] } } tails{ delegate.reverse().inits() } } Source: http://hamletdarcy.blogspot.com/2008/07/groovy-vs-f-showdown-side-by-side.html
  • 82. Refactoring recipes with a curry base • Static: Replace parameter with method – Refactoring [13]: Chapter 10 • Context – An object invokes a method, then passes the result as a parameter for a method. The receiver can also invoke this method. © ASERT 2006-2010 • Solution – Remove the parameter and let the receiver invoke the method. • Dynamic solution – Partial Application: Currying
  • 83. Replace parameter with method … class Order { private int quantity, itemPrice Let's explore the Order(q, p) {quantity = q; itemPrice = p} traditional refactoring double getPrice() { int basePrice = quantity * itemPrice int discountLevel if (quantity > 100) discountLevel = 2 else discountLevel = 1 © ASERT 2006-2010 double finalPrice = discountedPrice(basePrice, discountLevel) return finalPrice } private double discountedPrice(int basePrice, int discountLevel) { if (discountLevel == 2) return basePrice * 0.8 return basePrice * 0.9 } } println new Order(120, 5).price // => 480.0
  • 84. … Replace parameter with method … class Order { private int quantity, itemPrice Order(q, p) {quantity = q; itemPrice = p} double getPrice() { int basePrice = quantity * itemPrice int discountLevel if (quantity > 100) discountLevel = 2 else discountLevel = 1 © ASERT 2006-2010 double finalPrice = discountedPrice(basePrice, discountLevel) return finalPrice } private double discountedPrice(int basePrice, int discountLevel) { if (discountLevel == 2) return basePrice * 0.8 return basePrice * 0.9 } } println new Order(120, 5).price // => 480.0
  • 85. … Replace parameter with method … class Order { private int quantity, itemPrice Order(q, p) {quantity = q; itemPrice = p} double getPrice() { int basePrice = quantity * itemPrice double finalPrice = discountedPrice(basePrice) return finalPrice } © ASERT 2006-2010 private double discountedPrice(int basePrice) { if (getDiscountLevel() == 2) return basePrice * 0.8 return basePrice * 0.9 } private int getDiscountLevel() { if (quantity > 100) return 2 return 1 } } println new Order(120, 5).price // => 480.0
  • 86. … Replace parameter with method … class Order { private int quantity, itemPrice Order(q, p) {quantity = q; itemPrice = p} double getPrice() { int basePrice = quantity * itemPrice double finalPrice = discountedPrice(basePrice) return finalPrice } © ASERT 2006-2010 private double discountedPrice(int basePrice) { if (getDiscountLevel() == 2) return basePrice * 0.8 return basePrice * 0.9 } private int getDiscountLevel() { if (quantity > 100) return 2 return 1 } } println new Order(120, 5).price // => 480.0
  • 87. … Replace parameter with method … class Order { private int quantity, itemPrice Order(q, p) {quantity = q; itemPrice = p} double getPrice() { return discountedPrice(getBasePrice()) } private double discountedPrice(int basePrice) { © ASERT 2006-2010 if (getDiscountLevel() == 2) return basePrice * 0.8 return basePrice * 0.9 } private int getBasePrice() { quantity * itemPrice } private int getDiscountLevel() { if (quantity > 100) return 2 return 1 } } println new Order(120, 5).price // => 480.0
  • 88. … Replace parameter with method … class Order { private int quantity, itemPrice Order(q, p) {quantity = q; itemPrice = p} double getPrice() { return discountedPrice(getBasePrice()) } private double discountedPrice(int basePrice) { © ASERT 2006-2010 if (getDiscountLevel() == 2) return basePrice * 0.8 return basePrice * 0.9 } private int getBasePrice() { quantity * itemPrice } private int getDiscountLevel() { if (quantity > 100) return 2 return 1 } } println new Order(120, 5).price // => 480.0
  • 89. … Replace parameter with method … class Order { private int quantity, itemPrice Order(q, p) {quantity = q; itemPrice = p} double getPrice() { return discountedPrice() } private double discountedPrice() { © ASERT 2006-2010 if (getDiscountLevel() == 2) return getBasePrice() * 0.8 return getBasePrice() * 0.9 } private int getBasePrice() { quantity * itemPrice } private int getDiscountLevel() { if (quantity > 100) return 2 return 1 } } println new Order(120, 5).price // => 480.0
  • 90. … Replace parameter with method … class Order { private int quantity, itemPrice Order(q, p) {quantity = q; itemPrice = p} double getPrice() { return discountedPrice() } private double discountedPrice() { © ASERT 2006-2010 if (getDiscountLevel() == 2) return getBasePrice() * 0.8 return getBasePrice() * 0.9 } private int getBasePrice() { quantity * itemPrice } private int getDiscountLevel() { if (quantity > 100) return 2 return 1 } } println new Order(120, 5).price // => 480.0
  • 91. … Replace parameter with method class Order { private int quantity, itemPrice Order(q, p) {quantity = q; itemPrice = p} double getPrice() { if (getDiscountLevel() == 2) return getBasePrice() * 0.8 return getBasePrice() * 0.9 } © ASERT 2006-2010 private getBasePrice() { quantity * itemPrice } private getDiscountLevel() { if (quantity > 100) return 2 Note the now small return 1 parameter lists } } println new Order(120, 5).price // => 480.0
  • 92. Some functional style … class Order { private int quantity, itemPrice Order(q, p) {quantity = q; itemPrice = p} def discountedPrice = { basePrice, discountLevel -> discountLevel == 2 ? basePrice * 0.8 : basePrice * 0.9 } def price = { int basePrice = quantity * itemPrice © ASERT 2006-2010 def discountLevel = (quantity > 100) ? 2 : 1 discountedPrice(basePrice, discountLevel) } } println new Order(120, 5).price() // => 480.0 Traditional refactoring still applicable if we used closures rather than methods...
  • 93. … Some functional style … class Order { private int quantity, itemPrice Order(q, p) {quantity = q; itemPrice = p} def basePrice = { quantity * itemPrice } def discountLevel = { quantity > 100 ? 2 : 1 } def price = { © ASERT 2006-2010 discountLevel() == 2 ? basePrice() * 0.8 : basePrice() * 0.9 } } println new Order(120, 5).price() // => 480.0 ... as we see here
  • 94. … Some functional style … class Order { private int quantity, itemPrice Order(q, p) {quantity = q; itemPrice = p} def basePrice = { quantity * itemPrice } def discountLevel = { quantity > 100 ? 2 : 1 } def discountedPrice = { basePrice, discountLevel -> © ASERT 2006-2010 discountLevel == 2 ? basePrice * 0.8 : basePrice * 0.9 } def price = { discountedPrice.curry(basePrice()).curry(discountLevel()).call() } } println new Order(120, 5).price() // => 480.0 But we can also use currying
  • 95. … Some functional style class Order { private int quantity, itemPrice Order(q, p) {quantity = q; itemPrice = p} def basePrice = { quantity * itemPrice } def discountLevel = { quantity > 100 ? 2 : 1 } def discountedPrice(basePrice, discountLevel) { © ASERT 2006-2010 discountLevel == 2 ? basePrice * 0.8 : basePrice * 0.9 } def price = { this.&discountedPrice.curry(basePrice()).curry(discountLevel()).call() } } println new Order(120, 5).price() // => 480.0 We can also use currying with methods
  • 96. Closure Refactoring … • Complex code involving closures // Before refactoring def phrase = "The quick brown fox jumps over the lazy dog" def result = phrase.toLowerCase().toList(). findAll{ it in "aeiou".toList() }. // like WHERE ... © ASERT 2006-2010 groupBy{ it }. // like GROUP BY ... findAll{ it.value.size() > 1 }. // like HAVING ... sort{ it.key }.reverse(). // like ORDER BY ... collect{ "$it.key:${it.value.size()}" }. join(", ") println result
  • 97. … Closure Refactoring … • Possible Refactoring // Refactored helper closures def lowercaseLetters = phrase.toLowerCase() def vowels = { it in "aeiou".toList() } def occursMoreThanOnce = { it.value.size() > 1 } def byReverseKey = { a, b -> b.key <=> a.key } def self = { it } © ASERT 2006-2010 def entriesAsPrettyString = { "$it.key:${it.value.size()}" } def withCommaDelimiter = ", " // Refactored main closure println lowercaseLetters. findAll(vowels). groupBy(self). findAll(occursMoreThanOnce). sort(byReverseKey). collect(entriesAsPrettyString). join(withCommaDelimiter)
  • 98. … Closure Refactoring # Add group_by to the Array class class Array def group_by group_hash = {} uniq.each do |e| group_hash[e] = select { |i| i == e }.size end group_hash end end # Before refactoring phrase = "The quick brown fox jumps over the lazy dog" © ASERT 2006-2010 puts phrase.downcase. scan(/[aeiou]/). # like WHERE ... group_by. # like GROUP BY ... select { |key, value| value > 1 }. # like HAVING ... sort.reverse. # like ORDER BY ... DESC collect{ |key, value| "#{key}:#{value}" }.join(', ') # Refactored version lowercase_letters = phrase.downcase vowels = /[aeiou]/ occurs_more_than_once = lambda { |key,value| value > 1 } entries_as_pretty_string = lambda { |key, value| "#{key}:#{value}" } puts lowercase_letters. scan(vowels). group_by. select(&occurs_more_than_once). sort.reverse. collect(&entries_as_pretty_string).join(', ')
  • 99. Unnecessary Complexity Refactoring • Dynamic Code Creation – What to look for: Code uses eval, class_eval or module_eval to build new code dynamically – Issues: harder to read, fluid abstractions are harder to understand, harder to test © ASERT 2006-2010 and debug – What to do: • move string form of eval to block forms or use define_method • move method_missing to use class_eval (example of Replace Dynamic Receptor with Dynamic Method Definition) • consider using Move Eval from Run-time to Parse-time to overcome bottlenecks Source: Dynamic Code Creation in Chapter 7: Unnecessary Complexity (Refactoring in Ruby)
  • 100. Topics • Introduction • Design patterns • Refactoring Polyglot programming • SOLID principles © ASERT 2006-2010 • Other topics • More Info
  • 101. Programming Paradigms... • Named state (imperative style – leads to modularity) vs unnamed state http://www.info.ucl.ac.be/~pvr/paradigms.html (functional and logic style) • Deterministic vs © ASERT 2006-2010 observable nondeterminism (threads, guards) • Sequential vs concurrent (message passing and shared state styles)
  • 102. © ASERT 2006-2010 ...Programming Paradigms http://www.info.ucl.ac.be/~pvr/paradigms.html
  • 103. Polyglot Programming… • Groovy calling clojure @Grab('org.clojure:clojure:1.0.0') import clojure.lang.Compiler import clojure.lang.RT def src = new File('temp.clj') src.text = ''' © ASERT 2006-2010 (ns groovy) (defn factorial [n] (if (< n 2) 1 (* n (factorial (- n 1))))) ''' src.withReader { reader -> Compiler.load reader } def fac = RT.var('groovy', 'factorial') println fac.invoke(5)
  • 104. …Polyglot Programming • C# calling F# // F# Code type FCallback = delegate of int*int -> int;; type FCallback = delegate of int * int -> int © ASERT 2006-2010 let f3 (f:FCallback) a b = f.Invoke(a,b);; val f3 : FCallback -> int -> int -> int // C# Code // method gets converted to the delegate automatically in C# int a = Module1.f3(Module1.f2, 10, 20);
  • 105. Topics • Introduction • Design patterns • Refactoring • Polyglot programming SOLID principles © ASERT 2006-2010 • Other topics • More Info
  • 107. SOLID Principles • Single Responsibility Principle • Open/Closed Principle • Liskov Substitution Principle © ASERT 2006-2010 • Interface Segregation Principle • Dependency Inversion Principle
  • 109. Open-Closed Principle... • Fundamental rule to make your software flexible – Many other OOP principles, methodologies and conventions revolve around this principle • Open-Closed Principle (OCP) states: • Software entities should be open for © ASERT 2006-2010 extension, but closed for modification • References – Bertrand Meyer, Object Oriented Software Construction (88, 97) – Robert C Martin, The Open-Closed Principle – Craig Larman, Protected Variation: The Importance of Being Closed Picture source: http://www.vitalygorn.com
  • 110. ...Open-Closed Principle... • Following the Rules – Encapsulation: Make anything that shouldn’t be seen private – Polymorphism: Force things to be handled using abstract classes or interfaces • When making class hierarchies: © ASERT 2006-2010 – Make anything that shouldn’t be open final – Polymorphism: Always follow weaker pre stronger post (object substitutability in the static world) • When making changes that might break existing clients – Add a new class into the hierarchy – No compilation of existing code! No breakages!
  • 111. ...Open-Closed Principle... • Part I: If I violate the Open part of OCP in static languages – I can’t make the future enhancements I need • Part II: If I violate the Closed part of OCP – Client applications using my libraries might © ASERT 2006-2010 break or require recompilation in the future Class A Extendible Class A Interface User Class A User Class A’ Class A’ User Class A’ User Optional Optional Class A Class A’ Factory Factory ...
  • 112. ...Open-Closed Principle... • Part I: Consider Java’s String class – Has methods to convert to upper or lower case but no swapCase() method? – Traditionally, consider creating an EnhancedString class using inheritance? – I can’t: String is immutable and final © ASERT 2006-2010 • In OCP terms, it is not open for extension • Dynamic language solution: open classes String.metaClass.swapCase = { #light delegate.collect{ c -> open String type System.String with c in 'A'..'Z' ? member x.swapCase = c.toLowerCase() : seq { for letter in x.ToCharArray() do if (System.Char.IsLower(letter)) c.toUpperCase() then yield System.Char.ToUpper(letter) }.join() else yield System.Char.ToLower(letter) } } printfn "result: %A" "Foo".swapCase assert "Foo".swapCase() == "fOO" ...
  • 113. ...Open-Closed Principle... • Part II: Violating OCP (see [15]) class Square { def side } class Circle { def radius } © ASERT 2006-2010 class AreaCalculator { double area(shape) { switch (shape) { case Square: return shape.side * shape.side case Circle: return Math.PI * shape.radius ** 2 } } }
  • 114. ...Open-Closed Principle... def shapes = [ new Square(side: 3), new Square(side: 2), new Circle(radius: 1.5) ] def calc = new AreaCalculator() shapes.sort().each {s -> println "Area of $s.class.name is ${calc.area(s)}" © ASERT 2006-2010 } • What’s wrong – If we wanted to introduce a Triangle, the AreaCalculator would need to be recompiled – If we wanted to change the order the shape information was displayed, there might be many changes to make
  • 115. ...Open-Closed Principle... * Our abstractions never designed sorting to be • Dynamic shapes one of the things open for extension. See [15]. – No issue with adding Triangle but sorting is an issue * class Square { Note: Duck-type private side polymorphism double area() { side ** 2 } instead of } inheritance class Circle { polymorphism, © ASERT 2006-2010 private radius i.e. no base Shape double area() { Math.PI * radius ** 2 } (abstract) class or } interface. Hmm… what are def shapes = [ the testing new Square(side:3), implications when new Square(side:2), I add Triangle? new Circle(radius:1.5) Area of Square is 9.0 ] Area of Square is 4.0 // unsorted Area of Circle is 7.0685834705770345 def prettyPrint = { s -> println "Area of $s.class.name is ${s.area()}" } shapes.each(prettyPrint) ...
  • 116. ...Open-Closed Principle... • Dynamic sorting using Closures – As long as we are happy having our sort “code” within a closure we have complete freedom – Sometimes representing our abstractions within classes is appropriate; many times closures will do Area of Square is 4.0 © ASERT 2006-2010 Area of Circle is 7.0685834705770345 // sorted by area Area of Square is 9.0 def byArea = { s -> s.area() } shapes.sort(byArea).each(prettyPrint) Note: Make sure your closures are testable. // sorted circles before squares but otherwise by area def byClassNameThenArea = { sa, sb -> sa.class.name == sb.class.name ? Area of Circle is 7.06858... sa.area() <=> sb.area() : Area of Square is 4.0 Area of Square is 9.0 sa.class.name <=> sb.class.name } shapes.sort(byClassNameThenArea).each(prettyPrint) ...
  • 117. ...Open-Closed Principle... • Instead of worrying about – Rigidity – Fragility – Immobility (Because they can be easily gotten around even if you don’t try to apply OCP) © ASERT 2006-2010 • We must worry about – Duplication – Harder refactoring or testing – Feature interaction • And of course OCP then leads to ... – Liskov Substitution Principle, Single Responsibility Principle, Dependency Inversion Principle, ...
  • 118. ...Open-Closed Principle... • “Clean code” [23] states it this way: – Procedural code (i.e. using data structures) makes it easy to add new functions without changing existing data structures but when new data structures are added, all existing procedures may need to change – OO code makes it easy to add new classes without © ASERT 2006-2010 changing existing functions but when new functions are added, all classes must change • Recommendation? – Choose procedural or OO approach based on whether anticipated evolution of system involves functions or data – Use Visitor (dual dispatch) Pattern if you think both functions and data might change
  • 119. ...Open-Closed Principle... class Square { double side } class Rectangle { double height, width } class Circle { © ASERT 2006-2010 double radius } class Geometry { def area(shape) { switch (shape) { case Square: return shape.side ** 2 case Rectangle: return shape.height * shape.width case Circle: return PI * shape.radius ** 2 } } Can add perimeter() here without shape classes changing but if we } added a Triangle, area(), perimeter() etc. would need to change.
  • 120. ...Open-Closed Principle... interface Shape { If we add perimeter() here, each double area() shape class must change but we can } add new shapes with no changes class Square implements Shape { double side double area() { side ** 2 } } © ASERT 2006-2010 class Rectangle implements Shape { double height, width double area() { height * width } } class Circle implements Shape { double radius double area() { PI * radius ** 2 } }
  • 121. ...Open-Closed Principle... class Square { double side double area() { side ** 2 } } We can easily add perimeter() here but for any code requiring the perimeter() method to exist, we should test that code class Rectangle { with all shapes. double height, width double area() { height * width } } © ASERT 2006-2010 class Circle { double radius double area() { PI * radius ** 2 } }
  • 122. ...Open-Closed Principle... • “Clean code” [23] recommendation: – Choose procedural or OO approach or Visitor • Agile variation: – Defer moving to complicated solutions, e.g. Visitor Pattern, but have in place sufficient tests so that you can confidently refactor to use one later if needed © ASERT 2006-2010 • Dynamic language variation: – You won’t need an explicit visitor (more on this later) – Duck typing lets you add functions or data without changing existing classes at the expense of static type safety – If you add a function you might need additional tests for each class associated with that function – If you add a new class you might need additional tests for each function associated with that class
  • 123. Topics • Introduction • Design patterns • Refactoring • Polyglot programming • SOLID principles © ASERT 2006-2010 Other topics • More Info
  • 124. Other topics • The need for Dependency Injection • The need for Mocking frameworks • Concurrency • Feature interaction • Writing DSLs © ASERT 2006-2010
  • 125. Topics • Introduction • Design patterns • Refactoring • Polyglot programming • SOLID principles © ASERT 2006-2010 • Other topics More Info
  • 126. Further Information… • [1] Dynamic vs. Static Typing — A Pattern-Based Analysis, Pascal Costanza, University of Bonn, 2004 http://p-cos.net/documents/dynatype.pdf • [2] Interface-Oriented Design, Ken Pugh, Pragmatic Programmers, 2006 • [3] Bruce Eckel, Does Java need Checked Exceptions? www.mindview.net/Etc/Discussions/CheckedExceptions • [4] Null Object, Kevlin Henney, Proceedings EuroPLoP 2002 • [5] Design Patterns in Dynamic Programming, Peter Norvig, March 1998 http://www.norvig.com/design-patterns/ • [6] Advanced Programming Language Features and Software Engineering: © ASERT 2006-2010 Friend or Foe?, Greg Sullivan, April 2002 http://people.csail.mit.edu/gregs/proglangsandsofteng.pdf • [7] JunGL: a Scripting Language for Refactoring, Mathieu Verbaere et al, May 2006 http://progtools.comlab.ox.ac.uk/publications/icse06jungl • [8] Rails for Java Developers, Halloway et al, Pragmatic Bookshelf, 2007, Chapter 3, Ruby Eye for the Java Guy • [9] Building DSLs in Static & Dynamic languages http://www.nealford.com/downloads/conferences/canonical/Neal_Ford- Building_DSLs_in_Static_and_Dynamic_Languages-handouts.pdf • [10] Five Easy Pieces: Simple Python Non-Patterns, Alex Martelli http://www.aleax.it/5ep.html • [11] Emergent Design, Scott L. Bain, 2008
  • 127. …Further Information • [12] Design Patterns: Elements of Reusable Object-Oriented Software, Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides, 1995 • [13] Refactoring: Improving the Design of Existing Code, Martin Fowler, 1999 • [14] Effective Java Programming Language Guide, Erich Gamma, Joshua Bloch, 2001 • [15] Agile Software Development, Principles, Patterns, and Practices, Robert C Martin, 2002 • [16] Composing Features and Resolving Interactions, Jonathan Hay and Joanne Atlee, University of Waterloo • [17] Handling Feature Interactions in the Language for End System Services, © ASERT 2006-2010 Xiaotao Wua and Henning Schulzrinne, January 2007 • [18] FAQ Sheet on Feature Interaction, Pamela Zave http://www.research.att.com/~pamela/faq.html • [19] Liskov Substitution Principle and the Ruby Core Libraries, Dean Wampler http://blog.objectmentor.com/articles/tag/liskovsubstitutionprinciple • [20] Liskov Substitution in Dynamic Languages, Michael Feathers http://butunclebob.com/ArticleS.MichaelFeathers.LiskovSubstitutionInDynamicLanguages • [21] Domain-Specific Languages: An Annotated Bibliography, van Deursen et al http://homepages.cwi.nl/~arie/papers/dslbib/ • [22] Agile Principles, Patterns, and Practices in C#, Martin C. Robert et al, 2006 • [23] Clean Code, Robert C. Martin, 2008 • [24] The Craftsman: 51, Ruby Visitor, Robert C Martin, August 2007 http://www.objectmentor.com/resources/articles/Craftsman51.pdf • [25] http://www.info.ucl.ac.be/~pvr/paradigms.html • [26] http://p-cos.net/documents/dynatype.pdf