SlideShare une entreprise Scribd logo
1  sur  146
Télécharger pour lire hors ligne
Dynamic Language
Practices
“Unlearning Java/C#”
Dr Paul King - ASERT
Introduction …
                      • Developer practices
                        – Well understood and documented for
                          traditional and agile approaches such
                          as Java, C++ and C# development
                        – But dynamic languages like Groovy,
                          Ruby, Python, Boo, JavaScript and
(c) ASERT 2006-2009




                          others change the ground rules
                        – Many of the rules and patterns we
                          have been taught no longer apply
… 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 Language
(c) ASERT 2006-2009




                          Guide. Prentice Hall.
                        – Robert C Martin (2002), Agile Software Development, Principles,
                          Patterns, and Practices. Prentice Hall.
                      • In the dynamic
                        language world,
                        are these guidelines
                        FACT or MYTH !!
Examples to ponder
                      • What does Immutability mean?
                        – When even constants can be changed
                      • What does encapsulation
                        mean?
                        – When I can peek at anything
(c) ASERT 2006-2009




                          under the covers
                      • How can I devise tests
                        at development time?
                        – When my system can change in
                          unknown ways at runtime
                      • How can IDEs help me?
                        – If I no longer spoon feed it static-typing information
                          or if my language now only allows checks at runtime
What do I mean by Dynamic Language?
                      • I prefer a loose definition
                      • One or more of:
                        – Dynamic typing
                           • Greater polymorphism
                        – Metaprogramming
                           • Allow language itself to be dynamically changed
(c) ASERT 2006-2009




                           • 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 …
                      • The Debate
                        – Static vs dynamic typing
                         • Static: the type of each variable
                           (or expression) must be known
                           at compile time
                           like wearing a straight-jacket?
                         • Dynamic: type information is
(c) ASERT 2006-2009




                           known only at runtime
                           like tightrope walking with no net?
                        – Strong vs weak typing
                         • Strong: List<Integer> myList
                         • Weak: Object myList
                        – Type safety
                         • How is this provided if at all?
                        – Type inference
                         • Is this supported?
…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
(c) ASERT 2006-2009




                        – 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 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
(c) ASERT 2006-2009




                        – 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 …
                      • 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.
(c) ASERT 2006-2009
… Static vs Dynamic Typing …
                      • An example
                                      interface Reversible {
                                          def reverse()
                                           }

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

                                           class ReversibleArray implements Reversible {
                                               def reverse() { /* */ }
(c) ASERT 2006-2009




                                ???        }

                                           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
(c) ASERT 2006-2009




                                               }
                      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 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.
(c) ASERT 2006-2009




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

                                              ...dynamic languages certainly
                                             assist with removing duplication
                                             and sometimes removing clutter
                                                   and boilerplate code...
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()
(c) ASERT 2006-2009




                       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]
…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
(c) ASERT 2006-2009




                                                                                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 and IDEs…
                      • Class A has a bit of duplication

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




                                         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() {
(c) ASERT 2006-2009




                                          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',
(c) ASERT 2006-2009




                                         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"
                          }
                      }
(c) ASERT 2006-2009




                      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?
(c) ASERT 2006-2009




                              But remember:
                          “clearly express intent”
                                                              ...
Language features instead of Patterns …
                      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" }
                      }
(c) ASERT 2006-2009




                      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
…Language features instead of Patterns…
                      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)"
(c) ASERT 2006-2009




                        }
                      }

                      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
… Language features instead of Patterns …


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

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

                                                                                   Adapter Pattern
(c) ASERT 2006-2009




                                                                                   Do I create a whole
                                                                                   new class or just add
                                                                                   the method I need
                                                                                   on the fly?


                        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
                        –
(c) ASERT 2006-2009




                            Can help with test creation
                        –   Avoids class proliferation
                        –   But you still need testing
… Language features instead of Patterns
                      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)
                          }                                                                                               }
(c) ASERT 2006-2009




                      }
                                                                                                                      }
                      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 [24]
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
                        –
(c) ASERT 2006-2009




                            Avoids class proliferation
                        –   But you still need testing
Strategy Pattern
(c) ASERT 2006-2009




    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 {
                                                                  { n, m -> n * m },
                        def execute(n, m) {
                          def result = 0                          { n, m ->
                          n.times {                                 def total = 0; n.times{ total += m }; total },
                            result += m                           { n, m -> ([m] * n).sum() }
(c) ASERT 2006-2009




                          }                                     ]
                          return result
                        }
                                                                def sampleData = [
                      }
                                                                  [3, 4, 12],
                      def sampleData = [                          [5, -5, -25]
                          [3, 4, 12],                           ]
                          [5, -5, -25]
                      ]                                         sampleData.each{ data ->
                                                                  multiplicationStrategies.each{ calc ->
                      Calc[] multiplicationStrategies = [
                          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
(c) ASERT 2006-2009




                        – Aids refactoring
                        – Can help with test creation
                        – Avoids class proliferation
                        – But you still need testing
… Language features instead of Patterns …
                       • 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
(c) ASERT 2006-2009




                        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, …
… Language features instead of Patterns


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




                        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>
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) {
(c) ASERT 2006-2009




                                       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/
JavaFX Script

                      Frame {
                         title: "Hello World F3"
                         width: 200
                         content: Label {
                            text: "Hello World"
(c) ASERT 2006-2009




                         }
                         visible: true
                      }
Cheri::Swing




                      # requires JRuby
                      require 'rubygems'
(c) ASERT 2006-2009




                      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
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)
(c) ASERT 2006-2009




                          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
(c) ASERT 2006-2009
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
(c) ASERT 2006-2009




                                 }
                                 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
(c) ASERT 2006-2009




                             StaffMemberUsingDelegation(name, age, salary) {
                                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
(c) ASERT 2006-2009




                          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
                         }
(c) ASERT 2006-2009




                         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
(c) ASERT 2006-2009




                            • E.g. If I wanted to make haveBirthday() increment salary
                        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"
                            }
                        }
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
(c) ASERT 2006-2009
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
(c) ASERT 2006-2009




                                                            representation through subclassing
                        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;
