Download - Paulking dlp
![Page 1: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/1.jpg)
Dynamic Language Practices“Unlearning Java/C#”Dr Paul King - ASERT
![Page 2: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/2.jpg)
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
others change the ground rules
– Many of the rules and patterns we
have been taught no longer apply
(c) A
SE
RT
2006-2
009
![Page 3: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/3.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.
• In the dynamic
language world,
are these guidelines
FACT or MYTH !!
(c) A
SE
RT
2006-2
009
![Page 4: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/4.jpg)
Examples to ponder
• What does Immutability mean?– When even constants can be changed
• What does encapsulation
mean?– When I can peek at anything
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
(c) A
SE
RT
2006-2
009
![Page 5: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/5.jpg)
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
• 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
(c) A
SE
RT
2006-2
009
![Page 6: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/6.jpg)
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
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?
(c) A
SE
RT
2006-2
009
![Page 7: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/7.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
(c) A
SE
RT
2006-2
009
![Page 8: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/8.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
(c) A
SE
RT
2006-2
009
![Page 9: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/9.jpg)
… 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) A
SE
RT
2006-2
009
![Page 10: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/10.jpg)
… Static vs Dynamic Typing …
• An example
(c) A
SE
RT
2006-2
009
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 11: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/11.jpg)
… Static vs Dynamic Typing
(c) A
SE
RT
2006-2
009
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 12: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/12.jpg)
… 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) A
SE
RT
2006-2
009
...but not a total lie either...
...dynamic languages certainly
assist with removing duplication
and sometimes removing clutter
and boilerplate code...
![Page 13: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/13.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()
(c) A
SE
RT
2006-2
009
I tend to use Explicit types
for major boundaries and
implicit types internally.
![Page 14: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/14.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
(c) A
SE
RT
2006-2
009
Source: Rick DeNatale
© David Friel
![Page 15: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/15.jpg)
Typing approaches and IDEs…
• Class A has a bit of duplication
(c) A
SE
RT
2006-2
009
class A {def helperdef make() {
helper.invoke('create')}def get() {
helper.invoke('read')}def change() {
helper.invoke('update')}def remove() {
helper.invoke('delete')}
}
![Page 16: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/16.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)}
}
(c) A
SE
RT
2006-2
009
![Page 17: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/17.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])}
}
(c) A
SE
RT
2006-2
009
...
![Page 18: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/18.jpg)
… Typing approaches and IDEs …
(c) A
SE
RT
2006-2
009
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 19: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/19.jpg)
… Typing approaches and IDEs
• … At the expense of IDE completion?
(c) A
SE
RT
2006-2
009
...
But remember:
“clearly express intent”
![Page 20: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/20.jpg)
Language features instead of Patterns …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)) }
(c) A
SE
RT
2006-2
009
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 21: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/21.jpg)
…Language features instead of Patterns…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))) }
(c) A
SE
RT
2006-2
009
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 22: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/22.jpg)
… 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)) }
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?
(c) A
SE
RT
2006-2
009
Further reading: James Lyndsay, Agile is Groovy, Testing is Square
![Page 23: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/23.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
– But you still need testing
(c) A
SE
RT
2006-2
009
![Page 24: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/24.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()
… Language features instead of Patterns
(c) A
SE
RT
2006-2
009
See also Ruby Visitor [24]
Visitor Pattern
without closures
with closures
![Page 25: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/25.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
(c) A
SE
RT
2006-2
009
![Page 26: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/26.jpg)
Strategy Pattern
(c) A
SE
RT
2006-2
009
Source: http://nealford.com/
![Page 27: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/27.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…
(c) A
SE
RT
2006-2
009
Strategy Pattern
with interfaces
with closures
![Page 28: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/28.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
(c) A
SE
RT
2006-2
009
![Page 29: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/29.jpg)
… Language features instead of Patterns …
• 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, …
(c) A
SE
RT
2006-2
009
import groovy.xml.*def b = new MarkupBuilder()b.html {
head { title 'Hello' }body {
ul {for (count in 1..5) {
li "world $count"} } } }
![Page 30: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/30.jpg)
… Language features instead of Patterns
(c) A
SE
RT
2006-2
009
<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 31: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/31.jpg)
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/
(c) A
SE
RT
2006-2
009
![Page 32: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/32.jpg)
JavaFX Script
Frame {title: "Hello World F3"width: 200content: Label {
text: "Hello World"}visible: true
}
(c) A
SE
RT
2006-2
009
![Page 33: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/33.jpg)
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
(c) A
SE
RT
2006-2
009
![Page 34: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/34.jpg)
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()
(c) A
SE
RT
2006-2
009
![Page 35: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/35.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
(c) A
SE
RT
2006-2
009
![Page 36: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/36.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"}
}
(c) A
SE
RT
2006-2
009
![Page 37: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/37.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"}
}
(c) A
SE
RT
2006-2
009
![Page 38: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/38.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)
(c) A
SE
RT
2006-2
009
![Page 39: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/39.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
(c) A
SE
RT
2006-2
009
![Page 40: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/40.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
(c) A
SE
RT
2006-2
009
![Page 41: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/41.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
(c) A
SE
RT
2006-2
009
![Page 42: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/42.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
(c) A
SE
RT
2006-2
009
![Page 43: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/43.jpg)
…Singleton Pattern…
• The details quickly get messy …
(c) A
SE
RT
2006-2
009
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 44: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/44.jpg)
…Singleton Pattern…
• State of the art approach in Java?– Use an IoC framework, e.g. Spring or Guice
(c) A
SE
RT
2006-2
009
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 45: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/45.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())
(c) A
SE
RT
2006-2
009
![Page 46: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/46.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]
(c) A
SE
RT
2006-2
009
![Page 47: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/47.jpg)
…Singleton Pattern…
• And again with Ruby
class Aardvarkprivate_class_method :new@@instance = newdef Aardvark.instance@@instance
endend
(c) A
SE
RT
2006-2
009
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 48: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/48.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
(c) A
SE
RT
2006-2
009
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 49: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/49.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
(c) A
SE
RT
2006-2
009
![Page 50: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/50.jpg)
Pattern Summary
• Patterns can be replaced by language
features and libraries
• So patterns aren‟t important any more!
(c) A
SE
RT
2006-2
009
...
![Page 51: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/51.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
(c) A
SE
RT
2006-2
009
![Page 52: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/52.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()}
(c) A
SE
RT
2006-2
009
![Page 53: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/53.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()
(c) A
SE
RT
2006-2
009
![Page 54: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/54.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"
(c) A
SE
RT
2006-2
009
![Page 55: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/55.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"
(c) A
SE
RT
2006-2
009
![Page 56: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/56.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:
(c) A
SE
RT
2006-2
009
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 57: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/57.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() }
}
(c) A
SE
RT
2006-2
009
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 58: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/58.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
(c) A
SE
RT
2006-2
009
![Page 59: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/59.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
(c) A
SE
RT
2006-2
009
![Page 60: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/60.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
(c) A
SE
RT
2006-2
009
![Page 61: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/61.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
(c) A
SE
RT
2006-2
009
![Page 62: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/62.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
(c) A
SE
RT
2006-2
009
![Page 63: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/63.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
(c) A
SE
RT
2006-2
009
![Page 64: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/64.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
(c) A
SE
RT
2006-2
009
![Page 65: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/65.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
(c) A
SE
RT
2006-2
009
![Page 66: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/66.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
(c) A
SE
RT
2006-2
009
![Page 67: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/67.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
(c) A
SE
RT
2006-2
009
Note the now small
parameter lists
![Page 68: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/68.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
(c) A
SE
RT
2006-2
009
![Page 69: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/69.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
(c) A
SE
RT
2006-2
009
![Page 70: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/70.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
(c) A
SE
RT
2006-2
009
![Page 71: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/71.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
(c) A
SE
RT
2006-2
009
![Page 72: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/72.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
(c) A
SE
RT
2006-2
009
![Page 73: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/73.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)
(c) A
SE
RT
2006-2
009
![Page 74: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/74.jpg)
… Closure Refactoring
# Add group_by to the Array classclass Array
def group_bygroup_hash = {}uniq.each do |e|
group_hash[e] = select { |i| i == e }.sizeendgroup_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(', ')
(c) A
SE
RT
2006-2
009
![Page 75: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/75.jpg)
• This is the end of the talk
(c) ASERT 2006-2009
![Page 76: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/76.jpg)
• This is the end of the talk
• NO!
• We haven’t questioned some fundamental principles yet!
(c) ASERT 2006-2009
![Page 77: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/77.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
(c) A
SE
RT
2006-2
009
Picture source: http://www.vitalygorn.com
![Page 78: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/78.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!
(c) A
SE
RT
2006-2
009
![Page 79: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/79.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
(c) A
SE
RT
2006-2
009
Extendible
Class A
Class A‟
Class A
User
Class A‟
User
Interface
Class A Class A‟
Class A
User
Class A‟
User
...
![Page 80: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/80.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"
(c) A
SE
RT
2006-2
009
#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 81: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/81.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}
}}
(c) A
SE
RT
2006-2
009
![Page 82: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/82.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)}"}
(c) A
SE
RT
2006-2
009
![Page 83: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/83.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?
(c) A
SE
RT
2006-2
009
* Our abstractions never designed sorting to be
one of the things open for extension. See [15].
...
![Page 84: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/84.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.
(c) A
SE
RT
2006-2
009
...
![Page 85: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/85.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, ...
(c) A
SE
RT
2006-2
009
![Page 86: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/86.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
(c) A
SE
RT
2006-2
009
![Page 87: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/87.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
}}
}
(c) A
SE
RT
2006-2
009
Can add perimeter() here without shape classes changing but if we
added a Triangle, area(), perimeter() etc. would need to change.
![Page 88: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/88.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 }
}
(c) A
SE
RT
2006-2
009
If we add perimeter() here, each
shape class must change but we can
add new shapes with no changes
![Page 89: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/89.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 }
}
(c) A
SE
RT
2006-2
009
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 90: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/90.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
(c) A
SE
RT
2006-2
009
![Page 91: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/91.jpg)
...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) A
SE
RT
2006-2
009
![Page 92: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/92.jpg)
• This is the end of the talk
(c) ASERT 2006-2009
![Page 93: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/93.jpg)
• 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
![Page 94: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/94.jpg)
What & why of dependency injection?
• Construction by hand
• Factory pattern
• Service locator
• Dependency injection
(c) A
SE
RT
2006-2
009
Loose
Coupling
Flexibility
Testability
Reusability
Lifecycle
Control
Central
Control
![Page 95: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/95.jpg)
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?– 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
(c) A
SE
RT
2006-2
009
![Page 96: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/96.jpg)
… Dependency Injection vs Metaprogramming …
• Without dependency injection
class PrintService {
private printer = new PhysicalPrinter('Canon i9900')
}
class PrintServiceTest extends GroovyTestCase {
def testPrintService() {
def printService = new PrintService()
printService.print()
// go to the printer and fetch the page
}
}
class PrintService
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
(c) A
SE
RT
2006-2
009
![Page 97: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/97.jpg)
… Dependency Injection vs Metaprogramming …
• Improves testability
class TestablePrintService {
def printer
}
class TestablePrintServiceTest extends GroovyTestCase {
def testPrintService() {
def printService = new TestablePrintService()
printService.printer = new StubPrinter()
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
(c) A
SE
RT
2006-2
009
![Page 98: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/98.jpg)
… Dependency Injection vs Metaprogramming …
• Improves reuse
class ReusablePrintService
attr_accessor :printer
# interesting printer logic below
# ...
end
# One customer may use a network PostScript printer
class PostScriptPrinter
# ...
end
# The next customer may use a local Windows printer.
class LocalWindowsPrinter
# ...
end
# No change is required to the ReusablePrintService!
class UnreusablePrintService
def initialise(printer_name)
@printer = PostScriptPrinter.new(ip_address)
end
# printer logic below
# ...
end
# What if I don’t want to use a Physical Printer?
Without DI
(c) A
SE
RT
2006-2
009
![Page 99: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/99.jpg)
… Dependency Injection vs Metaprogramming …
• Printer dependency can be redefined in
test code
• Benefits of Dependency Injection only
become apparent in larger projects– where we need lots of reuse and centralized
configuration of services
• In smaller projects, this is just a burden
# Create stub/mock services in a separate module
Printer = Stubs::Printer
class PrintServiceTest < Test::Unit
def test_print_service
# The Printer instantiated by this class
# is now a stub!
print_service = UntestablePrintService.new
print_service.do_something_interesting_with_printer
end
end
(c) A
SE
RT
2006-2
009
![Page 100: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/100.jpg)
… Dependency Injection vs Metaprogramming …
• Improved lifecycle control– Guice and Spring let you declaratively specify
Singletons
• But see the discussion on Singleton Patterns
(c) A
SE
RT
2006-2
009
![Page 101: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/101.jpg)
… Dependency Injection vs Metaprogramming…
• Downsides.....– Code becomes more complex
– Constructors (and/or interfaces) contain noise
– Potentially need to introduce (many) new interfaces
– There is some magic
• Do I really need this?– In Java, for medium to large projects, the consensus
is yes!
– But Ruby and Groovy are extremely dynamic
– We can rely on duck typing
– We can change classes at runtime
– We can intercept object creation
(c) A
SE
RT
2006-2
009
![Page 102: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/102.jpg)
… Dependency Injection vs Metaprogramming …
• But should we abandon using these types
of frameworks altogether?– Not for large systems
– Yes we can step around many of the problems which
make such frameworks especially attractive
– But they typically require us to use a very beneficial
organizational discipline across our dependencies
– A great example: Dependency Injection in Ruby
http://onestepback.org/index.cgi/Tech/Ruby/DependencyInjectionInRuby.rdoc
(c) A
SE
RT
2006-2
009
![Page 103: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/103.jpg)
… Dependency Injection vs Metaprogramming …
• Traditional
• Service
Locator
class WebAppdef initialize
@quotes = StockQuotes.new@authenticator = Authenticator.new@database = Database.new@logger = Logger.new@error_handler = ErrorHandler.new
end# ...
end
def create_applicationlocator = {}locator[:logger] = Logger.newlocator[:error_handler] = ErrorHandler.new(locator)locator[:quotes] = StockQuotes.new(locator)locator[:database] = Database.new(locator)locator[:authenticator] = Authenticator.new(locator)locator[:webapp] = WebApp.new(locator)
end
class StockQuotesdef initialize(locator)
@error_handler = locator[:error_handler]@logger = locator[:logger]
end# ...
end
(c) A
SE
RT
2006-2
009
![Page 104: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/104.jpg)
… Dependency Injection vs Metaprogramming
• Using DIdef create_application
container = DI::Container.newcontainer.register(:logfilename) { "logfile.log" }container.register(:db_user) { "jim" }container.register(:db_password) { "secret" }container.register(:dbi_string) { "DBI:Pg:example_data" }
container.register(:app) { |c|app = WebApp.new(c.quotes, c.authenticator, c.database)app.logger = c.loggerapp.set_error_handler c.error_handlerapp
}
container.register(:quotes) { |c|StockQuotes.new(c.error_handler, c.logger)
}
container.register(:authenticator) { |c|Authenticator.new(c.database, c.logger, c.error_handler)
}
container.register(:database) { |c|DBI.connect(c.dbi_string, c.db_user, c.db_password)
}
container.register(:logger) { |c| Logger.new(c.logfilename) }container.register(:error_handler) { |c|
errh = ErrorHandler.newerrh.logger = c.loggererrh
}end
(c) A
SE
RT
2006-2
009
![Page 105: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/105.jpg)
Dependency Injection Framework Verdict
• DI frameworks aren‟t strictly needed when
using dynamic languages
• DI frameworks provide no value for
dynamic languages!
(c) A
SE
RT
2006-2
009
![Page 106: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/106.jpg)
Flexible Mocks …
• Mocking is an important aspect to
interaction-based testing
• But with dynamic languages, you don‟t
always need a complex mocking package:– Stubs
– Closures
– Maps
– Meta-programming
• Same goes for Behavior-Driven Design
packages– But that‟s another story
(c) A
SE
RT
2006-2
009
![Page 107: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/107.jpg)
class ClosureMocking {String methodOne() {
methodTwo()}String methodTwo() {
"television"}
}
def emc = new ExpandoMetaClass(ClosureMocking,true)emc.methodTwo = {-> "radio" }emc.initialize()
assert "radio" == new ClosureMocking().methodOne()
class MyApp {def factorydef loggerdef doBusinessLogic(param) {
def myObj = factory.instancemyObj.doSomething(param)myObj.doSomethingElse(param)logger.log('Something done with: ' + param)
}}
def factory = [instance: businessObj]
def logger = new Expando()logger.log = { msg ->
assert msg == 'Something done with: ' + param }
… Flexible Mocks
<= „mock‟ using meta-programming
Traditionally, we‟d
use mocks for factory
and logger, here we use
Expandos and Maps =>
(c) A
SE
RT
2006-2
009
![Page 108: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/108.jpg)
Aspects vs Meta-programmingclass TimingInterceptor extends TracingInterceptor {
private beforeTimedef beforeInvoke(object, String methodName, Object[] arguments) {
super.beforeInvoke(object, methodName, arguments)beforeTime = System.currentTimeMillis()
}public Object afterInvoke(Object object, String methodName,
Object[] arguments, Object result) {super.afterInvoke(object, methodName, arguments, result)def duration = System.currentTimeMillis() - beforeTimewriter.write("Duration: $duration ms\n")writer.flush()return result
}}
def proxy = ProxyMetaClass.getInstance(util.CalcImpl.class)proxy.interceptor = new TimingInterceptor()proxy.use {
assert 7 == new util.CalcImpl().add(1, 6)}// =>// before util.CalcImpl.ctor()// after util.CalcImpl.ctor()// Duration: 0 ms// before util.CalcImpl.add(java.lang.Integer, java.lang.Integer)// after util.CalcImpl.add(java.lang.Integer, java.lang.Integer)// Duration: 16 ms
(c) A
SE
RT
2006-2
009
![Page 109: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/109.jpg)
Checked vs unchecked exceptions
• From [1]:– “Statically checked exceptions lead to leaking
abstractions. This happens when a component
interacts with another component that declares to
throw unrelated exceptions that stem from a third
component but which can only be handled on a
higher level.”
• Is the debate less relevant now?– Groovy auto converts checked exceptions into
unchecked
– Techniques such as autoboundaries can be used to
overcome issues with existing code that abuses
checked exceptions
See: http://code.google.com/p/agileplugins/
(c) A
SE
RT
2006-2
009
![Page 110: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/110.jpg)
Immutability
• Why?– Simpler classes
– Good for certain kinds of domain objects
– No issues with concurrency
(c) A
SE
RT
2006-2
009
![Page 111: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/111.jpg)
Strict Class Immutability Rules (Bloch #13)
1. No mutator methods (that modify state)
2. Ensure methods can‟t be overridden– Prevents careless or malicious subclasses from
compromising the immutable behavior of the class
3. Make all fields private and final– Clearly expresses your intentions in an enforceable
manner (not strictly required but often desirable)
4. Ensure exclusive access to any mutable
components– Ensure that clients of the class cannot obtain
references to any mutable object fields and make
defensive copies where needed in constructors,
accessors, and readObject methods
(c) A
SE
RT
2006-2
009
![Page 112: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/112.jpg)
Immutability Discussion...
• Java– Strict Immutable
– Read-only accessors
– Read-only super classes
• Ruby– Consider freeze
– But watch out for thaw via dup
• Groovy– Even strict can be broken
– Read-only through meta-programming
(c) A
SE
RT
2006-2
009
![Page 113: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/113.jpg)
...Immutability Discussionstr = 'A simple string. '
str.freeze
begin
str << 'An attempt to modify.'
rescue => err
puts "#{err.class} #{err}"
end
# Output: TypeError can't modify frozen string
str2 = str.dup
puts "#{str.frozen?} #{str2.frozen?}"
# Output: true false
str2 << 'An attempt to modify.'
str2.freeze
puts str2
# Output: A simple string. An attempt to modify.
str = 'Original string - '
str.freeze
str += 'attachment'
puts str
# Output: Original string - attachment
Adapted from
Mutable and Immutable Objects
rubylearning.com
(c) A
SE
RT
2006-2
009
![Page 114: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/114.jpg)
Feature Interaction...• An unintended side-effect of adding features
iteratively [18]– Not helped by emergent behavior
– Nor by dynamic systems which can add behavior in ad-hoc ways
• Common problem in other domains, e.g.
services offered by PABX systems, Intelligent
Networks, Mobile Phones– They have been trying to tackle the problem for some time and
have some success
• From „http://c2.com/cgi/wiki?FeatureInteraction‟:– Good systems don't mind if you try using their features in new
ways; mediocre systems prevent you from using them in new
ways; bad systems break.
(c) A
SE
RT
2006-2
009
![Page 115: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/115.jpg)
...Feature Interaction...• Examples:
– Call Waiting (if a busy subscriber receives a call, the new incoming
call is put on hold) and Call Forwarding (every call to a subscriber is
forwarded to another phone) cannot be activated in conjunction: if
one of them has priority, the other is disabled
– Bob has Call Forwarding, and is forwarding all calls to Carol. Carol
has Do Not Disturb enabled. Alice calls Bob, the call is forwarded to
Carol, and Carol's phone rings, because Do Not Disturb is not
applied to a forwarded call
– A new Mobility service is offered to office workers. When Alice signs
up, her office phone number is forwarded to the Mobility service. On
receiving a call for Alice, the Mobility service forwards it to wherever
Alice's personal data dictates.
However, whenever the data indicates that Alice is in her office, an
incoming call enters a forwarding loop.
(c) A
SE
RT
2006-2
009
![Page 116: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/116.jpg)
...Feature Interaction...• More Examples:
– Alice@host1 and Bob@host2 correspond by email. Because Bob wishes to
remain anonymous in this correspondence, he is known to Alice as
anon@remailer, and the Remailer feature retargets electronic mail for
anon@remailer to Bob@host2.
However, Bob@host2 also has an Autoresponse feature set to notify his
correspondents that he is on vacation. When electronic mail arrives with
source address Alice@host1 and target address Bob@host2, it immediately
generates a response with source address Bob@host2 and target address
Alice@host1. When Alice receives the response, she learns Bob's identity.
– Bob has Call Forwarding, and is forwarding all calls to Carol. Alice calls
Bob, the feature forwards the call to Carol, and also changes the source
address of the call to Bob's address (on the grounds that Bob is responsible
for this leg of the call).
One scenario: Carol has Call Blocking, and is blocking all calls from Alice.
This call is not blocked, however, because it appears to be from Bob.
Another scenario: Carol misses the call, but later tries to use a Call Return
feature to return it. The feature places a call to Bob's address, which is
forwarded back incorrectly to Carol.
(c) A
SE
RT
2006-2
009
![Page 117: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/117.jpg)
...Feature Interaction...class Phone {
def namedef respond(number) {
println "RING RING ..."println "$name: RECEIVED CALL FROM $number"
}def call(number) {
println "$name: CALLING $number"}
}
class Exchange {def numbersdef makeCall(num1, num2) {
numbers[num1].call(num2)numbers[num2].respond(num1)
}}
def e = new Exchange()e.numbers = [
123 : new Phone(name: 'Alice'),456 : new Phone(name: 'Bob')
]e.makeCall(123, 456)
Alice: CALLING 456RING RING ...Bob: RECEIVED CALL FROM 123
(c) A
SE
RT
2006-2
009
![Page 118: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/118.jpg)
...Feature Interaction...
class ForwardingExchange extends Exchange {def forward = [456: 789]def makeCall(num1, num2) {
numbers[num1].call(forward[num2])numbers[forward[num2]].respond(num1)
}}def fe = new ForwardingExchange()fe.numbers = e.numbersfe.numbers[789] = new Phone(name: 'Carol')fe.makeCall(123, 456)
Alice: CALLING 789RING RING ...Carol: RECEIVED CALL FROM 123
(c) A
SE
RT
2006-2
009
![Page 119: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/119.jpg)
class NoDisturbingPhone extends Phone {def doNotDisturbdef acceptOrDeny(number) {
if (doNotDisturb) println "UNAVAILABLE"else super.respond(number)
}def init(exchange) {
def emc = new ExpandoMetaClass(Exchange, true)exchange.metaClass = emcemc.makeCall = { num1, num2 ->
numbers[num1].call(num2)numbers[num2] instanceof NoDisturbingPhone ?
numbers[num2].acceptOrDeny(num1) :numbers[num2].respond(num1)
}emc.initialize()
}}def ndphone = new NoDisturbingPhone(name: 'Carol',
doNotDisturb: true)ndphone.init(e); e.numbers[789] = ndphonee.makeCall(123, 789)
...Feature Interaction...
Alice: CALLING 789UNAVAILABLE
(c) A
SE
RT
2006-2
009
![Page 120: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/120.jpg)
...Feature Interaction...
• Now let‟s try to add both features
fe.numbers[789] = ndphonefe.makeCall(123, 456)
Alice: CALLING 456RING RING ...Bob: RECEIVED CALL FROM 123
Alice: CALLING 789RING RING ...Carol: RECEIVED CALL FROM 123
ndphone.init(fe)fe.makeCall(123, 456)
(c) A
SE
RT
2006-2
009
![Page 121: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/121.jpg)
...Feature Interaction...
• Strategies to solve?– Design the problem away
• redesign individual features
[Aho et al], [Stephen & Logrippo], [Burns, Mataga &
Sutherland]
• define how combinations of features behave
[Blom, Jonsson & Kempe], [Thistle, Malham, Hoang &
Lafortune]
– Architectural Solutions
• coordinate and constrain the features’ access to shared
variables and resources[Jackson & Zave], [Brooks], [Pinard,
Weiss & Gray], [Utas]
– Detect and resolve it at run-time
• apply corrective action when an interaction actually occurs
[Homayoon & Singh], [Marples & Magill], [Griffeth &
Velthuijsen]
(c) A
SE
RT
2006-2
009
Source: Composing Features and Resolving Interactions [16]
![Page 122: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/122.jpg)
...Feature Interaction...
(c) A
SE
RT
2006-2
009
Source: Handling Feature Interactions in LESS [17]
<?xml version="1.0"?><less><incoming><address-switch field="origin"><address is="sip:[email protected]"><accept /></address><otherwise><priority-switch><priority equal="emergency"><accept /></priority><otherwise><reject status="486" /></otherwise></priority-switch></otherwise></address-switch></incoming></less>
{incoming, accept, {{string-switch,organization="ABC Inc."},{address-switch, origin="sip:[email protected]"}, {string-switch,subject="group meeting"}}}
![Page 123: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/123.jpg)
...Feature Interaction
(c) A
SE
RT
2006-2
009
Abstract System State
CW
VM
Feature
Interaction
Manager
Abstract System State
CW
VM
Feature
Interaction
Manager
Abstract System State
CW
VM
Feature
Interaction
Manager
Abstract System State
CW
VM
Feature
Interaction
Manager
1 2
3 4
Source: Composing Features and Resolving Interactions [16]
CW = Call Waiting
VM = Voice Mail
![Page 124: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/124.jpg)
Feature Interaction Verdict
• Dynamic languages allow very flexible
systems to be developed which allow
dynamic behavior to emerge at runtime
• Dynamic languages help avoiding issues
(c) A
SE
RT
2006-2
009
...
![Page 125: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/125.jpg)
DSLs …
• Domain Specific Language– A limited form of computer language designed for a
specific class of problems
• Neal Ford[9]: Layers of abstraction using
language not hierarchies of objects– Object hierarchies still exist underneath a stronger
abstraction layer
– Objects, aspects, generics, et al become the building
blocks for DSLs
– Treat lines of code as sentences
– Allows developers to work at a higher level of
abstraction
• Declarative vs. imperative programming
(c) A
SE
RT
2006-2
009
![Page 126: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/126.jpg)
… DSLs …
• DSL benefits:– DSLs allow solutions to be expressed as closely as
possible in the language of the problem domain
– Consequently, domain experts themselves can
understand, validate, modify, and often even develop
DSL “programs”
– DSL programs are concise, self-documenting to a
large extent, and can be reused for different purposes
– DSLs enhance productivity, reliability, maintainability,
and portability
– DSLs embody domain knowledge, and thus enable
the conservation and reuse of this knowledge
– DSLs allow validation and optimization at the domain
level
– DSLs improve testability
(c) A
SE
RT
2006-2
009
Source: [21]
![Page 127: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/127.jpg)
… DSLs …
• The disadvantages of the use of a DSL
are:– The costs of designing, implementing and
maintaining a DSL
– The costs of education for DSL users
– The limited availability of DSLs
– The difficulty of finding the proper scope for a DSL
– The difficulty of balancing between domain-specificity
and general-purpose programming language
constructs
– The potential loss of efficiency when compared with
hand-coded software
(c) A
SE
RT
2006-2
009
Source: [21]
![Page 128: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/128.jpg)
… DSLs …
• Fluent APIs – Rhino Mocks/Lisp Macros:
using(mocks.Ordered()){Expect.Call(databaseManager.BeginTransaction()).Return(databaseManager);
using(mocks.Unordered()){accountOne.Withdraw(1000);accountTwo.Deposit(1000);
}databaseManager.Dispose();
}mocks.ReplayAll();
Bank bank = new Bank(databaseManager);bank.TransferFunds(accountOne, accountTwo, 1000);
Source: http://www.codeproject.com/useritems/Rhino_Mocks_22.asp
(defun songs-for-album (playlist album)
(select
:from (songs-table playlist)
:where ( matching (songs-table playlist)
:album album)
:order-by :track))
Source: http://blog.secosoft.net/2007/03/13/groovy-dsl-presentation-now-online/
(c) A
SE
RT
2006-2
009
![Page 129: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/129.jpg)
… DSLs …
• Fluent APIs – Custom Java:Event planningMeeting =
Plan.Event("Project planning meeting").RelatedTo(planningTask).WithPriority(1).At("Head office").OrganizedBy("[email protected]", "Jane Doe").StartingAt("12:00").Lasting(45).Minutes.Attendants(
"[email protected]","[email protected]","[email protected]").AreRequired.
Attendant("[email protected]").IsOptional.Resource("Projector").IsRequired.ClassifyAs("Public").CategorizeAs("Businees", "Development").Recurring.Until(2008).EverySingle.Week.On(Day.Thursday).Except.Each.Year.In(Month.July | Month.August);
planningMeeting.SendInvitations();
Source: http://andersnoras.com/blogs/anoras/archive/2007/07/04/i-m-coming-down-with-a-serious-case-of-the-dsls.aspx
(c) A
SE
RT
2006-2
009
![Page 130: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/130.jpg)
… DSLs …
• LINQ:– API form:
– Query expression form:
IEnumerable<string> query = names .Where(s => s.Length == 5) .OrderBy(s => s).Select(s => s.ToUpper());
Source: http://??
IEnumerable<string> query = from s in names where s.Length == 5orderby sselect s.ToUpper();
(c) A
SE
RT
2006-2
009
![Page 131: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/131.jpg)
… DSLs …
• Ruby/Groovy– Malleable languages, conducive to creating natural,
understandable code
then = now + 1.week + 3.days - 2.hours + 17.minutes
InTransaction.do { accOne << 500.euros << accTwo }
Select[:column1].from[:table1].where doequal :column2, 12equal :column3, 13
end
insert into table1 (column1, column2, column3)values (10, 'book', 'start')
(c) A
SE
RT
2006-2
009
![Page 132: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/132.jpg)
… DSLs …
• Rebol Examples
Turn on porch light at 7:30 pm
Search for flight from SFO to JFKprefer "American" or "United"departs 1-June-2000arrives before 10:30PMpriced less than $450
Source: http://www.ddj.com/184404172
Buy 1000 shares of "Acme" symbol ACMEat or below $4.60expires on 6-June-2000account #ACN-456-987confirm with [email protected]
(c) A
SE
RT
2006-2
009
![Page 133: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/133.jpg)
… DSLs …
• Creating a Testing DSL– Ideally, acceptance tests should be written by your
customer
– Examples:
add_book_with :name => "Groovy In Action",
:author => "Paul King",
:cost => 10.dollars
create_new_user_with :email => "[email protected]",
:name => "Jim",
:password => "letmein"
jim = login_as :email => "[email protected]",
:password => "letmein"
jim.add_to_shopping_cart 1.copy,
"Groovy In Action"
jim.checkout
add_book_with name: "Groovy In Action",
author: "Paul King",
cost: 10.dollars
create_new_user_with email: "[email protected]",
name: "Jim",
password: "letmein"
jim = login_as email: "[email protected]",
password: "letmein"
jim.add_to_shopping_cart 1.copy,
"Groovy In Action"
jim.checkout
(c) A
SE
RT
2006-2
009
![Page 134: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/134.jpg)
… DSLs …
(c) A
SE
RT
2006-2
009
Source: http://martinfowler.com/dslwip/Intro.html
events
doorClosed D1CL
drawOpened D2OP
lightOn L1ON
doorOpened D1OP
panelClosed PNCL
end
resetEvents
doorOpened
end
commands
unlockPanel PNUL
lockPanel PNLK
lockDoor D1LK
unlockDoor D1UL
end
state idle
actions {unlockDoor lockPanel}
doorClosed => active
end
state active
drawOpened => waitingForLight
lightOn => waitingForDraw
end
state waitingForLight
lightOn => unlockedPanel
end
state waitingForDraw
drawOpened => unlockedPanel
end
state unlockedPanel
actions {unlockPanel lockDoor}
panelClosed => idle
end
![Page 135: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/135.jpg)
… DSLs …
(c) A
SE
RT
2006-2
009
event ("doorClosed", "D1CL");
event ("drawOpened", "D2OP");
event ("lightOn", "L1ON");
event ("panelClosed", "PNCL");
resetEvent ("doorOpened", "D1OP");
command("unlockPanel", "PNUL");
command("lockPanel", "PNLK");
command("lockDoor", "D1LK");
command("unlockDoor", "D1UL");
state("idle")
.actions("unlockDoor","lockPanel")
.transition("doorClosed").to("active")
;
state("active")
.transition("drawOpened").to("waitingForLight")
.transition("lightOn"). to("waitingForDraw")
;
state("waitingForLight")
.transition("lightOn").to("unlockedPanel");
state("waitingForDraw")
.transition("drawOpened").to("unlockedPanel");
state("unlockedPanel")
.actions("unlockPanel", "lockDoor")
.transition("panelClosed").to("idle");
<stateMachine start = "idle">
<event name="doorClosed" code="D1CL"/>
<event name="drawOpened" code="D2OP"/>
<event name="lightOn" code="L1ON"/>
<event name="doorOpened" code="D1OP"/>
<event name="panelClosed" code="PNCL"/>
<command name="unlockPanel" code="PNUL"/>
<command name="lockPanel" code="PNLK"/>
<command name="lockDoor" code="D1LK"/>
<command name="unlockDoor" code="D1UL"/>
<state name="idle">
<transition event="doorClosed" target="active"/>
<action command="unlockDoor"/>
<action command="lockPanel"/>
</state>
<state name="active">
<transition event="drawOpened" target="waitingForLight"/>
<transition event="lightOn" target="waitingForDraw"/>
</state>
<state name="waitingForLight">
<transition event="lightOn" target="unlockedPanel"/>
</state>
<state name="waitingForDraw">
<transition event="drawOpened" target="unlockedPanel"/>
</state>
<state name="unlockedPanel">
<action command="unlockPanel"/>
<action command="lockDoor"/>
<transition event="panelClosed" target="idle"/>
</state>
<resetEvent name = "doorOpened"/>
</stateMachine>
![Page 136: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/136.jpg)
… DSLs …
• How to write your DSL– Pair with your customer
– Explore the syntax of your DSL using actual inputs
and outputs
– Envision the perfect result
• See the napkin test[9]
– But don‟t over-engineer
– Don‟t go overboard with symbols
– Ensure your „code‟ is syntactically correct
– Then implement it
– Apply different static analysis rules in your IDE and CI
build for DSLs compared to other parts of your code
(c) A
SE
RT
2006-2
009
![Page 137: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/137.jpg)
… DSLs …
• Ruby DSL Toolkit– Open classes enable methods on literals
– Method parenthesis are optional
– Named parameters, using hashes and symbols
– Code blocks to nest statements about something
– Advanced features: Meta-programming
• Ruby Meta-programming– eval, method_missing, define_method
• Groovy Meta-programming– invokeMethod, getProperty, setProperty
– Built-in builder capabilities
• These techniques may seem hackish at first, but they
actually eliminate duplication, and become natural
(c) A
SE
RT
2006-2
009
![Page 138: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/138.jpg)
… DSLs …
(c) A
SE
RT
2006-2
009
![Page 139: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/139.jpg)
… DSLs …
(c) A
SE
RT
2006-2
009
Source: http://martinfowler.com/dslwip/Intro.html
![Page 140: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/140.jpg)
… DSLs …
(c) A
SE
RT
2006-2
009
Source: http://nealford.com/
![Page 141: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/141.jpg)
… DSLs
• Summary– Who will use the DSL?
– When will they use it?
– How will they use it?
– When will changes be required?
– Keep it as cohesive as possible
– Don‟t try to write the entire universe in your DSL
• You may be better off using a bunch of very specific DSLs
– For business DSLs, enable your customer to write
tests as simple phrases, using the terminology of the
business
(c) A
SE
RT
2006-2
009
![Page 142: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/142.jpg)
DSL Verdict
• Dynamic languages help writing DSLs
• Writing DSLs is a trivial and well-
understood area of system design!
(c) A
SE
RT
2006-2
009
...
![Page 143: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/143.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
(c) A
SE
RT
2006-2
009
![Page 144: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/144.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
(c) A
SE
RT
2006-2
009
![Page 145: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/145.jpg)
• This is the end of the talk
(c) ASERT 2006-2009
![Page 146: Paulking dlp](https://reader035.vdocuments.net/reader035/viewer/2022062220/554f415eb4c90572088b532e/html5/thumbnails/146.jpg)
• This is the end of the talk
• Thank you for listening
• Any Questions?
(c) ASERT 2006-2009