Dynamic Language Practices
-
Upload
paul-king -
Category
Technology
-
view
6.351 -
download
1
description
Transcript of Dynamic Language Practices
![Page 1: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/1.jpg)
Developer Practices for
Dynamic Languages
“Unlearning Java/C#”
Dr Paul King, Director
ASERT, Australia
![Page 2: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/2.jpg)
Topics
Introduction
• Design patterns
• Refactoring
• Polyglot programming
• SOLID principles
• Other topics
• More Info
ESDC 2010 - 2
© A
SE
RT
2006-2
010
![Page 3: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/3.jpg)
Introduction …
• Developer practices– Well understood and documented for
traditional languages like Java, C++ and C#
– But dynamic languages like Groovy, Ruby,
Python, Boo, JavaScript and others,
change the ground rules
– Many of the rules and patterns we have
been taught must be adapted or adjusted;
some no longer apply at all
© A
SE
RT
2006-2
010
![Page 4: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/4.jpg)
...Introduction...
• What does Immutability mean?– When even constants can be changed
• What does encapsulation mean?– When I can peek at internal state or
when using languages without state
• How can I devise tests
at development time?– When my system can change in
unknown ways at runtime
• How can IDEs help me?– If I no longer spoon feed static-type information to my
IDE, what level of support can it give me in terms of
code completion and error checking
© A
SE
RT
2006-2
010
![Page 5: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/5.jpg)
… 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 Guide. Prentice Hall.
– Robert C Martin (2002), Agile Software Development,
Principles, Patterns, and Practices. Prentice Hall.
– Robert C Martin (2006), Agile Principles, Patterns, and
Practices in C#. Prentice Hall.
• In the dynamic language world, are the
guidelines in these books FACT or MYTH?
• But first let’s look at what we mean by
dynamic languages and dynamic typing
© A
SE
RT
2006-2
010
![Page 6: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/6.jpg)
What do I mean by Dynamic Language?
• I prefer a flexible
definition
• One or more of:– Dynamic typing
• Greater polymorphism
– Metaprogramming
• Allow language itself to be dynamically changed
• Allow hooks into object lifecycle and method calls
• Open classes/monkey patching
– Work with code as easily as data
• Closures
• Higher-order programming
– Escape hatches
• Hooks for polyglot programming
© A
SE
RT
2006-2
010
![Page 7: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/7.jpg)
… Static vs Dynamic Typing …
• MYTH or TRUTH?
Static typing is just spoon feeding the
compiler/IDE. It represents the old-school
way of thinking and requires extra work
while providing no real value.© A
SE
RT
2006-2
010
Static VS Dynamic
![Page 8: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/8.jpg)
Static vs Dynamic Typing …
• Static: the type of each variable
(or expression) must be known
at compile time
dynamic advocates: like programming
wearing a straight-jacket?
Unnecessary complexity
© A
SE
RT
2006-2
010
![Page 9: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/9.jpg)
…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
– 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
© A
SE
RT
2006-2
010
![Page 10: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/10.jpg)
…Static vs Dynamic Typing …
• Dynamic: type information is known only
at runtime
static advocates: like tightrope walking
with no net?
© A
SE
RT
2006-2
010
![Page 11: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/11.jpg)
… 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
– 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
© A
SE
RT
2006-2
010
![Page 12: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/12.jpg)
…Static vs Dynamic Typing …
• 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?
© A
SE
RT
2006-2
010
![Page 13: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/13.jpg)
…Static and Dynamic Typing…
© A
SE
RT
2006-2
010
Source: http://www.slideshare.net/pcalcado/one-or-two-things-you-may-not-know-about-typesystems
![Page 14: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/14.jpg)
Correctness?
© A
SE
RT
2006-2
010
Source: http://www.slideshare.net/pcalcado/one-or-two-things-you-may-not-know-about-typesystems
![Page 15: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/15.jpg)
Typing Approaches…
© A
SE
RT
2006-2
010
interface Duck {def waddle()def quack()
}
class DuckImpl implements Duck {def waddle() { println "waddle" }def quack() { println "quack" }
}
class Goose {def waddle() { println "Goose waddle" }def quack() { println "Goose quack" }
}
![Page 16: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/16.jpg)
…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– If your language doesn’t support it natively you
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
© A
SE
RT
2006-2
010
Source: Rick DeNatale
© David Friel
![Page 17: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/17.jpg)
…Typing Approaches
• Implicit vs Explicit interfaces– Inheritance too restrictive?
– Duck-typing too flexible?
Adapted from Interface-Oriented Design [2]
Shapedraw()
Rectangledraw()
set_sides()
Squaredraw()
set_sides()
Rectangledraw()
set_sides()
Squaredraw()
set_side()
EquilateralTriangledraw()
set_side()
Pistoldraw()
Menuset_sides()
Rectangledraw()
set_sides()
<<interface>>
Shapedraw()
<<interface>>
RegularPolygonset_side()
Squaredraw()
set_side()
EquilateralTriangledraw()
set_side()
© A
SE
RT
2006-2
010
I tend to use Explicit types
for major boundaries and
implicit types internally.
![Page 18: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/18.jpg)
… Static vs Dynamic Typing …
• MYTH
Removing static typing always leads to
more concise and readable code.
© A
SE
RT
2006-2
010 X incorrect
![Page 19: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/19.jpg)
… Static vs Dynamic Typing …
• An example
© A
SE
RT
2006-2
010
interface Reversible {def reverse()
}
class ReversibleString implements Reversible {def reverse() { /* */ }
}
class ReversibleArray implements Reversible {def reverse() { /* */ }
}
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
![Page 20: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/20.jpg)
… Static vs Dynamic Typing ...
© A
SE
RT
2006-2
010
interface Reversible {def reverse()
}
class ReversibleString implements Reversible {def reverse() { /* */ }
}
class ReversibleArray implements Reversible {def reverse() { /* */ }
}
Reversible[] things = [new ReversibleString(), new ReversibleArray()
]
for (i in 0..<things.size()) {things[i].reverse()
}
With dynamically
typed languages,
there is no need to
explicitly declare the
types of variables or
the “protocols”
observed by our
objects:
Less code
Less declarative
Less IDE support
More testing
Less Robust?
def things = ["abc", [1, 2 ,3]]def expected = ["cba", [3, 2, 1]]assert things*.reverse() == expected
![Page 21: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/21.jpg)
… Static vs Dynamic Typing …
• MYTH
Dynamic typing means the IDE can’t
provide support for completion and early
syntax error checks.
© A
SE
RT
2006-2
010 X incorrect
![Page 22: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/22.jpg)
… Static vs Dynamic Typing ...
• Consider Groovy in Intellij
• And Eclipse
© A
SE
RT
2006-2
010
Eclipse example: http://contraptionsforprogramming.blogspot.com/
![Page 23: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/23.jpg)
Typing approaches and IDEs…
• Class A has a bit of duplication
© A
SE
RT
2006-2
010
class A {def helperdef make() {
helper.invoke('create')}def get() {
helper.invoke('read')}def change() {
helper.invoke('update')}def remove() {
helper.invoke('delete')}
}
![Page 24: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/24.jpg)
… Typing approaches and IDEs …
• No problems, we can refactor out the dup
class B {def helperdef make() {
invoke('create')}def get() {
invoke('read')}def change() {
invoke('update')}def remove() {
invoke('delete')}private invoke(cmd) {
helper.invoke(cmd)}
}
© A
SE
RT
2006-2
010
![Page 25: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/25.jpg)
… Typing approaches and IDEs …
• But we can do more using a dynamic
language by leveraging metaprogramming
• Which is a whole lot nicer?
• At the expense of IDE completion? …
class C {def helperdef commands = [
make: 'create',get: 'read',change: 'update',remove: 'delete'
]def invokeMethod(String name, ignoredArgs) {
helper.invoke(commands[name])}
}
© A
SE
RT
2006-2
010
...
![Page 26: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/26.jpg)
… Typing approaches and IDEs …
© A
SE
RT
2006-2
010
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()
}
class Dumper {def namedef invokeMethod(String methodName, args) {
println "$name: called $methodName with $args"}
}
![Page 27: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/27.jpg)
… Typing approaches and IDEs
• … At the expense of IDE completion?
© A
SE
RT
2006-2
010
But remember:
“clearly express intent”
![Page 28: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/28.jpg)
… Static vs Dynamic Typing …
• MYTH
Static typing means runtime errors are a
thing of the past.
© A
SE
RT
2006-2
010
X incorrectSource: http://www.slideshare.net/pcalcado/one-or-two-things-you-may-not-know-about-typesystems (phillip calçado)
![Page 29: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/29.jpg)
… Static vs Dynamic Typing ...
• Consider Lift (based on Scala)
© A
SE
RT
2006-2
010
<lift:surround with="default" at="content">
<h2>Welcome to your project!</h2>
<p><lift:hellWorld.howdy /></p>
</lift:surround>
<lift:surrond with="default" at="content">
<h2>Welcome to your project!</h2>
<p><lift:hellWorld.howdy /></p>
</lift:surround>
Source: http://zef.me/2371/when-scala-dsls-fail
Result: No error but
empty home page
![Page 30: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/30.jpg)
Static and Dynamic Strong Typing
© A
SE
RT
2006-2
010
Source: http://www.slideshare.net/pcalcado/one-or-two-things-you-may-not-know-about-typesystems
![Page 31: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/31.jpg)
Static vs Dynamic Typing
© A
SE
RT
2006-2
010
Static Dynamic
Syntax bugs
Optimisation
Arithmetic bugs
Logic bugs
Resource bugs
Concurrency bugs
approx same approx same
Power
Flexibility
![Page 32: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/32.jpg)
Static vs Dynamic Typing Verdict
• MYTH or TRUTH?
Static typing is just spoon feeding the
compiler. It represents the old-school way
of thinking and requires extra work while
providing no real value.
© A
SE
RT
2006-2
010 ...but not a total lie either...
...dynamic languages certainly assist
with removing duplication, clutter and
boilerplate code...
![Page 33: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/33.jpg)
An open debate
© A
SE
RT
2006-2
010
Source: http://www.slideshare.net/pcalcado/one-or-two-things-you-may-not-know-about-typesystems
![Page 34: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/34.jpg)
Topics
• Introduction
Design patterns
• Refactoring
• Polyglot programming
• SOLID principles
• Other topics
• More Info
© A
SE
RT
2006-2
010
![Page 35: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/35.jpg)
Language features instead of Patterns
• So called "Design Patterns" are merely
hacks to overcome the limitations of your
language
– You call that a
language?
– This is a language
• "Design Patterns" are really anti-patterns
you must sometimes put up with because
your language is so archaic!
• In my superior language, that would be
built-in, simply a library, so easy, ...
© A
SE
RT
2006-2
010
![Page 36: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/36.jpg)
Language features instead of Patterns
• So called "Design Patterns" are merely
hacks to overcome the limitations of your
language
– You call that a
language?
– This is a language
• "Design Patterns" are really anti-patterns
you must sometimes put up with because
your language is so archaic!
• In my superior language, that would be
built-in, simply a library, so easy, ...
© A
SE
RT
2006-2
010
![Page 37: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/37.jpg)
Adapter Pattern…class RoundPeg {def radiusString toString() { "RoundPeg with radius $radius" }
}
class RoundHole {def radiusdef pegFits(peg) { peg.radius <= radius }String toString() { "RoundHole with radius $radius" }
}
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)) }
© A
SE
RT
2006-2
010
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
![Page 38: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/38.jpg)
…Adapter Pattern…class SquarePeg {def widthString toString() { "SquarePeg with width $width" }
}
class SquarePegAdapter {def pegdef getRadius() { Math.sqrt(((peg.width/2) ** 2)*2) }String toString() {"SquarePegAdapter with width $peg.width (and notional radius $radius)"
}}
def hole = new RoundHole(radius:4.0)
(4..7).each { w ->pretty(hole, new SquarePegAdapter(peg: new SquarePeg(width: w))) }
© A
SE
RT
2006-2
010
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
![Page 39: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/39.jpg)
…Adapter Pattern
SquarePeg.metaClass.getRadius ={ Math.sqrt(((delegate.width/2)**2)*2) }
(4..7).each { w -> pretty(hole, new SquarePeg(width:w)) }
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
Adapter Pattern
Do I create a whole new class
or just add the method I need
on the fly?
Consider the Pros and Cons!
© A
SE
RT
2006-2
010
Further reading: James Lyndsay, Agile is Groovy, Testing is Square
![Page 40: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/40.jpg)
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
– Can help with test creation
– Avoids class proliferation
• At the expense of class pollution?
– But you still need testing
© A
SE
RT
2006-2
010
![Page 41: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/41.jpg)
Immutable Pattern...
QCON 2010 - 41
© A
SE
RT
2006-2
010
public final class Punter {private final String first;private final String last;
public String getFirst() {return first;
}
public String getLast() {return last;
}
@Overridepublic int hashCode() {
final int prime = 31;int result = 1;result = prime * result + ((first == null)
? 0 : first.hashCode());result = prime * result + ((last == null)
? 0 : last.hashCode());return result;
}
public Punter(String first, String last) {this.first = first;this.last = last;
}// ...
// ...@Overridepublic boolean equals(Object obj) {
if (this == obj)return true;
if (obj == null)return false;
if (getClass() != obj.getClass())return false;
Punter other = (Punter) obj;if (first == null) {
if (other.first != null)return false;
} else if (!first.equals(other.first))return false;
if (last == null) {if (other.last != null)
return false;} else if (!last.equals(other.last))
return false;return true;
}
@Overridepublic String toString() {
return "Punter(first:" + first+ ", last:" + last + ")";
}
}
• Java Immutable Class– As per Joshua Bloch
Effective Java
boilerplate
![Page 42: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/42.jpg)
...Immutable Pattern
QCON 2010 - 42
© A
SE
RT
2006-2
010
@Immutable class Punter {String first, last
}
![Page 43: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/43.jpg)
abstract class Shape {}
class Rectangle extends Shape {def x, y, width, height
Rectangle(x, y, width, height) {this.x = x; this.y = y; this.width = width; this.height = height
}
def union(rect) {if (!rect) return thisdef minx = [rect.x, x].min()def maxx = [rect.x + width, x + width].max()def miny = [rect.y, y].min()def maxy = [rect.y + height, y + height].max()new Rectangle(minx, miny, maxx - minx, maxy - miny)
}
def accept(visitor) {visitor.visit_rectangle(this)
}}
class Line extends Shape {def x1, y1, x2, y2
Line(x1, y1, x2, y2) {this.x1 = x1; this.y1 = y1; this.x2 = x2; this.y2 = y2
}
def accept(visitor) {visitor.visit_line(this)
}}
class Group extends Shape {def shapes = []
def add(shape) { shapes += shape }
def remove(shape) { shapes -= shape }
def accept(visitor) {visitor.visit_group(this)
}}
class BoundingRectangleVisitor {def bounds
def visit_rectangle(rectangle) {if (bounds)bounds = bounds.union(rectangle)
elsebounds = rectangle
}
def visit_line(line) {def line_bounds = new Rectangle(line.x1, line.y1, line.x2 - line.y1, line.x2 - line.y2)if (bounds)bounds = bounds.union(line_bounds)
elsebounds = line_bounds
}
def visit_group(group) {group.shapes.each {shape -> shape.accept(this) }
}}
def group = new Group()group.add(new Rectangle(100, 40, 10, 5))group.add(new Rectangle(100, 70, 10, 5))group.add(new Line(90, 30, 60, 5))def visitor = new BoundingRectangleVisitor()group.accept(visitor)bounding_box = visitor.boundsprintln bounding_box.dump()
abstract class Shape {def accept(Closure yield) { yield(this) }
}
class Rectangle extends Shape {def x, y, w, hdef bounds() { this }def union(rect) {
if (!rect) return thisdef minx = [rect.x, x].min()def maxx = [rect.x + w, x + w].max()def miny = [rect.y, y].min()def maxy = [rect.y + h, y + h].max()new Rectangle(x:minx, y:miny, w:maxx - minx, h:maxy - miny)
}}
class Line extends Shape {def x1, y1, x2, y2def bounds() {
new Rectangle(x:x1, y:y1, w:x2-y1, h:x2-y2)}
}
class Group {def shapes = []def leftShift(shape) { shapes += shape }def accept(Closure yield) { shapes.each{it.accept(yield)} }
}
def group = new Group()group << new Rectangle(x:100, y:40, w:10, h:5)group << new Rectangle(x:100, y:70, w:10, h:5)group << new Line(x1:90, y1:30, x2:60, y2:5)def boundsgroup.accept{ bounds = it.bounds().union(bounds) }println bounds.dump()
Visitor Pattern
© A
SE
RT
2006-2
010
See also Ruby Visitor
Visitor Pattern
without closures
with closures
![Page 44: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/44.jpg)
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
– Avoids class proliferation
– But you still need testing
© A
SE
RT
2006-2
010
![Page 45: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/45.jpg)
Strategy Pattern
© A
SE
RT
2006-2
010
Source: http://nealford.com/
![Page 46: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/46.jpg)
interface Calc {def execute(n, m)
}
class CalcByMult implements Calc {def execute(n, m) { n * m }
}
class CalcByManyAdds implements Calc {def execute(n, m) {def result = 0n.times {
result += m}return result
}}
def sampleData = [[3, 4, 12],[5, -5, -25]
]
Calc[] multiplicationStrategies = [new CalcByMult(),new CalcByManyAdds()
]
sampleData.each {data ->multiplicationStrategies.each {calc ->assert data[2] == calc.execute(data[0], data[1])
}}
def multiplicationStrategies = [{ n, m -> n * m },{ n, m ->
def total = 0; n.times{ total += m }; total },{ n, m -> ([m] * n).sum() }
]
def sampleData = [[3, 4, 12],[5, -5, -25]
]
sampleData.each{ data ->multiplicationStrategies.each{ calc ->
assert data[2] == calc(data[0], data[1])}
}
Language features instead of Patterns…
© A
SE
RT
2006-2
010
Strategy Pattern
with interfaces
with closures
![Page 47: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/47.jpg)
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
– Aids refactoring
– Can help with test creation
– Avoids class proliferation
– But you still need testing
© A
SE
RT
2006-2
010
![Page 48: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/48.jpg)
Builder Pattern: MarkupBuilder…
• Create new builder
• Call pretended methods
(html, head, ...)
• Arguments are Closures
• Builder code looks very
declarative but is ordinary
Groovy program code and
can contain any kind of
logic
• Builder pattern from the GoF at the syntax-level
• Represents easily any nested tree-structured data
NodeBuilder, DomBuilder,
SwingBuilder, AntBuilder, …
© A
SE
RT
2006-2
010
import groovy.xml.*def b = new MarkupBuilder()b.html {
head { title 'Hello' }body {ul {
for (count in 1..5) {li "world $count"
} } } }
![Page 49: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/49.jpg)
...Builder Pattern: MarkupBuilder
© A
SE
RT
2006-2
010
<html><head><title>Hello</title>
</head><body><ul>
<li>world 1</li><li>world 2</li><li>world 3</li><li>world 4</li><li>world 5</li>
</ul></body>
</html>
import groovy.xml.*def b = new MarkupBuilder()b.html {
head { title 'Hello' }body {ul {
for (count in 1..5) {li "world $count"
} } } }
![Page 50: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/50.jpg)
Builder Pattern: SwingBuilderimport java.awt.FlowLayoutbuilder = 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) {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/
© A
SE
RT
2006-2
010
![Page 51: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/51.jpg)
Builder Pattern: JavaFX Script
Frame {title: "Hello World F3"width: 200content: Label {
text: "Hello World"}visible: true
}
© A
SE
RT
2006-2
010
![Page 52: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/52.jpg)
Builder Pattern: Cheri::Swing
# requires JRubyrequire 'rubygems'require 'cheri/swing'include Cheri::Swing
@frame = swing.frame('Hello') {size 500,200flow_layouton_window_closing {|event| @frame.dispose}button('Hit me') {on_click { puts 'button clicked' }
}}@frame.show
© A
SE
RT
2006-2
010
![Page 53: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/53.jpg)
Builder Pattern: AntBuilderdef ant = new AntBuilder()
ant.echo("hello") // let's just call one task
// create a block of Ant using the builder patternant.sequential {
myDir = "target/test/"mkdir(dir: myDir)copy(todir: myDir) {
fileset(dir: "src/test") {include(name: "**/*.groovy")
}}echo("done")
}
// now let's do some normal Groovy againfile = new File("target/test/AntTest.groovy")assert file.exists()
© A
SE
RT
2006-2
010
![Page 54: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/54.jpg)
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
© A
SE
RT
2006-2
010
![Page 55: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/55.jpg)
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, agePerson(name, age) {
this.name = namethis.age = age
}def haveBirthday() { age++ }String toString() { "$name is $age years old" }
}
class StaffMemberUsingInheritance extends Person {private salaryStaffMemberUsingInheritance(name, age, salary) {
super(name, age)this.salary = salary
}String toString() {
super.toString() + " and has a salary of $salary"}
}
© A
SE
RT
2006-2
010
![Page 56: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/56.jpg)
… 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 delegateprivate salaryStaffMemberUsingDelegation(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"}
}
© A
SE
RT
2006-2
010
![Page 57: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/57.jpg)
… 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
parameters rather than the traditional positional
parameters shown here (future versions of Ruby may
have this too)
© A
SE
RT
2006-2
010
![Page 58: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/58.jpg)
… 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
}
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()
println p2
p3.haveBirthday()
println p3
Tom is 21 years old and has a salary of 1000
Dick is 26 years old and has a salary of 1100
Harry is 31 years old and has a salary of 1200
© A
SE
RT
2006-2
010
![Page 59: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/59.jpg)
class StaffMemberUsingLibrary {private salaryprivate personStaffMemberUsingLibrary(name, age, salary) {
person = new Person(name, age)this.salary = salarydef delegator = new Delegator(StaffMemberUsingLibrary, person)delegator.delegate haveBirthday
}String toString() {
person.toString() + " and has a salary of $salary"}
}
… Delegation Pattern• Going Further
–The example shown (on the previous slide) codes the
delegate directly but both Groovy and Ruby let you
encapsulate the delegation pattern as a library:
• Groovy: Delegator, Injecto; Ruby: forwardable, delegate
–But only if I don’t want to add logic as I delegate
• E.g. If I wanted to make haveBirthday() increment salary
© A
SE
RT
2006-2
010
![Page 60: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/60.jpg)
QCON 2010 - 60
© A
SE
RT
2006-2
010
Better Design Patterns: Delegate…
import java.util.Date;
public class Event {private String title;private String url;private Date when;
public String getUrl() {return url;
}
public void setUrl(String url) {this.url = url;
}
public String getTitle() {return title;
}
public void setTitle(String title) {this.title = title;
}// ...
public Date getWhen() {return when;
}
public void setWhen(Date when) {this.when = when;
}
public boolean before(Date other) {return when.before(other);
}
public void setTime(long time) {when.setTime(time);
}
public long getTime() {return when.getTime();
}
public boolean after(Date other) {return when.after(other);
}// ...
![Page 61: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/61.jpg)
QCON 2010 - 61
© A
SE
RT
2006-2
010
…Better Design Patterns: Delegate…
import java.util.Date;
public class Event {private String title;private String url;private Date when;
public String getUrl() {return url;
}
public void setUrl(String url) {this.url = url;
}
public String getTitle() {return title;
}
public void setTitle(String title) {this.title = title;
}// ...
public Date getWhen() {return when;
}
public void setWhen(Date when) {this.when = when;
}
public boolean before(Date other) {return when.before(other);
}
public void setTime(long time) {when.setTime(time);
}
public long getTime() {return when.getTime();
}
public boolean after(Date other) {return when.after(other);
}// ...
boilerplate
![Page 62: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/62.jpg)
QCON 2010 - 62
© A
SE
RT
2006-2
010
…Better Design Patterns: Delegate
class Event {String title, url@Delegate Date when
}
def gr8conf = new Event(title: "GR8 Conference",url: "http://www.gr8conf.org",when: Date.parse("yyyy/MM/dd", "2009/05/18"))
def javaOne = new Event(title: "JavaOne",url: "http://java.sun.com/javaone/",when: Date.parse("yyyy/MM/dd", "2009/06/02"))
assert gr8conf.before(javaOne.when)
![Page 63: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/63.jpg)
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
© A
SE
RT
2006-2
010
![Page 64: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/64.jpg)
Singleton Pattern…• Pattern Intent
– Ensure that only one instance of a class is created
– Provide a global point of access to the object
– Allow multiple instances in the future without affecting a singleton class's clients
• Static language discussion
points
– Need exactly one instance of a class
and a well-known controlled access
point
• Allows for lazy creation of instance
– More flexible than static class
variables and methods alone
• Permits refinement of operations and
representation through subclassing
– Reduces name space clutter
• Compared to using static approach
– Multi-threading implications
– 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
© A
SE
RT
2006-2
010
![Page 65: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/65.jpg)
…Singleton Pattern…
• The details quickly get messy …
© A
SE
RT
2006-2
010
public final class Singleton {private static final class SingletonHolder {
static final Singleton singleton = new Singleton();}private Singleton() {}public static Singleton getInstance() {
return SingletonHolder.singleton;}
}
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;}
}
![Page 66: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/66.jpg)
…Singleton Pattern…
• State of the art approach in Java?– Use an IoC framework, e.g. Spring or Guice
© A
SE
RT
2006-2
010
import com.google.inject.*
@ImplementedBy(CalculatorImpl)interface Calculator {
def add(a, b)}
@Singletonclass CalculatorImpl implements Calculator {
private total = 0def add(a, b) { total++; a + b }def getTotalCalculations() { 'Total Calculations: ' + total }String toString() { 'Calc: ' + hashCode()}
}
class Client {@Inject Calculator calc// ...
}
def injector = Guice.createInjector()
![Page 67: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/67.jpg)
…Singleton Pattern…
• But it is easy using meta-programming– Old style
class Calculator {private total = 0def add(a, b) { total++; a + b }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.metaClassRegistryregistry.setMetaClass(Calculator, new CalculatorMetaClass())
© A
SE
RT
2006-2
010
![Page 68: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/68.jpg)
…Singleton Pattern…
• But it is easy using meta-programming
class Calculator {def total = 0def add(a, b) { total++; a + b }
}
def INSTANCE = new Calculator()Calculator.metaClass.constructor = { -> INSTANCE }
def c1 = new Calculator()def c2 = new Calculator()
assert c1.add(1, 2) == 3assert c2.add(3, 4) == 7
assert c1.is(c2)assert [c1, c2].total == [2, 2]
© A
SE
RT
2006-2
010
![Page 69: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/69.jpg)
…Singleton Pattern…
• Or annotations
© A
SE
RT
2006-2
010
@Singleton(lazy=true)class X {
def getHello () {"Hello, World!"
}}
println X.instance.hello
![Page 70: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/70.jpg)
…Singleton Pattern…
• And again with Ruby
class Aardvarkprivate_class_method :new@@instance = newdef Aardvark.instance@@instance
endend
© A
SE
RT
2006-2
010
class Aardvarkprivate_class_method :newdef Aardvark.instance@@instance = new if not @@instance@@instance
endend
module ThreadSafeSingletondef self.append_features(clazz)require 'thread'clazz.module_eval { private_class_method :new@instance_mutex = Mutex.newdef self.instance@instance_mutex.synchronize {@instance = new if not (@instance)@instance
}end
}end
end Source: http://c2.com/cgi/wiki?RubySingleton
![Page 71: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/71.jpg)
…Singleton Pattern
• Or for Python– Classic class version (pre 2.2)
– Non-classic class version
class Borg:_shared_state = {}def __init__(self):
self.__dict__ = self._shared_state
© A
SE
RT
2006-2
010
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
# UsagemySingleton1 = Singleton()mySingleton2 = Singleton()assert mySingleton1 is mySingleton2
Source: [10] and wikipedia
![Page 72: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/72.jpg)
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
© A
SE
RT
2006-2
010
![Page 73: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/73.jpg)
Pattern Summary
• Patterns can be replaced by language
features and libraries
• So patterns aren’t important any more!
© A
SE
RT
2006-2
010
...
![Page 74: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/74.jpg)
Topics
• Introduction
• Design patterns
Refactoring
• Polyglot programming
• SOLID principles
• Other topics
• More Info
© A
SE
RT
2006-2
010
![Page 75: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/75.jpg)
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’– Some refactorings are done differently
© A
SE
RT
2006-2
010
![Page 76: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/76.jpg)
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
// Before refactoringObject lastReading() {
return readings.lastElement()}
// After refactoringReading lastReading() {
return (Reading) readings.lastElement()}
© A
SE
RT
2006-2
010
![Page 77: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/77.jpg)
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?– Maybe but not usually
// Traditional Java styleList myIntList = new LinkedList()myIntList.add(new Integer(0))Integer result = (Integer) myIntList.iterator().next()
// Java generified styleList<Integer> myIntList2 = new LinkedList<Integer>()myIntList2.add(new Integer(0))Integer result2 = myIntList2.iterator().next()
// Groovier styledef myIntList3 = [0]def result3 = myIntList3.iterator().next()
© A
SE
RT
2006-2
010
![Page 78: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/78.jpg)
Enabling a functional style …
• Consider the Maximum Segment Sum
(MSS) problem– Take a list of integers; the MSS is the maximum of the sums of
any number of adjacent integers
• Imperative solution:
def numbers = [31,-41,59,26,-53,58,97,-93,-23,84]
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"
© A
SE
RT
2006-2
010
![Page 79: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/79.jpg)
… Enabling a functional style …
• A first attempt at a more functional style:
def numbers = [31,-41,59,26,-53,58,97,-93,-23,84]
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"
© A
SE
RT
2006-2
010
![Page 80: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/80.jpg)
… 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:
© A
SE
RT
2006-2
010
assert letters.inits() == [['a'],['a', 'b'],['a', 'b', 'c'],['a', 'b', 'c', 'd']
]
letters = ['a', 'b', 'c', 'd']
assert letters.tails() == [['d'],
['c', 'd'],['b', 'c', 'd'],
['a', 'b', 'c', 'd']]
![Page 81: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/81.jpg)
… Enabling a functional style
• An even more functional stylemss = max º sum* º (flatten º tails* º inits)
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() }
}
© A
SE
RT
2006-2
010
def segs = { it.inits()*.tails().sum() }
def solve = { segs(it)*.sum().max() }
def numbers = [31,-41,59,26,-53,58,97,-93,-23,84]
println "Maximum Segment Sum of $numbers is ${solve numbers}"
Source: http://hamletdarcy.blogspot.com/2008/07/groovy-vs-f-showdown-side-by-side.html
![Page 82: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/82.jpg)
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.
• Solution– Remove the parameter and let the receiver invoke the
method.
• Dynamic solution– Partial Application: Currying
© A
SE
RT
2006-2
010
![Page 83: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/83.jpg)
Replace parameter with method …
class Order {private int quantity, itemPriceOrder(q, p) {quantity = q; itemPrice = p}
double getPrice() {int basePrice = quantity * itemPriceint discountLevelif (quantity > 100) discountLevel = 2else discountLevel = 1double finalPrice = discountedPrice(basePrice, discountLevel)return finalPrice
}
private double discountedPrice(int basePrice, int discountLevel) {if (discountLevel == 2) return basePrice * 0.8return basePrice * 0.9
}}
println new Order(120, 5).price // => 480.0
© A
SE
RT
2006-2
010
Let's explore the
traditional refactoring
![Page 84: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/84.jpg)
… Replace parameter with method …
class Order {private int quantity, itemPriceOrder(q, p) {quantity = q; itemPrice = p}
double getPrice() {int basePrice = quantity * itemPriceint discountLevelif (quantity > 100) discountLevel = 2else discountLevel = 1double finalPrice = discountedPrice(basePrice, discountLevel)return finalPrice
}
private double discountedPrice(int basePrice, int discountLevel) {if (discountLevel == 2) return basePrice * 0.8return basePrice * 0.9
}}
println new Order(120, 5).price // => 480.0
© A
SE
RT
2006-2
010
![Page 85: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/85.jpg)
… Replace parameter with method …
class Order {private int quantity, itemPriceOrder(q, p) {quantity = q; itemPrice = p}
double getPrice() {int basePrice = quantity * itemPricedouble finalPrice = discountedPrice(basePrice)return finalPrice
}
private double discountedPrice(int basePrice) {if (getDiscountLevel() == 2) return basePrice * 0.8return basePrice * 0.9
}
private int getDiscountLevel() {if (quantity > 100) return 2return 1
}}println new Order(120, 5).price // => 480.0
© A
SE
RT
2006-2
010
![Page 86: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/86.jpg)
… Replace parameter with method …
class Order {private int quantity, itemPriceOrder(q, p) {quantity = q; itemPrice = p}
double getPrice() {int basePrice = quantity * itemPricedouble finalPrice = discountedPrice(basePrice)return finalPrice
}
private double discountedPrice(int basePrice) {if (getDiscountLevel() == 2) return basePrice * 0.8return basePrice * 0.9
}
private int getDiscountLevel() {if (quantity > 100) return 2return 1
}}println new Order(120, 5).price // => 480.0
© A
SE
RT
2006-2
010
![Page 87: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/87.jpg)
… Replace parameter with method …
class Order {private int quantity, itemPriceOrder(q, p) {quantity = q; itemPrice = p}
double getPrice() {return discountedPrice(getBasePrice())
}
private double discountedPrice(int basePrice) {if (getDiscountLevel() == 2) return basePrice * 0.8return basePrice * 0.9
}
private int getBasePrice() {quantity * itemPrice
}
private int getDiscountLevel() {if (quantity > 100) return 2return 1
}}println new Order(120, 5).price // => 480.0
© A
SE
RT
2006-2
010
![Page 88: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/88.jpg)
… Replace parameter with method …
class Order {private int quantity, itemPriceOrder(q, p) {quantity = q; itemPrice = p}
double getPrice() {return discountedPrice(getBasePrice())
}
private double discountedPrice(int basePrice) {if (getDiscountLevel() == 2) return basePrice * 0.8return basePrice * 0.9
}
private int getBasePrice() {quantity * itemPrice
}
private int getDiscountLevel() {if (quantity > 100) return 2return 1
}}println new Order(120, 5).price // => 480.0
© A
SE
RT
2006-2
010
![Page 89: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/89.jpg)
… Replace parameter with method …
class Order {private int quantity, itemPriceOrder(q, p) {quantity = q; itemPrice = p}
double getPrice() {return discountedPrice()
}
private double discountedPrice() {if (getDiscountLevel() == 2) return getBasePrice() * 0.8return getBasePrice() * 0.9
}
private int getBasePrice() {quantity * itemPrice
}
private int getDiscountLevel() {if (quantity > 100) return 2return 1
}}println new Order(120, 5).price // => 480.0
© A
SE
RT
2006-2
010
![Page 90: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/90.jpg)
… Replace parameter with method …
class Order {private int quantity, itemPriceOrder(q, p) {quantity = q; itemPrice = p}
double getPrice() {return discountedPrice()
}
private double discountedPrice() {if (getDiscountLevel() == 2) return getBasePrice() * 0.8return getBasePrice() * 0.9
}
private int getBasePrice() {quantity * itemPrice
}
private int getDiscountLevel() {if (quantity > 100) return 2return 1
}}println new Order(120, 5).price // => 480.0
© A
SE
RT
2006-2
010
![Page 91: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/91.jpg)
… Replace parameter with method
class Order {private int quantity, itemPriceOrder(q, p) {quantity = q; itemPrice = p}
double getPrice() {if (getDiscountLevel() == 2) return getBasePrice() * 0.8return getBasePrice() * 0.9
}
private getBasePrice() {quantity * itemPrice
}
private getDiscountLevel() {if (quantity > 100) return 2return 1
}}println new Order(120, 5).price // => 480.0
© A
SE
RT
2006-2
010
Note the now small
parameter lists
![Page 92: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/92.jpg)
Some functional style …
class Order {private int quantity, itemPriceOrder(q, p) {quantity = q; itemPrice = p}
def discountedPrice = { basePrice, discountLevel ->discountLevel == 2 ? basePrice * 0.8 : basePrice * 0.9 }
def price = {int basePrice = quantity * itemPricedef discountLevel = (quantity > 100) ? 2 : 1discountedPrice(basePrice, discountLevel) }
}println new Order(120, 5).price() // => 480.0
© A
SE
RT
2006-2
010
Traditional refactoring
still applicable if we used
closures rather than methods...
![Page 93: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/93.jpg)
… Some functional style …
class Order {private int quantity, itemPriceOrder(q, p) {quantity = q; itemPrice = p}
def basePrice = { quantity * itemPrice }
def discountLevel = { quantity > 100 ? 2 : 1 }
def price = {discountLevel() == 2 ? basePrice() * 0.8 : basePrice() * 0.9 }
}println new Order(120, 5).price() // => 480.0
© A
SE
RT
2006-2
010
... as we see here
![Page 94: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/94.jpg)
… Some functional style …
class Order {private int quantity, itemPriceOrder(q, p) {quantity = q; itemPrice = p}
def basePrice = { quantity * itemPrice }
def discountLevel = { quantity > 100 ? 2 : 1 }
def discountedPrice = { basePrice, discountLevel ->discountLevel == 2 ? basePrice * 0.8 : basePrice * 0.9 }
def price = {discountedPrice.curry(basePrice()).curry(discountLevel()).call() }
}println new Order(120, 5).price() // => 480.0
© A
SE
RT
2006-2
010
But we can also use currying
![Page 95: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/95.jpg)
… Some functional style
class Order {private int quantity, itemPriceOrder(q, p) {quantity = q; itemPrice = p}
def basePrice = { quantity * itemPrice }
def discountLevel = { quantity > 100 ? 2 : 1 }
def discountedPrice(basePrice, discountLevel) {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
© A
SE
RT
2006-2
010
We can also use currying with methods
![Page 96: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/96.jpg)
Closure Refactoring …
• Complex code involving closures
// Before refactoringdef phrase = "The quick brown fox jumps over the lazy dog"def result = phrase.toLowerCase().toList().
findAll{ it in "aeiou".toList() }. // like WHERE ...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
© A
SE
RT
2006-2
010
![Page 97: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/97.jpg)
… Closure Refactoring …
• Possible Refactoring
// Refactored helper closuresdef 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 }def entriesAsPrettyString = { "$it.key:${it.value.size()}" }def withCommaDelimiter = ", "
// Refactored main closureprintln lowercaseLetters.
findAll(vowels).groupBy(self).findAll(occursMoreThanOnce).sort(byReverseKey).collect(entriesAsPrettyString).join(withCommaDelimiter)
© A
SE
RT
2006-2
010
![Page 98: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/98.jpg)
… Closure Refactoring
# Add group_by to the Array classclass Arraydef group_bygroup_hash = {}uniq.each do |e|group_hash[e] = select { |i| i == e }.size
endgroup_hash
endend
# Before refactoringphrase = "The quick brown fox jumps over the lazy dog"puts phrase.downcase.scan(/[aeiou]/). # like WHERE ...group_by. # like GROUP BY ...select { |key, value| value > 1 }. # like HAVING ...sort.reverse. # like ORDER BY ... DESCcollect{ |key, value| "#{key}:#{value}" }.join(', ')
# Refactored versionlowercase_letters = phrase.downcasevowels = /[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(', ')
© A
SE
RT
2006-2
010
![Page 99: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/99.jpg)
Unnecessary Complexity Refactoring
• Dynamic Code Creation– What to look for: Code uses eval,
class_eval or module_eval to build new
code dynamically
– Issues: harder to read, fluid abstractions
are harder to understand, harder to test
and debug
– What to do:
• move string form of eval to block forms or use
define_method
• move method_missing to use class_eval
(example of Replace Dynamic Receptor with
Dynamic Method Definition)
• consider using Move Eval from Run-time to
Parse-time to overcome bottlenecks
© A
SE
RT
2006-2
010
Source: Dynamic Code Creation in Chapter 7: Unnecessary Complexity (Refactoring in Ruby)
![Page 100: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/100.jpg)
Topics
• Introduction
• Design patterns
• Refactoring
Polyglot programming
• SOLID principles
• Other topics
• More Info
© A
SE
RT
2006-2
010
![Page 101: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/101.jpg)
Programming Paradigms...• Named state
(imperative style –
leads to modularity)
vs unnamed state
(functional and logic
style)
• Deterministic vs
observable
nondeterminism
(threads, guards)
• Sequential vs
concurrent
(message passing
and shared state
styles)
© A
SE
RT
2006-2
010
htt
p:/
/ww
w.info
.ucl.ac.b
e/~
pvr/
para
dig
ms.h
tml
![Page 102: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/102.jpg)
...Programming Paradigms
© A
SE
RT
2006-2
010
htt
p:/
/ww
w.info
.ucl.ac.b
e/~
pvr/
para
dig
ms.h
tml
![Page 103: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/103.jpg)
Polyglot Programming…
• Groovy calling clojure
@Grab('org.clojure:clojure:1.0.0')import clojure.lang.Compilerimport clojure.lang.RT
def src = new File('temp.clj')src.text = '''(ns groovy)(defn factorial [n]
(if (< n 2)1(* n (factorial (- n 1)))))
'''src.withReader { reader ->
Compiler.load reader}def fac = RT.var('groovy', 'factorial') println fac.invoke(5)
© A
SE
RT
2006-2
010
![Page 104: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/104.jpg)
…Polyglot Programming
• C# calling F#
// F# Code
type FCallback = delegate of int*int -> int;;type FCallback =
delegate of int * int -> int
let f3 (f:FCallback) a b = f.Invoke(a,b);;val f3 : FCallback -> int -> int -> int
// C# Code
// method gets converted to the delegate automatically in C#int a = Module1.f3(Module1.f2, 10, 20);
© A
SE
RT
2006-2
010
![Page 105: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/105.jpg)
Topics
• Introduction
• Design patterns
• Refactoring
• Polyglot programming
SOLID principles
• Other topics
• More Info
© A
SE
RT
2006-2
010
![Page 106: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/106.jpg)
© A
SE
RT
2006-2
010
Source: http://www.lostechies.com/content/pablo_ebook.aspx (Derick Bailey)
![Page 107: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/107.jpg)
SOLID Principles
• Single Responsibility Principle
• Open/Closed Principle
• Liskov Substitution Principle
• Interface Segregation Principle
• Dependency Inversion Principle
© A
SE
RT
2006-2
010
![Page 108: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/108.jpg)
© A
SE
RT
2006-2
010
Source: http://www.lostechies.com/content/pablo_ebook.aspx
![Page 109: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/109.jpg)
Open-Closed Principle...
• Fundamental rule to make
your software flexible– Many other OOP principles, methodologies and
conventions revolve around this principle
• Open-Closed Principle (OCP) states:
• Software entities should be open for
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
© A
SE
RT
2006-2
010
Picture source: http://www.vitalygorn.com
![Page 110: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/110.jpg)
...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:– 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!
© A
SE
RT
2006-2
010
![Page 111: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/111.jpg)
Optional
FactoryOptional
Factory
...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
break or require recompilation in the future
© A
SE
RT
2006-2
010
Extendible
Class A
Class A’
Class A
User
Class A’
User
Interface
Class A Class A’
Class A
User
Class A’
User
...
![Page 112: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/112.jpg)
...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
• In OCP terms, it is not open for extension
• Dynamic language solution: open classes
String.metaClass.swapCase = {delegate.collect{ c ->c in 'A'..'Z' ?c.toLowerCase() :c.toUpperCase()
}.join()}assert "Foo".swapCase() == "fOO"
© A
SE
RT
2006-2
010
#lightopen Stringtype System.String with
member x.swapCase =seq { for letter in x.ToCharArray() do
if (System.Char.IsLower(letter))then yield System.Char.ToUpper(letter)else yield System.Char.ToLower(letter)
}printfn "result: %A" "Foo".swapCase
...
![Page 113: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/113.jpg)
...Open-Closed Principle...
• Part II: Violating OCP (see [15])
class Square {def side
}class Circle {
def radius}
class AreaCalculator {double area(shape) {
switch (shape) {case Square:
return shape.side * shape.sidecase Circle:
return Math.PI * shape.radius ** 2}
}}
© A
SE
RT
2006-2
010
![Page 114: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/114.jpg)
...Open-Closed Principle...
• 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
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)}"}
© A
SE
RT
2006-2
010
![Page 115: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/115.jpg)
...Open-Closed Principle...
• Dynamic shapes– No issue with adding Triangle but sorting is an issue *
class Square {private sidedouble area() { side ** 2 }
}class Circle {
private radiusdouble area() { Math.PI * radius ** 2 }
}
def shapes = [new Square(side:3),new Square(side:2),new Circle(radius:1.5)
]// unsorteddef prettyPrint = { s ->
println "Area of $s.class.name is ${s.area()}" }shapes.each(prettyPrint)
Area of Square is 9.0
Area of Square is 4.0
Area of Circle is 7.0685834705770345
Note: Duck-type
polymorphism
instead of
inheritance
polymorphism,
i.e. no base Shape
(abstract) class or
interface.
Hmm… what are
the testing
implications when
I add Triangle?
© A
SE
RT
2006-2
010
* Our abstractions never designed sorting to be
one of the things open for extension. See [15].
...
![Page 116: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/116.jpg)
...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
// sorted by areadef byArea = { s -> s.area() }shapes.sort(byArea).each(prettyPrint)
Area of Square is 4.0
Area of Circle is 7.0685834705770345
Area of Square is 9.0
// sorted circles before squares but otherwise by areadef byClassNameThenArea = { sa, sb ->
sa.class.name == sb.class.name ?sa.area() <=> sb.area() :sa.class.name <=> sb.class.name
}shapes.sort(byClassNameThenArea).each(prettyPrint)
Area of Circle is 7.06858...
Area of Square is 4.0
Area of Square is 9.0
Note: Make sure your
closures are testable.
© A
SE
RT
2006-2
010
...
![Page 117: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/117.jpg)
...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)
• 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, ...
© A
SE
RT
2006-2
010
![Page 118: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/118.jpg)
...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
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
© A
SE
RT
2006-2
010
![Page 119: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/119.jpg)
...Open-Closed Principle...
class Square {double side
}
class Rectangle {double height, width
}
class Circle {double radius
}
class Geometry {def area(shape) {switch (shape) {
case Square: return shape.side ** 2case Rectangle: return shape.height * shape.widthcase Circle: return PI * shape.radius ** 2
}}
}
© A
SE
RT
2006-2
010
Can add perimeter() here without shape classes changing but if we
added a Triangle, area(), perimeter() etc. would need to change.
![Page 120: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/120.jpg)
...Open-Closed Principle...
interface Shape {double area()
}
class Square implements Shape {double sidedouble area() { side ** 2 }
}
class Rectangle implements Shape {double height, widthdouble area() { height * width }
}
class Circle implements Shape {double radiusdouble area() { PI * radius ** 2 }
}
© A
SE
RT
2006-2
010
If we add perimeter() here, each
shape class must change but we can
add new shapes with no changes
![Page 121: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/121.jpg)
...Open-Closed Principle...
class Square {double sidedouble area() { side ** 2 }
}
class Rectangle {double height, widthdouble area() { height * width }
}
class Circle {double radiusdouble area() { PI * radius ** 2 }
}
© A
SE
RT
2006-2
010
We can easily add perimeter() here
but for any code requiring the perimeter()
method to exist, we should test that code
with all shapes.
![Page 122: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/122.jpg)
...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
• 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
© A
SE
RT
2006-2
010
![Page 123: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/123.jpg)
Topics
• Introduction
• Design patterns
• Refactoring
• Polyglot programming
• SOLID principles
Other topics
• More Info
© A
SE
RT
2006-2
010
![Page 124: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/124.jpg)
Other topics
• The need for Dependency Injection
• The need for Mocking frameworks
• Concurrency
• Feature interaction
• Writing DSLs
© A
SE
RT
2006-2
010
![Page 125: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/125.jpg)
Topics
• Introduction
• Design patterns
• Refactoring
• Polyglot programming
• SOLID principles
• Other topics
More Info
© A
SE
RT
2006-2
010
![Page 126: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/126.jpg)
Further Information…• [1] Dynamic vs. Static Typing — A Pattern-Based Analysis, Pascal Costanza,
University of Bonn, 2004http://p-cos.net/documents/dynatype.pdf
• [2] Interface-Oriented Design, Ken Pugh, Pragmatic Programmers, 2006
• [3] Bruce Eckel, Does Java need Checked Exceptions?www.mindview.net/Etc/Discussions/CheckedExceptions
• [4] Null Object, Kevlin Henney, Proceedings EuroPLoP 2002
• [5] Design Patterns in Dynamic Programming, Peter Norvig, March 1998http://www.norvig.com/design-patterns/
• [6] Advanced Programming Language Features and Software Engineering: Friend or Foe?, Greg Sullivan, April 2002http://people.csail.mit.edu/gregs/proglangsandsofteng.pdf
• [7] JunGL: a Scripting Language for Refactoring, Mathieu Verbaere et al, May 2006http://progtools.comlab.ox.ac.uk/publications/icse06jungl
• [8] Rails for Java Developers, Halloway et al, Pragmatic Bookshelf, 2007, Chapter 3, Ruby Eye for the Java Guy
• [9] Building DSLs in Static & Dynamic languageshttp://www.nealford.com/downloads/conferences/canonical/Neal_Ford-Building_DSLs_in_Static_and_Dynamic_Languages-handouts.pdf
• [10] Five Easy Pieces: Simple Python Non-Patterns, Alex Martellihttp://www.aleax.it/5ep.html
• [11] Emergent Design, Scott L. Bain, 2008
© A
SE
RT
2006-2
010
![Page 127: Dynamic Language Practices](https://reader033.fdocuments.in/reader033/viewer/2022052822/554f497ab4c905423f8b4bd1/html5/thumbnails/127.jpg)
…Further Information• [12] Design Patterns: Elements of Reusable Object-Oriented Software, Erich
Gamma, Richard Helm, Ralph Johnson, John Vlissides, 1995
• [13] Refactoring: Improving the Design of Existing Code, Martin Fowler, 1999
• [14] Effective Java Programming Language Guide, Erich Gamma, Joshua Bloch, 2001
• [15] Agile Software Development, Principles, Patterns, and Practices, Robert C Martin, 2002
• [16] Composing Features and Resolving Interactions, Jonathan Hay and Joanne Atlee, University of Waterloo
• [17] Handling Feature Interactions in the Language for End System Services, Xiaotao Wua and Henning Schulzrinne, January 2007
• [18] FAQ Sheet on Feature Interaction, Pamela Zavehttp://www.research.att.com/~pamela/faq.html
• [19] Liskov Substitution Principle and the Ruby Core Libraries, Dean Wamplerhttp://blog.objectmentor.com/articles/tag/liskovsubstitutionprinciple
• [20] Liskov Substitution in Dynamic Languages, Michael Feathershttp://butunclebob.com/ArticleS.MichaelFeathers.LiskovSubstitutionInDynamicLanguages
• [21] Domain-Specific Languages: An Annotated Bibliography, van Deursen et alhttp://homepages.cwi.nl/~arie/papers/dslbib/
• [22] Agile Principles, Patterns, and Practices in C#, Martin C. Robert et al, 2006
• [23] Clean Code, Robert C. Martin, 2008
• [24] The Craftsman: 51, Ruby Visitor, Robert C Martin, August 2007http://www.objectmentor.com/resources/articles/Craftsman51.pdf
• [25] http://www.info.ucl.ac.be/~pvr/paradigms.html
• [26] http://p-cos.net/documents/dynatype.pdf
© A
SE
RT
2006-2
010