(c) ASERT 2006-2009




                          }
                      }

                       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)
                      }
(c) ASERT 2006-2009




                      @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 }
(c) ASERT 2006-2009




                           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()
(c) ASERT 2006-2009




                        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…
                      • 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
(c) ASERT 2006-2009




                            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
(c) ASERT 2006-2009




                                     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
(c) ASERT 2006-2009
Pattern Summary
                      • Patterns can be replaced by language
                        features and libraries
(c) ASERT 2006-2009




                      • So patterns aren‟t important any more!




                                                                 ...
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‟
(c) ASERT 2006-2009




                        – 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
(c) ASERT 2006-2009




                                // 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?
(c) ASERT 2006-2009




                        – 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:
(c) ASERT 2006-2009




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

                              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]
(c) ASERT 2006-2009




                          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:
(c) ASERT 2006-2009




                                    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]
(c) ASERT 2006-2009




                        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.
(c) ASERT 2006-2009




                      • 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
                        Order(q, p) {quantity = q; itemPrice = p}

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




                            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
(c) ASERT 2006-2009




                            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
                        }
(c) ASERT 2006-2009




                        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
                        }
(c) ASERT 2006-2009




                        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) {
(c) ASERT 2006-2009




                          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) {
(c) ASERT 2006-2009




                          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() {
(c) ASERT 2006-2009




                          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() {
(c) ASERT 2006-2009




                          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
                        }
(c) ASERT 2006-2009




                        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
(c) ASERT 2006-2009




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

                      }
                      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 basePrice = { quantity * itemPrice }

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

                        def price = {
(c) ASERT 2006-2009




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

                      }
                      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 basePrice = { quantity * itemPrice }

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

                        def discountedPrice = { basePrice, discountLevel ->
(c) ASERT 2006-2009




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

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

                      }
                      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 basePrice = { quantity * itemPrice }

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

                        def discountedPrice(basePrice, discountLevel) {
(c) ASERT 2006-2009




                          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
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 ...
(c) ASERT 2006-2009




                        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 }
(c) ASERT 2006-2009




                      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"
(c) ASERT 2006-2009




                      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(', ')
• This is the end of the talk




                        (c) ASERT 2006-2009
• This is the end of the talk




• NO!
• We haven’t questioned some fundamental
  principles yet!


                        (c) ASERT 2006-2009
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:
(c) ASERT 2006-2009




                        • Software entities should be open for
                          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:
(c) ASERT 2006-2009




                        – 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
(c) ASERT 2006-2009




                           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
(c) ASERT 2006-2009




                               • In OCP terms, it is not open for extension

                       • Dynamic language solution: open classes
                                                                           String.metaClass.swapCase = {
                      #light
                      open String
                                                                             delegate.collect{ c ->
                      type System.String with                                  c in 'A'..'Z' ?
                        member x.swapCase =
                          seq { for letter in x.ToCharArray() do
                                                                                 c.toLowerCase() :
                                  if (System.Char.IsLower(letter))               c.toUpperCase()
                                  then yield System.Char.ToUpper(letter)
                                  else yield System.Char.ToLower(letter)
                                                                             }.join()
                          }                                                }
                      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
                        }
(c) ASERT 2006-2009




                        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)}"
(c) ASERT 2006-2009




                        }


                      • 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,
(c) ASERT 2006-2009




                          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
(c) ASERT 2006-2009




                                                               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)
(c) ASERT 2006-2009




                      • 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
(c) ASERT 2006-2009




                          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 {
(c) ASERT 2006-2009




                        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 }
                      }
(c) ASERT 2006-2009




                      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 }
                      }
(c) ASERT 2006-2009




                      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
(c) ASERT 2006-2009




                      • 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
...Open-Closed Principle
                      • Sometimes referred to as the Expression
                        Problem:
                        – Independently Extensible Solutions to the Expression Problem
                          by Matthias Zenger Martin Odersky
                        – http://www.scala-lang.org/docu/files/
                          IC_TECH_REPORT_200433.pdf
(c) ASERT 2006-2009
• This is the end of the talk




                        (c) ASERT 2006-2009
• This is the end of the talk




• NO!
• We haven’t looked at Advanced Topics yet
  including Aspects, Testability, Feature Interaction
  and DSLs!

                        (c) ASERT 2006-2009
What & why of dependency injection?
                      • Construction by hand
                                                       Loose
                                                      Coupling

                      • Factory pattern               Flexibility
(c) ASERT 2006-2009




                                                      Testability

                                                     Reusability
                      • Service locator               Lifecycle
                                                       Control

                                                       Central

                      • Dependency injection           Control
Dependency Injection vs Metaprogramming …
                      • Dependency Injection
                        – Dependencies are explicitly declared and allowed to
                          be set externally (typically via constructor or setters)
                           • Transparent injection of dependent service objects into other
                             service objects by a controlling container hence the name
                             inversion of control

                      • Why?
(c) ASERT 2006-2009




                        – More flexible
                           • Central configuration of service objects
                        – Can be less work to do
                           • Service objects are instantiated by the dependency injection
                             framework
                        – Improves testability
                        – Improves reusability
                        – Improved lifecycle control
… Dependency Injection vs Metaprogramming …
                      • Without dependency injection

                      class PrintService {
                         private printer = new PhysicalPrinter('Canon i9900')
                      }
                      class PrintServiceTest extends GroovyTestCase {
                         def testPrintService() {
(c) ASERT 2006-2009




                            def printService = new PrintService()
                            printService.print()                     class PrintService
                            // go to the printer and fetch the page     def initialise
                         }                                                 @printer =
                      }                                                       PhysicalPrinter.new('Canon i9900')
                                                                        end
                                                                     end
                                                                     class PrintServiceTest < Test::Unit
                                                                        def test_print_service
                                                                           print_service = PrinterService.new
                                                                           print_service.print
                                                                           # go to the printer and fetch the page
                                                                        end
                                                                     end
… Dependency Injection vs Metaprogramming …
                      • Improves testability
                      class TestablePrintService {
                         def printer
                      }
                      class TestablePrintServiceTest extends GroovyTestCase {
                         def testPrintService() {
                            def printService = new TestablePrintService()
                            printService.printer = new StubPrinter()
(c) ASERT 2006-2009




                            printService.print()
                            //...
                         }                                 class TestablePrintService
                      }                                       attr_accessor :printer
                                                           end
                                                           class TestablePrintServiceTest < Test::Unit::TestCase
                                                              def test_print_service
                                                                 print_service = TestablePrintService.new
                                                                 print_service.printer = StubPrinter.new
                                                                 print_service.print
                                                                 #...
                                                              end
                                                           end
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp

Contenu connexe

Similaire à Paulking dlp

Dynamic Language Practices
Dynamic Language PracticesDynamic Language Practices
Dynamic Language PracticesPaul King
 
Converging Textual and Graphical Editors
Converging Textual  and Graphical EditorsConverging Textual  and Graphical Editors
Converging Textual and Graphical Editorsmeysholdt
 
Safe and Reliable Embedded Linux Programming: How to Get There
Safe and Reliable Embedded Linux Programming: How to Get ThereSafe and Reliable Embedded Linux Programming: How to Get There
Safe and Reliable Embedded Linux Programming: How to Get ThereAdaCore
 
Peyton jones-2011-parallel haskell-the_future
Peyton jones-2011-parallel haskell-the_futurePeyton jones-2011-parallel haskell-the_future
Peyton jones-2011-parallel haskell-the_futureTakayuki Muranushi
 
Simon Peyton Jones: Managing parallelism
Simon Peyton Jones: Managing parallelismSimon Peyton Jones: Managing parallelism
Simon Peyton Jones: Managing parallelismSkills Matter
 
Epic.NET: Processes, patterns and architectures
Epic.NET: Processes, patterns and architecturesEpic.NET: Processes, patterns and architectures
Epic.NET: Processes, patterns and architecturesGiacomo Tesio
 
Understanding Typing. Understanding Ruby.
Understanding Typing. Understanding Ruby.Understanding Typing. Understanding Ruby.
Understanding Typing. Understanding Ruby.Justin Lin
 
Reverse engineering
Reverse engineeringReverse engineering
Reverse engineeringSaswat Padhi
 
make builds groovy
make builds groovymake builds groovy
make builds groovyguest88884d
 
Make Your Builds More Groovy
Make Your Builds More GroovyMake Your Builds More Groovy
Make Your Builds More GroovyPaul King
 
chapter7.ppt java programming lecture notes
chapter7.ppt java programming lecture noteschapter7.ppt java programming lecture notes
chapter7.ppt java programming lecture noteskavitamittal18
 
The Lean Tech Stack
The Lean Tech StackThe Lean Tech Stack
The Lean Tech StackBill Scott
 
Programming Language Selection
Programming Language SelectionProgramming Language Selection
Programming Language SelectionDhananjay Nene
 
Introduction to Software - Coder Forge - John Mulhall
Introduction to Software - Coder Forge - John MulhallIntroduction to Software - Coder Forge - John Mulhall
Introduction to Software - Coder Forge - John MulhallJohn Mulhall
 
groovy and concurrency
groovy and concurrencygroovy and concurrency
groovy and concurrencyPaul King
 
Data Segmenting in Anzo
Data Segmenting in AnzoData Segmenting in Anzo
Data Segmenting in AnzoLeeFeigenbaum
 

Similaire à Paulking dlp (20)

Dynamic Language Practices
Dynamic Language PracticesDynamic Language Practices
Dynamic Language Practices
 
Converging Textual and Graphical Editors
Converging Textual  and Graphical EditorsConverging Textual  and Graphical Editors
Converging Textual and Graphical Editors
 
Safe and Reliable Embedded Linux Programming: How to Get There
Safe and Reliable Embedded Linux Programming: How to Get ThereSafe and Reliable Embedded Linux Programming: How to Get There
Safe and Reliable Embedded Linux Programming: How to Get There
 
Peyton jones-2011-parallel haskell-the_future
Peyton jones-2011-parallel haskell-the_futurePeyton jones-2011-parallel haskell-the_future
Peyton jones-2011-parallel haskell-the_future
 
Simon Peyton Jones: Managing parallelism
Simon Peyton Jones: Managing parallelismSimon Peyton Jones: Managing parallelism
Simon Peyton Jones: Managing parallelism
 
Jax keynote
Jax keynoteJax keynote
Jax keynote
 
Epic.NET: Processes, patterns and architectures
Epic.NET: Processes, patterns and architecturesEpic.NET: Processes, patterns and architectures
Epic.NET: Processes, patterns and architectures
 
Comp102 lec 3
Comp102   lec 3Comp102   lec 3
Comp102 lec 3
 
Understanding Typing. Understanding Ruby.
Understanding Typing. Understanding Ruby.Understanding Typing. Understanding Ruby.
Understanding Typing. Understanding Ruby.
 
Reverse engineering
Reverse engineeringReverse engineering
Reverse engineering
 
make builds groovy
make builds groovymake builds groovy
make builds groovy
 
Make Your Builds More Groovy
Make Your Builds More GroovyMake Your Builds More Groovy
Make Your Builds More Groovy
 
chapter7.ppt java programming lecture notes
chapter7.ppt java programming lecture noteschapter7.ppt java programming lecture notes
chapter7.ppt java programming lecture notes
 
GroovyDSLs
GroovyDSLsGroovyDSLs
GroovyDSLs
 
The Lean Tech Stack
The Lean Tech StackThe Lean Tech Stack
The Lean Tech Stack
 
Programming Language Selection
Programming Language SelectionProgramming Language Selection
Programming Language Selection
 
Introduction to Software - Coder Forge - John Mulhall
Introduction to Software - Coder Forge - John MulhallIntroduction to Software - Coder Forge - John Mulhall
Introduction to Software - Coder Forge - John Mulhall
 
groovy and concurrency
groovy and concurrencygroovy and concurrency
groovy and concurrency
 
Data Segmenting in Anzo
Data Segmenting in AnzoData Segmenting in Anzo
Data Segmenting in Anzo
 
Java introduction
Java introductionJava introduction
Java introduction
 

Plus de d0nn9n

腾讯大讲堂:62 拇指下的精彩(手机qq交互设计经验分享)
腾讯大讲堂:62 拇指下的精彩(手机qq交互设计经验分享)腾讯大讲堂:62 拇指下的精彩(手机qq交互设计经验分享)
腾讯大讲堂:62 拇指下的精彩(手机qq交互设计经验分享)d0nn9n
 
腾讯大讲堂:55 企业法律风险防范
腾讯大讲堂:55 企业法律风险防范腾讯大讲堂:55 企业法律风险防范
腾讯大讲堂:55 企业法律风险防范d0nn9n
 
腾讯大讲堂:56 qzone安全之路
腾讯大讲堂:56 qzone安全之路腾讯大讲堂:56 qzone安全之路
腾讯大讲堂:56 qzone安全之路d0nn9n
 
腾讯大讲堂:59 数据蕴含商机,挖掘决胜千里
腾讯大讲堂:59 数据蕴含商机,挖掘决胜千里腾讯大讲堂:59 数据蕴含商机,挖掘决胜千里
腾讯大讲堂:59 数据蕴含商机,挖掘决胜千里d0nn9n
 
腾讯大讲堂:57 超级qq的千万之路
腾讯大讲堂:57 超级qq的千万之路 腾讯大讲堂:57 超级qq的千万之路
腾讯大讲堂:57 超级qq的千万之路 d0nn9n
 
蔡学镛 Rebol漫谈
蔡学镛   Rebol漫谈蔡学镛   Rebol漫谈
蔡学镛 Rebol漫谈d0nn9n
 
赵泽欣 - 淘宝网前端应用与发展
赵泽欣 - 淘宝网前端应用与发展赵泽欣 - 淘宝网前端应用与发展
赵泽欣 - 淘宝网前端应用与发展d0nn9n
 
Yanggang wps
Yanggang wpsYanggang wps
Yanggang wpsd0nn9n
 
熊节 - 软件工厂的精益之路
熊节 - 软件工厂的精益之路熊节 - 软件工厂的精益之路
熊节 - 软件工厂的精益之路d0nn9n
 
谢恩伟 - 微软在云端
谢恩伟 - 微软在云端谢恩伟 - 微软在云端
谢恩伟 - 微软在云端d0nn9n
 
去哪儿平台技术
去哪儿平台技术去哪儿平台技术
去哪儿平台技术d0nn9n
 
吴磊 - Silverlight企业级RIA
吴磊 - Silverlight企业级RIA吴磊 - Silverlight企业级RIA
吴磊 - Silverlight企业级RIAd0nn9n
 
Tom - Scrum
Tom - ScrumTom - Scrum
Tom - Scrumd0nn9n
 
Tim - FSharp
Tim - FSharpTim - FSharp
Tim - FSharpd0nn9n
 
Tiger oracle
Tiger oracleTiger oracle
Tiger oracled0nn9n
 
Paulking groovy
Paulking groovyPaulking groovy
Paulking groovyd0nn9n
 
Patrick jcp
Patrick jcpPatrick jcp
Patrick jcpd0nn9n
 
Nick twitter
Nick twitterNick twitter
Nick twitterd0nn9n
 
Marc facebook
Marc facebookMarc facebook
Marc facebookd0nn9n
 
Kane debt
Kane debtKane debt
Kane debtd0nn9n
 

Plus de d0nn9n (20)

腾讯大讲堂:62 拇指下的精彩(手机qq交互设计经验分享)
腾讯大讲堂:62 拇指下的精彩(手机qq交互设计经验分享)腾讯大讲堂:62 拇指下的精彩(手机qq交互设计经验分享)
腾讯大讲堂:62 拇指下的精彩(手机qq交互设计经验分享)
 
腾讯大讲堂:55 企业法律风险防范
腾讯大讲堂:55 企业法律风险防范腾讯大讲堂:55 企业法律风险防范
腾讯大讲堂:55 企业法律风险防范
 
腾讯大讲堂:56 qzone安全之路
腾讯大讲堂:56 qzone安全之路腾讯大讲堂:56 qzone安全之路
腾讯大讲堂:56 qzone安全之路
 
腾讯大讲堂:59 数据蕴含商机,挖掘决胜千里
腾讯大讲堂:59 数据蕴含商机,挖掘决胜千里腾讯大讲堂:59 数据蕴含商机,挖掘决胜千里
腾讯大讲堂:59 数据蕴含商机,挖掘决胜千里
 
腾讯大讲堂:57 超级qq的千万之路
腾讯大讲堂:57 超级qq的千万之路 腾讯大讲堂:57 超级qq的千万之路
腾讯大讲堂:57 超级qq的千万之路
 
蔡学镛 Rebol漫谈
蔡学镛   Rebol漫谈蔡学镛   Rebol漫谈
蔡学镛 Rebol漫谈
 
赵泽欣 - 淘宝网前端应用与发展
赵泽欣 - 淘宝网前端应用与发展赵泽欣 - 淘宝网前端应用与发展
赵泽欣 - 淘宝网前端应用与发展
 
Yanggang wps
Yanggang wpsYanggang wps
Yanggang wps
 
熊节 - 软件工厂的精益之路
熊节 - 软件工厂的精益之路熊节 - 软件工厂的精益之路
熊节 - 软件工厂的精益之路
 
谢恩伟 - 微软在云端
谢恩伟 - 微软在云端谢恩伟 - 微软在云端
谢恩伟 - 微软在云端
 
去哪儿平台技术
去哪儿平台技术去哪儿平台技术
去哪儿平台技术
 
吴磊 - Silverlight企业级RIA
吴磊 - Silverlight企业级RIA吴磊 - Silverlight企业级RIA
吴磊 - Silverlight企业级RIA
 
Tom - Scrum
Tom - ScrumTom - Scrum
Tom - Scrum
 
Tim - FSharp
Tim - FSharpTim - FSharp
Tim - FSharp
 
Tiger oracle
Tiger oracleTiger oracle
Tiger oracle
 
Paulking groovy
Paulking groovyPaulking groovy
Paulking groovy
 
Patrick jcp
Patrick jcpPatrick jcp
Patrick jcp
 
Nick twitter
Nick twitterNick twitter
Nick twitter
 
Marc facebook
Marc facebookMarc facebook
Marc facebook
 
Kane debt
Kane debtKane debt
Kane debt
 

Dernier

Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slidevu2urc
 
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptxFactors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptxKatpro Technologies
 
Developing An App To Navigate The Roads of Brazil
Developing An App To Navigate The Roads of BrazilDeveloping An App To Navigate The Roads of Brazil
Developing An App To Navigate The Roads of BrazilV3cube
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Scriptwesley chun
 
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfEnterprise Knowledge
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking MenDelhi Call girls
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Enterprise Knowledge
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking MenDelhi Call girls
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreternaman860154
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)Gabriella Davis
 
Top 5 Benefits OF Using Muvi Live Paywall For Live Streams
Top 5 Benefits OF Using Muvi Live Paywall For Live StreamsTop 5 Benefits OF Using Muvi Live Paywall For Live Streams
Top 5 Benefits OF Using Muvi Live Paywall For Live StreamsRoshan Dwivedi
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerThousandEyes
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Drew Madelung
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxMalak Abu Hammad
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024The Digital Insurer
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking MenDelhi Call girls
 
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...gurkirankumar98700
 
Unblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesUnblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesSinan KOZAK
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityPrincipled Technologies
 

Dernier (20)

Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slide
 
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptxFactors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
 
Developing An App To Navigate The Roads of Brazil
Developing An App To Navigate The Roads of BrazilDeveloping An App To Navigate The Roads of Brazil
Developing An App To Navigate The Roads of Brazil
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Script
 
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreter
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 
Top 5 Benefits OF Using Muvi Live Paywall For Live Streams
Top 5 Benefits OF Using Muvi Live Paywall For Live StreamsTop 5 Benefits OF Using Muvi Live Paywall For Live Streams
Top 5 Benefits OF Using Muvi Live Paywall For Live Streams
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptx
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men
 
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
 
Unblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesUnblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen Frames
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
 

Paulking dlp

  • 2. Introduction … • Developer practices – Well understood and documented for traditional and agile approaches such as Java, C++ and C# development – But dynamic languages like Groovy, Ruby, Python, Boo, JavaScript and (c) ASERT 2006-2009 others change the ground rules – Many of the rules and patterns we have been taught no longer apply
  • 3. … 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 Language (c) ASERT 2006-2009 Guide. Prentice Hall. – Robert C Martin (2002), Agile Software Development, Principles, Patterns, and Practices. Prentice Hall. • In the dynamic language world, are these guidelines FACT or MYTH !!
  • 4. Examples to ponder • What does Immutability mean? – When even constants can be changed • What does encapsulation mean? – When I can peek at anything (c) ASERT 2006-2009 under the covers • How can I devise tests at development time? – When my system can change in unknown ways at runtime • How can IDEs help me? – If I no longer spoon feed it static-typing information or if my language now only allows checks at runtime
  • 5. What do I mean by Dynamic Language? • I prefer a loose definition • One or more of: – Dynamic typing • Greater polymorphism – Metaprogramming • Allow language itself to be dynamically changed (c) ASERT 2006-2009 • 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
  • 6. Static vs Dynamic Typing … • The Debate – Static vs dynamic typing • Static: the type of each variable (or expression) must be known at compile time like wearing a straight-jacket? • Dynamic: type information is (c) ASERT 2006-2009 known only at runtime like tightrope walking with no net? – Strong vs weak typing • Strong: List<Integer> myList • Weak: Object myList – Type safety • How is this provided if at all? – Type inference • Is this supported?
  • 7. …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 (c) ASERT 2006-2009 – 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
  • 8. … 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 (c) ASERT 2006-2009 – 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
  • 9. … Static vs Dynamic Typing … • 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. (c) ASERT 2006-2009
  • 10. … Static vs Dynamic Typing … • An example interface Reversible { def reverse() } class ReversibleString implements Reversible { def reverse() { /* */ } ??? } class ReversibleArray implements Reversible { def reverse() { /* */ } (c) ASERT 2006-2009 ??? } 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
  • 11. … 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 (c) ASERT 2006-2009 } 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
  • 12. … Static vs Dynamic Typing … • 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. (c) ASERT 2006-2009 ...but not a total lie either... ...dynamic languages certainly assist with removing duplication and sometimes removing clutter and boilerplate code...
  • 13. 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() (c) ASERT 2006-2009 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]
  • 14. …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 (c) ASERT 2006-2009 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
  • 15. Typing approaches and IDEs… • Class A has a bit of duplication class A { def helper def make() { helper.invoke('create') } def get() { (c) ASERT 2006-2009 helper.invoke('read') } def change() { helper.invoke('update') } def remove() { helper.invoke('delete') } }
  • 16. … Typing approaches and IDEs … • No problems, we can refactor out the dup class B { def helper def make() { invoke('create') } def get() { (c) ASERT 2006-2009 invoke('read') } def change() { invoke('update') } def remove() { invoke('delete') } private invoke(cmd) { helper.invoke(cmd) } }
  • 17. … Typing approaches and IDEs … • But we can do more using a dynamic language by leveraging metaprogramming class C { def helper def commands = [ make: 'create', (c) ASERT 2006-2009 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? … ...
  • 18. … Typing approaches and IDEs … class Dumper { def name def invokeMethod(String methodName, args) { println "$name: called $methodName with $args" } } (c) ASERT 2006-2009 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() }
  • 19. … Typing approaches and IDEs • … At the expense of IDE completion? (c) ASERT 2006-2009 But remember: “clearly express intent” ...
  • 20. Language features instead of Patterns … 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" } } (c) ASERT 2006-2009 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
  • 21. …Language features instead of Patterns… 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)" (c) ASERT 2006-2009 } } 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
  • 22. … Language features instead of Patterns … SquarePeg.metaClass.getRadius = { Math.sqrt(((delegate.width/2)**2)*2) } (4..7).each { w -> pretty(hole, new SquarePeg(width:w)) } Adapter Pattern (c) ASERT 2006-2009 Do I create a whole new class or just add the method I need on the fly? 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
  • 23. 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 – (c) ASERT 2006-2009 Can help with test creation – Avoids class proliferation – But you still need testing
  • 24. … Language features instead of Patterns 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) } } (c) ASERT 2006-2009 } } 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 [24]
  • 25. 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 – (c) ASERT 2006-2009 Avoids class proliferation – But you still need testing
  • 26. Strategy Pattern (c) ASERT 2006-2009 Source: http://nealford.com/
  • 27. 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 { { n, m -> n * m }, def execute(n, m) { def result = 0 { n, m -> n.times { def total = 0; n.times{ total += m }; total }, result += m { n, m -> ([m] * n).sum() } (c) ASERT 2006-2009 } ] return result } def sampleData = [ } [3, 4, 12], def sampleData = [ [5, -5, -25] [3, 4, 12], ] [5, -5, -25] ] sampleData.each{ data -> multiplicationStrategies.each{ calc -> Calc[] multiplicationStrategies = [ 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]) } }
  • 28. 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 (c) ASERT 2006-2009 – Aids refactoring – Can help with test creation – Avoids class proliferation – But you still need testing
  • 29. … Language features instead of Patterns … • 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 (c) ASERT 2006-2009 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, …
  • 30. … Language features instead of Patterns <html> <head> import groovy.xml.* <title>Hello</title> def b = new MarkupBuilder() </head> b.html { <body> (c) ASERT 2006-2009 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>
  • 31. 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) { (c) ASERT 2006-2009 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/
  • 32. JavaFX Script Frame { title: "Hello World F3" width: 200 content: Label { text: "Hello World" (c) ASERT 2006-2009 } visible: true }
  • 33. Cheri::Swing # requires JRuby require 'rubygems' (c) ASERT 2006-2009 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
  • 34. 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) (c) ASERT 2006-2009 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()
  • 35. 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 (c) ASERT 2006-2009
  • 36. 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 (c) ASERT 2006-2009 } 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" } }
  • 37. … 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 (c) ASERT 2006-2009 StaffMemberUsingDelegation(name, age, salary) { delegate = new Person(name, age) this.salary = salary } def haveBirthday() { delegate.haveBirthday() } String toString() { delegate.toString() + " and has a salary of $salary" } }
  • 38. … 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 (c) ASERT 2006-2009 parameters rather than the traditional positional parameters shown here (future versions of Ruby may have this too)
  • 39. … 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 } (c) ASERT 2006-2009 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
  • 40. … 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 (c) ASERT 2006-2009 • E.g. If I wanted to make haveBirthday() increment salary 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" } }
  • 41. 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 (c) ASERT 2006-2009
  • 42. 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 (c) ASERT 2006-2009 representation through subclassing 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
  • 43. …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; (c) ASERT 2006-2009 } } 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; } }
  • 44. …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) } (c) ASERT 2006-2009 @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()
  • 45. …Singleton Pattern… • But it is easy using meta-programming – Old style class Calculator { private total = 0 def add(a, b) { total++; a + b } (c) ASERT 2006-2009 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())
  • 46. …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() (c) ASERT 2006-2009 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]
  • 47. …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 (c) ASERT 2006-2009 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
  • 48. …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 (c) ASERT 2006-2009 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
  • 49. 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 (c) ASERT 2006-2009
  • 50. Pattern Summary • Patterns can be replaced by language features and libraries (c) ASERT 2006-2009 • So patterns aren‟t important any more! ...
  • 51. 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‟ (c) ASERT 2006-2009 – Some refactorings are done differently
  • 52. 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 (c) ASERT 2006-2009 // Before refactoring Object lastReading() { return readings.lastElement() } // After refactoring Reading lastReading() { return (Reading) readings.lastElement() }
  • 53. 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? (c) ASERT 2006-2009 – 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()
  • 54. 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: (c) ASERT 2006-2009 def numbers = [31,-41,59,26,-53,58,97,-93,-23,84] 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"
  • 55. … Enabling a functional style … • A first attempt at a more functional style: def numbers = [31,-41,59,26,-53,58,97,-93,-23,84] (c) ASERT 2006-2009 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"
  • 56. … 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: (c) ASERT 2006-2009 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'] ] ]
  • 57. … 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] (c) ASERT 2006-2009 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
  • 58. 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. (c) ASERT 2006-2009 • Solution – Remove the parameter and let the receiver invoke the method. • Dynamic solution – Partial Application: Currying
  • 59. 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 (c) ASERT 2006-2009 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
  • 60. … 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 (c) ASERT 2006-2009 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
  • 61. … 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 } (c) ASERT 2006-2009 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
  • 62. … 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 } (c) ASERT 2006-2009 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
  • 63. … 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) { (c) ASERT 2006-2009 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
  • 64. … 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) { (c) ASERT 2006-2009 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
  • 65. … Replace parameter with method … class Order { private int quantity, itemPrice Order(q, p) {quantity = q; itemPrice = p} double getPrice() { return discountedPrice() } private double discountedPrice() { (c) ASERT 2006-2009 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
  • 66. … Replace parameter with method … class Order { private int quantity, itemPrice Order(q, p) {quantity = q; itemPrice = p} double getPrice() { return discountedPrice() } private double discountedPrice() { (c) ASERT 2006-2009 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
  • 67. … 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 } (c) ASERT 2006-2009 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
  • 68. 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 (c) ASERT 2006-2009 def discountLevel = (quantity > 100) ? 2 : 1 discountedPrice(basePrice, discountLevel) } } println new Order(120, 5).price() // => 480.0
  • 69. … 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 = { (c) ASERT 2006-2009 discountLevel() == 2 ? basePrice() * 0.8 : basePrice() * 0.9 } } println new Order(120, 5).price() // => 480.0
  • 70. … 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 -> (c) ASERT 2006-2009 discountLevel == 2 ? basePrice * 0.8 : basePrice * 0.9 } def price = { discountedPrice.curry(basePrice()).curry(discountLevel()).call() } } println new Order(120, 5).price() // => 480.0
  • 71. … 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) { (c) ASERT 2006-2009 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
  • 72. 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 ... (c) ASERT 2006-2009 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
  • 73. … 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 } (c) ASERT 2006-2009 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)
  • 74. … 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" (c) ASERT 2006-2009 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(', ')
  • 75. • This is the end of the talk (c) ASERT 2006-2009
  • 76. • This is the end of the talk • NO! • We haven’t questioned some fundamental principles yet! (c) ASERT 2006-2009
  • 77. 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: (c) ASERT 2006-2009 • Software entities should be open for 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
  • 78. ...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: (c) ASERT 2006-2009 – 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!
  • 79. ...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 (c) ASERT 2006-2009 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 ...
  • 80. ...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 (c) ASERT 2006-2009 • In OCP terms, it is not open for extension • Dynamic language solution: open classes String.metaClass.swapCase = { #light open String delegate.collect{ c -> type System.String with c in 'A'..'Z' ? member x.swapCase = seq { for letter in x.ToCharArray() do c.toLowerCase() : if (System.Char.IsLower(letter)) c.toUpperCase() then yield System.Char.ToUpper(letter) else yield System.Char.ToLower(letter) }.join() } } printfn "result: %A" "Foo".swapCase assert "Foo".swapCase() == "fOO" ...
  • 81. ...Open-Closed Principle... • Part II: Violating OCP (see [15]) class Square { def side } class Circle { def radius } (c) ASERT 2006-2009 class AreaCalculator { double area(shape) { switch (shape) { case Square: return shape.side * shape.side case Circle: return Math.PI * shape.radius ** 2 } } }
  • 82. ...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)}" (c) ASERT 2006-2009 } • 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
  • 83. ...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, (c) ASERT 2006-2009 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) ...
  • 84. ...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 (c) ASERT 2006-2009 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) ...
  • 85. ...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) (c) ASERT 2006-2009 • 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, ...
  • 86. ...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 (c) ASERT 2006-2009 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
  • 87. ...Open-Closed Principle... class Square { double side } class Rectangle { double height, width } class Circle { (c) ASERT 2006-2009 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.
  • 88. ...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 } } (c) ASERT 2006-2009 class Rectangle implements Shape { double height, width double area() { height * width } } class Circle implements Shape { double radius double area() { PI * radius ** 2 } }
  • 89. ...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 } } (c) ASERT 2006-2009 class Circle { double radius double area() { PI * radius ** 2 } }
  • 90. ...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 (c) ASERT 2006-2009 • 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
  • 91. ...Open-Closed Principle • Sometimes referred to as the Expression Problem: – Independently Extensible Solutions to the Expression Problem by Matthias Zenger Martin Odersky – http://www.scala-lang.org/docu/files/ IC_TECH_REPORT_200433.pdf (c) ASERT 2006-2009
  • 92. • This is the end of the talk (c) ASERT 2006-2009
  • 93. • This is the end of the talk • NO! • We haven’t looked at Advanced Topics yet including Aspects, Testability, Feature Interaction and DSLs! (c) ASERT 2006-2009
  • 94. What & why of dependency injection? • Construction by hand Loose Coupling • Factory pattern Flexibility (c) ASERT 2006-2009 Testability Reusability • Service locator Lifecycle Control Central • Dependency injection Control
  • 95. Dependency Injection vs Metaprogramming … • Dependency Injection – Dependencies are explicitly declared and allowed to be set externally (typically via constructor or setters) • Transparent injection of dependent service objects into other service objects by a controlling container hence the name inversion of control • Why? (c) ASERT 2006-2009 – More flexible • Central configuration of service objects – Can be less work to do • Service objects are instantiated by the dependency injection framework – Improves testability – Improves reusability – Improved lifecycle control
  • 96. … Dependency Injection vs Metaprogramming … • Without dependency injection class PrintService { private printer = new PhysicalPrinter('Canon i9900') } class PrintServiceTest extends GroovyTestCase { def testPrintService() { (c) ASERT 2006-2009 def printService = new PrintService() printService.print() class PrintService // go to the printer and fetch the page def initialise } @printer = } PhysicalPrinter.new('Canon i9900') end end class PrintServiceTest < Test::Unit def test_print_service print_service = PrinterService.new print_service.print # go to the printer and fetch the page end end
  • 97. … Dependency Injection vs Metaprogramming … • Improves testability class TestablePrintService { def printer } class TestablePrintServiceTest extends GroovyTestCase { def testPrintService() { def printService = new TestablePrintService() printService.printer = new StubPrinter() (c) ASERT 2006-2009 printService.print() //... } class TestablePrintService } attr_accessor :printer end class TestablePrintServiceTest < Test::Unit::TestCase def test_print_service print_service = TestablePrintService.new print_service.printer = StubPrinter.new print_service.print #... end end