Your SlideShare is downloading. ×
0
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Paulking dlp
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

Paulking dlp

4,234

Published on

Published in: Technology, Education
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total Views
4,234
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
14
Comments
0
Likes
0
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. Dynamic Language Practices “Unlearning Java/C#” Dr Paul King - ASERT
  • 2. Introduction … • Developer practices – Well understood and documented for traditional and agile approaches such as Java, C++ and C# development – But dynamic languages like Groovy, Ruby, Python, Boo, JavaScript and (c) ASERT 2006-2009 others change the ground rules – Many of the rules and patterns we have been taught no longer apply
  • 3. … Introduction • Traditional developer practice guidelines – Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides (1995). Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley. – Martin Fowler (1999). Refactoring: Improving the Design of Existing Code. Addison-Wesley. – Joshua Bloch (2001). Effective Java Programming Language (c) ASERT 2006-2009 Guide. Prentice Hall. – Robert C Martin (2002), Agile Software Development, Principles, Patterns, and Practices. Prentice Hall. • In the dynamic language world, are these guidelines FACT or MYTH !!
  • 4. Examples to ponder • What does Immutability mean? – When even constants can be changed • What does encapsulation mean? – When I can peek at anything (c) ASERT 2006-2009 under the covers • How can I devise tests at development time? – When my system can change in unknown ways at runtime • How can IDEs help me? – If I no longer spoon feed it static-typing information or if my language now only allows checks at runtime
  • 5. What do I mean by Dynamic Language? • I prefer a loose definition • One or more of: – Dynamic typing • Greater polymorphism – Metaprogramming • Allow language itself to be dynamically changed (c) ASERT 2006-2009 • Allow hooks into object lifecycle and method calls • Open classes/monkey patching – Work with code as easily as data • Closures • Higher-order programming – Escape hatches • Hooks for polyglot programming
  • 6. Static vs Dynamic Typing … • The Debate – Static vs dynamic typing • Static: the type of each variable (or expression) must be known at compile time like wearing a straight-jacket? • Dynamic: type information is (c) ASERT 2006-2009 known only at runtime like tightrope walking with no net? – Strong vs weak typing • Strong: List<Integer> myList • Weak: Object myList – Type safety • How is this provided if at all? – Type inference • Is this supported?
  • 7. …Static vs Dynamic Typing … • Static Typing Pros – Errors are often detected earlier and with better error messages – Code can sometimes be clearer – you don‟t need to infer the types to understand the code – especially when revisiting the code later (c) ASERT 2006-2009 – Safer because certain kinds of injection hacks don‟t apply – Code can be more declarative – Better IDE support: refactoring, editing and other forms of source processing support is often possible – Better optimisations are often possible – Often easier to understand a system from the outside (“self-documenting” statically-typed APIs and interfaces) – With generics support you can start to nail down even complex cases
  • 8. … Static vs Dynamic Typing … • Dynamic Typing Pros – Speed development through duck-typing and less boiler-plate code – Clearer more concise code is easier to read and maintain – Allow more expressiveness through DSLs (c) ASERT 2006-2009 – You should have comprehensive tests anyway, why not cover off types as part of those tests – Enforced healthy practices: • Static language developers may get a false sense of security and not design/test for runtime issues • Less likely to neglect good documentation and/or good coding conventions on the grounds that your static types make everything “inherently” clear
  • 9. … Static vs Dynamic Typing … • MYTH or TRUTH? Static typing is just spoon feeding the compiler. It represents the old-school way of thinking and requires extra work while providing no real value. (c) ASERT 2006-2009
  • 10. … Static vs Dynamic Typing … • An example interface Reversible { def reverse() } class ReversibleString implements Reversible { def reverse() { /* */ } ??? } class ReversibleArray implements Reversible { def reverse() { /* */ } (c) ASERT 2006-2009 ??? } Reversible[] things = [ new ReversibleString(), new ReversibleArray() ] for (i in 0..<things.size()) { things[i].reverse() } def things = ["abc", [1, 2 ,3]] def expected = ["cba", [3, 2, 1]] assert things*.reverse() == expected
  • 11. … Static vs Dynamic Typing interface Reversible { With dynamically def reverse() typed languages, } there is no need to class ReversibleString implements Reversible { explicitly declare the def reverse() { /* */ } } types of variables or the “protocols” class ReversibleArray implements Reversible { def reverse() { /* */ } observed by our (c) ASERT 2006-2009 } objects:  Less code Reversible[] things = [ new ReversibleString(), new ReversibleArray()  Less declarative ]  Less IDE support for (i in 0..<things.size()) {  More testing things[i].reverse()  Less Robust? } def things = ["abc", [1, 2 ,3]] def expected = ["cba", [3, 2, 1]] assert things*.reverse() == expected
  • 12. … Static vs Dynamic Typing … • MYTH or TRUTH? Static typing is just spoon feeding the compiler. It represents the old-school way of thinking and requires extra work while providing no real value. (c) ASERT 2006-2009 ...but not a total lie either... ...dynamic languages certainly assist with removing duplication and sometimes removing clutter and boilerplate code...
  • 13. Typing Approaches… • Implicit vs Explicit interfaces – Inheritance too restrictive? – Duck-typing too flexible? Menu set_sides() Shape <<interface>> <<interface>> Rectangle draw() Shape RegularPolygon draw() draw() set_side() set_sides() (c) ASERT 2006-2009 Rectangle Square draw() draw() set_sides() Rectangle Square EquilateralTriangle set_side() draw() draw() draw() set_sides() set_side() set_side() Square Pistol draw() draw() set_sides() I tend to use Explicit types for major boundaries and EquilateralTriangle implicit types internally. draw() set_side() Adapted from Interface-Oriented Design [2]
  • 14. …Typing Approaches • Inheritance hierarchies – Very clear intent but use sparingly • Interface-oriented design – Use if it adds clarity & your language supports it – If you do use it, stick to fine-grained interfaces • Dynamic interface-oriented design (c) ASERT 2006-2009 Source: Rick DeNatale – If your language doesn‟t support it natively you © David Friel can use a guard: is_a?, kind_of?, instanceof • Chicken typing – Use a guard: responds_to?, respondsTo • Duck typing – Use when flexibility is important but have appropriate tests in place; e.g. you don‟t want to violate the Liskov Substitution Principal[15] by not considering a refused bequest[13]. • AKA roll your own type safety
  • 15. Typing approaches and IDEs… • Class A has a bit of duplication class A { def helper def make() { helper.invoke('create') } def get() { (c) ASERT 2006-2009 helper.invoke('read') } def change() { helper.invoke('update') } def remove() { helper.invoke('delete') } }
  • 16. … Typing approaches and IDEs … • No problems, we can refactor out the dup class B { def helper def make() { invoke('create') } def get() { (c) ASERT 2006-2009 invoke('read') } def change() { invoke('update') } def remove() { invoke('delete') } private invoke(cmd) { helper.invoke(cmd) } }
  • 17. … Typing approaches and IDEs … • But we can do more using a dynamic language by leveraging metaprogramming class C { def helper def commands = [ make: 'create', (c) ASERT 2006-2009 get: 'read', change: 'update', remove: 'delete' ] def invokeMethod(String name, ignoredArgs) { helper.invoke(commands[name]) } } • Which is a whole lot nicer? • At the expense of IDE completion? … ...
  • 18. … Typing approaches and IDEs … class Dumper { def name def invokeMethod(String methodName, args) { println "$name: called $methodName with $args" } } (c) ASERT 2006-2009 for (x in [A, B, C]) { def o = x.newInstance() o.helper = new Dumper(name: "$x.name's helper") o.make() o.get() o.change() o.remove() }
  • 19. … Typing approaches and IDEs • … At the expense of IDE completion? (c) ASERT 2006-2009 But remember: “clearly express intent” ...
  • 20. Language features instead of Patterns … class RoundPeg { def radius String toString() { "RoundPeg with radius $radius" } } class RoundHole { def radius def pegFits(peg) { peg.radius <= radius } String toString() { "RoundHole with radius $radius" } } (c) ASERT 2006-2009 def pretty(hole, peg) { if (hole.pegFits(peg)) println "$peg fits in $hole" else println "$peg does not fit in $hole" } def hole = new RoundHole(radius:4.0) (3..6).each { w -> pretty(hole, new RoundPeg(radius:w)) } RoundPeg with radius 3 fits in RoundHole with radius 4.0 RoundPeg with radius 4 fits in RoundHole with radius 4.0 RoundPeg with radius 5 does not fit in RoundHole with radius 4.0 RoundPeg with radius 6 does not fit in RoundHole with radius 4.0
  • 21. …Language features instead of Patterns… class SquarePeg { def width String toString() { "SquarePeg with width $width" } } class SquarePegAdapter { def peg def getRadius() { Math.sqrt(((peg.width/2) ** 2)*2) } String toString() { "SquarePegAdapter with width $peg.width (and notional radius $radius)" (c) ASERT 2006-2009 } } def hole = new RoundHole(radius:4.0) (4..7).each { w -> pretty(hole, new SquarePegAdapter(peg: new SquarePeg(width: w))) } SquarePegAdapter with width 4 (and notional radius 2.8284271247461903) fits in RoundHole with radius 4.0 SquarePegAdapter with width 5 (and notional radius 3.5355339059327378) fits in RoundHole with radius 4.0 SquarePegAdapter with width 6 (and notional radius 4.242640687119285) does not fit in RoundHole with radius 4.0 SquarePegAdapter with width 7 (and notional radius 4.949747468305833) does not fit in RoundHole with radius 4.0
  • 22. … Language features instead of Patterns … SquarePeg.metaClass.getRadius = { Math.sqrt(((delegate.width/2)**2)*2) } (4..7).each { w -> pretty(hole, new SquarePeg(width:w)) } Adapter Pattern (c) ASERT 2006-2009 Do I create a whole new class or just add the method I need on the fly? SquarePeg with width 4 fits in RoundHole with radius 4.0 SquarePeg with width 5 fits in RoundHole with radius 4.0 SquarePeg with width 6 does not fit in RoundHole with radius 4.0 SquarePeg with width 7 does not fit in RoundHole with radius 4.0 Further reading: James Lyndsay, Agile is Groovy, Testing is Square
  • 23. Adapter Pattern Verdict • Dynamic languages can make it easier to apply the adapter pattern to the extent that its use may not even be apparent: – Express intent more clearly and improves readability – Aids refactoring – (c) ASERT 2006-2009 Can help with test creation – Avoids class proliferation – But you still need testing
  • 24. … Language features instead of Patterns abstract class Shape {} class Rectangle extends Shape { def x, y, width, height Visitor Pattern abstract class Shape { Rectangle(x, y, width, height) { def accept(Closure yield) { yield(this) } } without closures this.x = x; this.y = y; this.width = width; this.height = height } def union(rect) { if (!rect) return this def minx = [rect.x, x].min() with closures class Rectangle extends Shape { def maxx = [rect.x + width, x + width].max() def miny = [rect.y, y].min() def maxy = [rect.y + height, y + height].max() def x, y, w, h new Rectangle(minx, miny, maxx - minx, maxy - miny) } def bounds() { this } def accept(visitor) { def union(rect) { visitor.visit_rectangle(this) } if (!rect) return this } def minx = [rect.x, x].min() class Line extends Shape { def x1, y1, x2, y2 def maxx = [rect.x + w, x + w].max() Line(x1, y1, x2, y2) { def miny = [rect.y, y].min() this.x1 = x1; this.y1 = y1; this.x2 = x2; this.y2 = y2 } def maxy = [rect.y + h, y + h].max() def accept(visitor) { new Rectangle(x:minx, y:miny, w:maxx - minx, h:maxy - miny) visitor.visit_line(this) } } (c) ASERT 2006-2009 } } class Group extends Shape { def shapes = [] def add(shape) { shapes += shape } class Line extends Shape { def remove(shape) { shapes -= shape } def x1, y1, x2, y2 def accept(visitor) { def bounds() { visitor.visit_group(this) } new Rectangle(x:x1, y:y1, w:x2-y1, h:x2-y2) } } class BoundingRectangleVisitor { def bounds } def visit_rectangle(rectangle) { if (bounds) bounds = bounds.union(rectangle) class Group { else bounds = rectangle def shapes = [] } def leftShift(shape) { shapes += shape } def visit_line(line) { def line_bounds = new Rectangle(line.x1, line.y1, line.x2 - line.y1, line.x2 - line.y2) def accept(Closure yield) { shapes.each{it.accept(yield)} } if (bounds) bounds = bounds.union(line_bounds) } else bounds = line_bounds } def group = new Group() def visit_group(group) { group.shapes.each {shape -> shape.accept(this) } group << new Rectangle(x:100, y:40, w:10, h:5) } } group << new Rectangle(x:100, y:70, w:10, h:5) def group = new Group() group << new Line(x1:90, y1:30, x2:60, y2:5) group.add(new Rectangle(100, 40, 10, 5)) group.add(new Rectangle(100, 70, 10, 5)) def bounds group.add(new Line(90, 30, 60, 5)) def visitor = new BoundingRectangleVisitor() group.accept{ bounds = it.bounds().union(bounds) } group.accept(visitor) bounding_box = visitor.bounds println bounds.dump() println bounding_box.dump() See also Ruby Visitor [24]
  • 25. Visitor Pattern Verdict • Dynamic languages can make it easier to apply the visitor pattern to the extent that its use may not even be apparent: – Express intent more clearly and improves readability – Aids refactoring – (c) ASERT 2006-2009 Avoids class proliferation – But you still need testing
  • 26. Strategy Pattern (c) ASERT 2006-2009 Source: http://nealford.com/
  • 27. Language features instead of Patterns… interface Calc { def execute(n, m) Strategy Pattern } with interfaces class CalcByMult implements Calc { with closures def execute(n, m) { n * m } } def multiplicationStrategies = [ class CalcByManyAdds implements Calc { { n, m -> n * m }, def execute(n, m) { def result = 0 { n, m -> n.times { def total = 0; n.times{ total += m }; total }, result += m { n, m -> ([m] * n).sum() } (c) ASERT 2006-2009 } ] return result } def sampleData = [ } [3, 4, 12], def sampleData = [ [5, -5, -25] [3, 4, 12], ] [5, -5, -25] ] sampleData.each{ data -> multiplicationStrategies.each{ calc -> Calc[] multiplicationStrategies = [ new CalcByMult(), assert data[2] == calc(data[0], data[1]) new CalcByManyAdds() } ] } sampleData.each {data -> multiplicationStrategies.each {calc -> assert data[2] == calc.execute(data[0], data[1]) } }
  • 28. Strategy Pattern Verdict • Dynamic languages can make it easier to apply the strategy pattern to the extent that its use may not even be apparent: – Express intent more clearly and improves readability – Closures open up whole new possibilities for solving problems (c) ASERT 2006-2009 – Aids refactoring – Can help with test creation – Avoids class proliferation – But you still need testing
  • 29. … Language features instead of Patterns … • Builder pattern from the GoF at the syntax-level • Represents easily any nested tree-structured data import groovy.xml.* • Create new builder def b = new MarkupBuilder() b.html { • Call pretended methods (c) ASERT 2006-2009 head { title 'Hello' } (html, head, ...) body { • Arguments are Closures ul { for (count in 1..5) { • Builder code looks very li "world $count" declarative but is ordinary } } } } Groovy program code and can contain any kind of NodeBuilder, DomBuilder, logic SwingBuilder, AntBuilder, …
  • 30. … Language features instead of Patterns <html> <head> import groovy.xml.* <title>Hello</title> def b = new MarkupBuilder() </head> b.html { <body> (c) ASERT 2006-2009 head { title 'Hello' } <ul> body { <li>world 1</li> ul { <li>world 2</li> for (count in 1..5) { <li>world 3</li> li "world $count" <li>world 4</li> } } } } <li>world 5</li> </ul> </body> </html>
  • 31. SwingBuilder import java.awt.FlowLayout builder = new groovy.swing.SwingBuilder() langs = ["Groovy", "Ruby", "Python", "Pnuts"] gui = builder.frame(size: [290, 100], title: 'Swinging with Groovy!’) { panel(layout: new FlowLayout()) { panel(layout: new FlowLayout()) { for (lang in langs) { (c) ASERT 2006-2009 checkBox(text: lang) } } button(text: 'Groovy Button', actionPerformed: { builder.optionPane(message: 'Indubitably Groovy!'). createDialog(null, 'Zen Message').show() }) button(text: 'Groovy Quit', actionPerformed: {System.exit(0)}) } } gui.show() Source: http://www.ibm.com/developerworks/java/library/j-pg04125/
  • 32. JavaFX Script Frame { title: "Hello World F3" width: 200 content: Label { text: "Hello World" (c) ASERT 2006-2009 } visible: true }
  • 33. Cheri::Swing # requires JRuby require 'rubygems' (c) ASERT 2006-2009 require 'cheri/swing' include Cheri::Swing @frame = swing.frame('Hello') { size 500,200 flow_layout on_window_closing {|event| @frame.dispose} button('Hit me') { on_click { puts 'button clicked' } } } @frame.show
  • 34. AntBuilder def ant = new AntBuilder() ant.echo("hello") // let's just call one task // create a block of Ant using the builder pattern ant.sequential { myDir = "target/test/" mkdir(dir: myDir) (c) ASERT 2006-2009 copy(todir: myDir) { fileset(dir: "src/test") { include(name: "**/*.groovy") } } echo("done") } // now let's do some normal Groovy again file = new File("target/test/AntTest.groovy") assert file.exists()
  • 35. Builder Pattern Verdict • The builder pattern in combination with dynamic languages helps me: – Express intent more clearly and improves readability – Aids refactoring – Can help with test creation – Tests are still important (c) ASERT 2006-2009
  • 36. Delegation Pattern ... • Traditional approach to creating a class that is an extension of another class is to use inheritance – Clearest intent & simplest, clearest code for simple cases class Person { private name, age Person(name, age) { this.name = name this.age = age (c) ASERT 2006-2009 } def haveBirthday() { age++ } String toString() { "$name is $age years old" } } class StaffMemberUsingInheritance extends Person { private salary StaffMemberUsingInheritance(name, age, salary) { super(name, age) this.salary = salary } String toString() { super.toString() + " and has a salary of $salary" } }
  • 37. … Delegation Pattern ... • Most common alternative is to use delegation – Intention less clear (can be helped with interfaces) – Overcomes multiple inheritance issues & inheritance abuse class StaffMemberUsingDelegation { private delegate private salary (c) ASERT 2006-2009 StaffMemberUsingDelegation(name, age, salary) { delegate = new Person(name, age) this.salary = salary } def haveBirthday() { delegate.haveBirthday() } String toString() { delegate.toString() + " and has a salary of $salary" } }
  • 38. … Delegation Pattern … • Downside of delegation is maintenance issues – Refactoring overhead if we change the base class – Meta-programming allows us to achieve inheritance like behavior by intercepting missing method calls (invokeMethod or method_missing) – You could take this further with Groovy using named (c) ASERT 2006-2009 parameters rather than the traditional positional parameters shown here (future versions of Ruby may have this too)
  • 39. … Delegation Pattern … class StaffMemberUsingMOP { private delegate private salary StaffMemberUsingMOP(name, age, salary) { delegate = new Person(name, age) this.salary = salary } def invokeMethod(String name, args) { delegate.invokeMethod name, args } (c) ASERT 2006-2009 String toString() { delegate.toString() + " and has a salary of $salary" } } def p1 = new StaffMemberUsingInheritance("Tom", 20, 1000) def p2 = new StaffMemberUsingDelegation("Dick", 25, 1100) def p3 = new StaffMemberUsingMOP("Harry", 30, 1200) p1.haveBirthday() println p1 p2.haveBirthday() Tom is 21 years old and has a salary of 1000 println p2 Dick is 26 years old and has a salary of 1100 p3.haveBirthday() Harry is 31 years old and has a salary of 1200 println p3
  • 40. … Delegation Pattern • Going Further –The example shown (on the previous slide) codes the delegate directly but both Groovy and Ruby let you encapsulate the delegation pattern as a library: • Groovy: Delegator, Injecto; Ruby: forwardable, delegate –But only if I don‟t want to add logic as I delegate (c) ASERT 2006-2009 • E.g. If I wanted to make haveBirthday() increment salary class StaffMemberUsingLibrary { private salary private person StaffMemberUsingLibrary(name, age, salary) { person = new Person(name, age) this.salary = salary def delegator = new Delegator(StaffMemberUsingLibrary, person) delegator.delegate haveBirthday } String toString() { person.toString() + " and has a salary of $salary" } }
  • 41. Delegation Pattern Verdict • The delegation pattern can be expressed more succinctly with dynamic languages: – Express intent more clearly and improves readability – Aids refactoring – But don‟t forget the testing implications (c) ASERT 2006-2009
  • 42. Singleton Pattern… • Pattern Intent • Static language discussion points – Ensure that only one – Need exactly one instance of a class instance of a class is and a well-known controlled access created point • Allows for lazy creation of instance – Provide a global point of – More flexible than static class access to the object variables and methods alone • Permits refinement of operations and – Allow multiple instances (c) ASERT 2006-2009 representation through subclassing in the future without – Reduces name space clutter affecting a singleton • Compared to using static approach – Multi-threading implications class's clients – Serializable implications • need to have readResolve() method to avoid spurious copies – Garbage collection implications • May need "sticky" static self-reference – Need to be careful subclassing • Parent may already create instance or be final or constructor may be hidden
  • 43. …Singleton Pattern… • The details quickly get messy … public final class Singleton { private static final class SingletonHolder { static final Singleton singleton = new Singleton(); } private Singleton() {} public static Singleton getInstance() { return SingletonHolder.singleton; (c) ASERT 2006-2009 } } public class Singleton implements java.io.Serializable { public static Singleton INSTANCE = new Singleton(); protected Singleton() { // Exists only to thwart instantiation. } private Object readResolve() { return INSTANCE; } }
  • 44. …Singleton Pattern… • State of the art approach in Java? – Use an IoC framework, e.g. Spring or Guice import com.google.inject.* @ImplementedBy(CalculatorImpl) interface Calculator { def add(a, b) } (c) ASERT 2006-2009 @Singleton class CalculatorImpl implements Calculator { private total = 0 def add(a, b) { total++; a + b } def getTotalCalculations() { 'Total Calculations: ' + total } String toString() { 'Calc: ' + hashCode()} } class Client { @Inject Calculator calc // ... } def injector = Guice.createInjector()
  • 45. …Singleton Pattern… • But it is easy using meta-programming – Old style class Calculator { private total = 0 def add(a, b) { total++; a + b } (c) ASERT 2006-2009 def getTotalCalculations() { 'Total Calculations: ' + total } String toString() { 'Calc: ' + hashCode()} } class CalculatorMetaClass extends MetaClassImpl { private final static INSTANCE = new Calculator() CalculatorMetaClass() { super(Calculator) } def invokeConstructor(Object[] arguments) { return INSTANCE } } def registry = GroovySystem.metaClassRegistry registry.setMetaClass(Calculator, new CalculatorMetaClass())
  • 46. …Singleton Pattern… • But it is easy using meta-programming class Calculator { def total = 0 def add(a, b) { total++; a + b } } def INSTANCE = new Calculator() (c) ASERT 2006-2009 Calculator.metaClass.constructor = { -> INSTANCE } def c1 = new Calculator() def c2 = new Calculator() assert c1.add(1, 2) == 3 assert c2.add(3, 4) == 7 assert c1.is(c2) assert [c1, c2].total == [2, 2]
  • 47. …Singleton Pattern… • And again with Ruby class Aardvark class Aardvark private_class_method :new private_class_method :new @@instance = new def Aardvark.instance def Aardvark.instance @@instance = new if not @@instance @@instance @@instance end end end end (c) ASERT 2006-2009 module ThreadSafeSingleton def self.append_features(clazz) require 'thread' clazz.module_eval { private_class_method :new @instance_mutex = Mutex.new def self.instance @instance_mutex.synchronize { @instance = new if not (@instance) @instance } end } end end Source: http://c2.com/cgi/wiki?RubySingleton
  • 48. …Singleton Pattern • Or for Python – Classic class version (pre 2.2) class Borg: _shared_state = {} def __init__(self): self.__dict__ = self._shared_state – Non-classic class version (c) ASERT 2006-2009 class Singleton (object): instance = None def __new__(cls, *args, **kargs): if cls.instance is None: cls.instance = object.__new__(cls, *args, **kargs) return cls.instance # Usage mySingleton1 = Singleton() mySingleton2 = Singleton() assert mySingleton1 is mySingleton2 Source: [10] and wikipedia
  • 49. Singleton Pattern Verdict • The singleton pattern can be expressed in better ways with dynamic languages: – Express intent more clearly and improves readability – Aids refactoring – But don‟t forgot testing implications (c) ASERT 2006-2009
  • 50. Pattern Summary • Patterns can be replaced by language features and libraries (c) ASERT 2006-2009 • So patterns aren‟t important any more! ...
  • 51. Refactoring Refactoring • Out with the Old – Some refactorings no longer make sense • In with the New – There are some new refactorings • Times … they are a changin‟ (c) ASERT 2006-2009 – Some refactorings are done differently
  • 52. Encapsulate Downcast Refactoring • Description – Context: A method returns an object that needs to be downcasted by its callers – Solution: Move the downcast to within the method • Is there a point in a dynamic language? – Maybe but not usually (c) ASERT 2006-2009 // Before refactoring Object lastReading() { return readings.lastElement() } // After refactoring Reading lastReading() { return (Reading) readings.lastElement() }
  • 53. Introduce Generics Refactoring • Description – Context: Casting is a runtime hack that allows JVM to clean up a mess caused by a compiler that couldn’t infer intent – Solution: Use Generics to reveal intent to compiler • Is there a point in a dynamic language? (c) ASERT 2006-2009 – Maybe but not usually // Traditional Java style List myIntList = new LinkedList() myIntList.add(new Integer(0)) Integer result = (Integer) myIntList.iterator().next() // Java generified style List<Integer> myIntList2 = new LinkedList<Integer>() myIntList2.add(new Integer(0)) Integer result2 = myIntList2.iterator().next() // Groovier style def myIntList3 = [0] def result3 = myIntList3.iterator().next()
  • 54. Enabling a functional style … • Consider the Maximum Segment Sum (MSS) problem – Take a list of integers; the MSS is the maximum of the sums of any number of adjacent integers • Imperative solution: (c) ASERT 2006-2009 def numbers = [31,-41,59,26,-53,58,97,-93,-23,84] def size = numbers.size() def max = null (0..<size).each { from -> (from..<size).each { to -> def sum = numbers[from..to].sum() if (max == null || sum > max) max = sum } } println "Maximum Segment Sum of $numbers is $max"
  • 55. … Enabling a functional style … • A first attempt at a more functional style: def numbers = [31,-41,59,26,-53,58,97,-93,-23,84] (c) ASERT 2006-2009 def size = numbers.size() def max = [0..<size, 0..<size].combinations().collect{ numbers[it[0]..it[1]].sum() }.max() println "Maximum Segment Sum of $numbers is $max"
  • 56. … Enabling a functional style … • An even more functional style – A known solution using functional composition: mss = max º sum* º (flatten º tails* º inits) – Where inits and tails are defined as follows: (c) ASERT 2006-2009 letters = ['a', 'b', 'c', 'd'] assert letters.inits() == [ assert letters.tails() == [ ['a'], ['d'], ['a', 'b'], ['c', 'd'], ['a', 'b', 'c'], ['b', 'c', 'd'], ['a', 'b', 'c', 'd'] ['a', 'b', 'c', 'd'] ] ]
  • 57. … Enabling a functional style • An even more functional style mss = max º sum* º (flatten º tails* º inits) def segs = { it.inits()*.tails().sum() } def solve = { segs(it)*.sum().max() } def numbers = [31,-41,59,26,-53,58,97,-93,-23,84] (c) ASERT 2006-2009 println "Maximum Segment Sum of $numbers is ${solve numbers}" Notes: – sum() is one-level flatten in Groovy, flatten() is recursive – Metaprogramming allowed us to enhance all Lists List.metaClass { inits{ (0..<delegate.size()).collect{ delegate[0..it] } } tails{ delegate.reverse().inits() } } Source: http://hamletdarcy.blogspot.com/2008/07/groovy-vs-f-showdown-side-by-side.html
  • 58. Refactoring recipes with a curry base • Static: Replace parameter with method – Refactoring [13]: Chapter 10 • Context – An object invokes a method, then passes the result as a parameter for a method. The receiver can also invoke this method. (c) ASERT 2006-2009 • Solution – Remove the parameter and let the receiver invoke the method. • Dynamic solution – Partial Application: Currying
  • 59. Replace parameter with method … class Order { private int quantity, itemPrice Order(q, p) {quantity = q; itemPrice = p} double getPrice() { int basePrice = quantity * itemPrice int discountLevel if (quantity > 100) discountLevel = 2 else discountLevel = 1 (c) ASERT 2006-2009 double finalPrice = discountedPrice(basePrice, discountLevel) return finalPrice } private double discountedPrice(int basePrice, int discountLevel) { if (discountLevel == 2) return basePrice * 0.8 return basePrice * 0.9 } } println new Order(120, 5).price // => 480.0
  • 60. … Replace parameter with method … class Order { private int quantity, itemPrice Order(q, p) {quantity = q; itemPrice = p} double getPrice() { int basePrice = quantity * itemPrice int discountLevel if (quantity > 100) discountLevel = 2 else discountLevel = 1 (c) ASERT 2006-2009 double finalPrice = discountedPrice(basePrice, discountLevel) return finalPrice } private double discountedPrice(int basePrice, int discountLevel) { if (discountLevel == 2) return basePrice * 0.8 return basePrice * 0.9 } } println new Order(120, 5).price // => 480.0
  • 61. … Replace parameter with method … class Order { private int quantity, itemPrice Order(q, p) {quantity = q; itemPrice = p} double getPrice() { int basePrice = quantity * itemPrice double finalPrice = discountedPrice(basePrice) return finalPrice } (c) ASERT 2006-2009 private double discountedPrice(int basePrice) { if (getDiscountLevel() == 2) return basePrice * 0.8 return basePrice * 0.9 } private int getDiscountLevel() { if (quantity > 100) return 2 return 1 } } println new Order(120, 5).price // => 480.0
  • 62. … Replace parameter with method … class Order { private int quantity, itemPrice Order(q, p) {quantity = q; itemPrice = p} double getPrice() { int basePrice = quantity * itemPrice double finalPrice = discountedPrice(basePrice) return finalPrice } (c) ASERT 2006-2009 private double discountedPrice(int basePrice) { if (getDiscountLevel() == 2) return basePrice * 0.8 return basePrice * 0.9 } private int getDiscountLevel() { if (quantity > 100) return 2 return 1 } } println new Order(120, 5).price // => 480.0
  • 63. … Replace parameter with method … class Order { private int quantity, itemPrice Order(q, p) {quantity = q; itemPrice = p} double getPrice() { return discountedPrice(getBasePrice()) } private double discountedPrice(int basePrice) { (c) ASERT 2006-2009 if (getDiscountLevel() == 2) return basePrice * 0.8 return basePrice * 0.9 } private int getBasePrice() { quantity * itemPrice } private int getDiscountLevel() { if (quantity > 100) return 2 return 1 } } println new Order(120, 5).price // => 480.0
  • 64. … Replace parameter with method … class Order { private int quantity, itemPrice Order(q, p) {quantity = q; itemPrice = p} double getPrice() { return discountedPrice(getBasePrice()) } private double discountedPrice(int basePrice) { (c) ASERT 2006-2009 if (getDiscountLevel() == 2) return basePrice * 0.8 return basePrice * 0.9 } private int getBasePrice() { quantity * itemPrice } private int getDiscountLevel() { if (quantity > 100) return 2 return 1 } } println new Order(120, 5).price // => 480.0
  • 65. … Replace parameter with method … class Order { private int quantity, itemPrice Order(q, p) {quantity = q; itemPrice = p} double getPrice() { return discountedPrice() } private double discountedPrice() { (c) ASERT 2006-2009 if (getDiscountLevel() == 2) return getBasePrice() * 0.8 return getBasePrice() * 0.9 } private int getBasePrice() { quantity * itemPrice } private int getDiscountLevel() { if (quantity > 100) return 2 return 1 } } println new Order(120, 5).price // => 480.0
  • 66. … Replace parameter with method … class Order { private int quantity, itemPrice Order(q, p) {quantity = q; itemPrice = p} double getPrice() { return discountedPrice() } private double discountedPrice() { (c) ASERT 2006-2009 if (getDiscountLevel() == 2) return getBasePrice() * 0.8 return getBasePrice() * 0.9 } private int getBasePrice() { quantity * itemPrice } private int getDiscountLevel() { if (quantity > 100) return 2 return 1 } } println new Order(120, 5).price // => 480.0
  • 67. … Replace parameter with method class Order { private int quantity, itemPrice Order(q, p) {quantity = q; itemPrice = p} double getPrice() { if (getDiscountLevel() == 2) return getBasePrice() * 0.8 return getBasePrice() * 0.9 } (c) ASERT 2006-2009 private getBasePrice() { quantity * itemPrice } private getDiscountLevel() { if (quantity > 100) return 2 Note the now small return 1 parameter lists } } println new Order(120, 5).price // => 480.0
  • 68. Some functional style … class Order { private int quantity, itemPrice Order(q, p) {quantity = q; itemPrice = p} def discountedPrice = { basePrice, discountLevel -> discountLevel == 2 ? basePrice * 0.8 : basePrice * 0.9 } def price = { int basePrice = quantity * itemPrice (c) ASERT 2006-2009 def discountLevel = (quantity > 100) ? 2 : 1 discountedPrice(basePrice, discountLevel) } } println new Order(120, 5).price() // => 480.0
  • 69. … Some functional style … class Order { private int quantity, itemPrice Order(q, p) {quantity = q; itemPrice = p} def basePrice = { quantity * itemPrice } def discountLevel = { quantity > 100 ? 2 : 1 } def price = { (c) ASERT 2006-2009 discountLevel() == 2 ? basePrice() * 0.8 : basePrice() * 0.9 } } println new Order(120, 5).price() // => 480.0
  • 70. … Some functional style … class Order { private int quantity, itemPrice Order(q, p) {quantity = q; itemPrice = p} def basePrice = { quantity * itemPrice } def discountLevel = { quantity > 100 ? 2 : 1 } def discountedPrice = { basePrice, discountLevel -> (c) ASERT 2006-2009 discountLevel == 2 ? basePrice * 0.8 : basePrice * 0.9 } def price = { discountedPrice.curry(basePrice()).curry(discountLevel()).call() } } println new Order(120, 5).price() // => 480.0
  • 71. … Some functional style class Order { private int quantity, itemPrice Order(q, p) {quantity = q; itemPrice = p} def basePrice = { quantity * itemPrice } def discountLevel = { quantity > 100 ? 2 : 1 } def discountedPrice(basePrice, discountLevel) { (c) ASERT 2006-2009 discountLevel == 2 ? basePrice * 0.8 : basePrice * 0.9 } def price = { this.&discountedPrice.curry(basePrice()).curry(discountLevel()).call() } } println new Order(120, 5).price() // => 480.0
  • 72. Closure Refactoring … • Complex code involving closures // Before refactoring def phrase = "The quick brown fox jumps over the lazy dog" def result = phrase.toLowerCase().toList(). findAll{ it in "aeiou".toList() }. // like WHERE ... (c) ASERT 2006-2009 groupBy{ it }. // like GROUP BY ... findAll{ it.value.size() > 1 }. // like HAVING ... sort{ it.key }.reverse(). // like ORDER BY ... collect{ "$it.key:${it.value.size()}" }. join(", ") println result
  • 73. … Closure Refactoring … • Possible Refactoring // Refactored helper closures def lowercaseLetters = phrase.toLowerCase() def vowels = { it in "aeiou".toList() } def occursMoreThanOnce = { it.value.size() > 1 } def byReverseKey = { a, b -> b.key <=> a.key } def self = { it } (c) ASERT 2006-2009 def entriesAsPrettyString = { "$it.key:${it.value.size()}" } def withCommaDelimiter = ", " // Refactored main closure println lowercaseLetters. findAll(vowels). groupBy(self). findAll(occursMoreThanOnce). sort(byReverseKey). collect(entriesAsPrettyString). join(withCommaDelimiter)
  • 74. … Closure Refactoring # Add group_by to the Array class class Array def group_by group_hash = {} uniq.each do |e| group_hash[e] = select { |i| i == e }.size end group_hash end end # Before refactoring phrase = "The quick brown fox jumps over the lazy dog" (c) ASERT 2006-2009 puts phrase.downcase. scan(/[aeiou]/). # like WHERE ... group_by. # like GROUP BY ... select { |key, value| value > 1 }. # like HAVING ... sort.reverse. # like ORDER BY ... DESC collect{ |key, value| "#{key}:#{value}" }.join(', ') # Refactored version lowercase_letters = phrase.downcase vowels = /[aeiou]/ occurs_more_than_once = lambda { |key,value| value > 1 } entries_as_pretty_string = lambda { |key, value| "#{key}:#{value}" } puts lowercase_letters. scan(vowels). group_by. select(&occurs_more_than_once). sort.reverse. collect(&entries_as_pretty_string).join(', ')
  • 75. • This is the end of the talk (c) ASERT 2006-2009
  • 76. • This is the end of the talk • NO! • We haven’t questioned some fundamental principles yet! (c) ASERT 2006-2009
  • 77. Open-Closed Principle... • Fundamental rule to make your software flexible – Many other OOP principles, methodologies and conventions revolve around this principle • Open-Closed Principle (OCP) states: (c) ASERT 2006-2009 • Software entities should be open for extension, but closed for modification • References – Bertrand Meyer, Object Oriented Software Construction (88, 97) – Robert C Martin, The Open-Closed Principle – Craig Larman, Protected Variation: The Importance of Being Closed Picture source: http://www.vitalygorn.com
  • 78. ...Open-Closed Principle... • Following the Rules – Encapsulation: Make anything that shouldn‟t be seen private – Polymorphism: Force things to be handled using abstract classes or interfaces • When making class hierarchies: (c) ASERT 2006-2009 – Make anything that shouldn‟t be open final – Polymorphism: Always follow weaker pre stronger post (object substitutability in the static world) • When making changes that might break existing clients – Add a new class into the hierarchy – No compilation of existing code! No breakages!
  • 79. ...Open-Closed Principle... • Part I: If I violate the Open part of OCP in static languages – I can‟t make the future enhancements I need • Part II: If I violate the Closed part of OCP – Client applications using my libraries might (c) ASERT 2006-2009 break or require recompilation in the future Class A Extendible Class A Interface User Class A User Class A‟ Class A‟ User Class A‟ User Optional Optional Class A Class A‟ Factory Factory ...
  • 80. ...Open-Closed Principle... • Part I: Consider Java‟s String class – Has methods to convert to upper or lower case but no swapCase() method? – Traditionally, consider creating an EnhancedString class using inheritance? – I can‟t: String is immutable and final (c) ASERT 2006-2009 • In OCP terms, it is not open for extension • Dynamic language solution: open classes String.metaClass.swapCase = { #light open String delegate.collect{ c -> type System.String with c in 'A'..'Z' ? member x.swapCase = seq { for letter in x.ToCharArray() do c.toLowerCase() : if (System.Char.IsLower(letter)) c.toUpperCase() then yield System.Char.ToUpper(letter) else yield System.Char.ToLower(letter) }.join() } } printfn "result: %A" "Foo".swapCase assert "Foo".swapCase() == "fOO" ...
  • 81. ...Open-Closed Principle... • Part II: Violating OCP (see [15]) class Square { def side } class Circle { def radius } (c) ASERT 2006-2009 class AreaCalculator { double area(shape) { switch (shape) { case Square: return shape.side * shape.side case Circle: return Math.PI * shape.radius ** 2 } } }
  • 82. ...Open-Closed Principle... def shapes = [ new Square(side: 3), new Square(side: 2), new Circle(radius: 1.5) ] def calc = new AreaCalculator() shapes.sort().each {s -> println "Area of $s.class.name is ${calc.area(s)}" (c) ASERT 2006-2009 } • What‟s wrong – If we wanted to introduce a Triangle, the AreaCalculator would need to be recompiled – If we wanted to change the order the shape information was displayed, there might be many changes to make
  • 83. ...Open-Closed Principle... * Our abstractions never designed sorting to be • Dynamic shapes one of the things open for extension. See [15]. – No issue with adding Triangle but sorting is an issue * class Square { Note: Duck-type private side polymorphism double area() { side ** 2 } instead of } inheritance class Circle { polymorphism, (c) ASERT 2006-2009 private radius i.e. no base Shape double area() { Math.PI * radius ** 2 } (abstract) class or } interface. Hmm… what are def shapes = [ the testing new Square(side:3), implications when new Square(side:2), I add Triangle? new Circle(radius:1.5) Area of Square is 9.0 ] Area of Square is 4.0 // unsorted Area of Circle is 7.0685834705770345 def prettyPrint = { s -> println "Area of $s.class.name is ${s.area()}" } shapes.each(prettyPrint) ...
  • 84. ...Open-Closed Principle... • Dynamic sorting using Closures – As long as we are happy having our sort “code” within a closure we have complete freedom – Sometimes representing our abstractions within classes is appropriate; many times closures will do Area of Square is 4.0 (c) ASERT 2006-2009 Area of Circle is 7.0685834705770345 // sorted by area Area of Square is 9.0 def byArea = { s -> s.area() } shapes.sort(byArea).each(prettyPrint) Note: Make sure your closures are testable. // sorted circles before squares but otherwise by area def byClassNameThenArea = { sa, sb -> sa.class.name == sb.class.name ? Area of Circle is 7.06858... sa.area() <=> sb.area() : Area of Square is 4.0 Area of Square is 9.0 sa.class.name <=> sb.class.name } shapes.sort(byClassNameThenArea).each(prettyPrint) ...
  • 85. ...Open-Closed Principle... • Instead of worrying about – Rigidity – Fragility – Immobility (Because they can be easily gotten around even if you don‟t try to apply OCP) (c) ASERT 2006-2009 • We must worry about – Duplication – Harder refactoring or testing – Feature interaction • And of course OCP then leads to ... – Liskov Substitution Principle, Single Responsibility Principle, Dependency Inversion Principle, ...
  • 86. ...Open-Closed Principle... • “Clean code” [23] states it this way: – Procedural code (i.e. using data structures) makes it easy to add new functions without changing existing data structures but when new data structures are added, all existing procedures may need to change – OO code makes it easy to add new classes without (c) ASERT 2006-2009 changing existing functions but when new functions are added, all classes must change • Recommendation? – Choose procedural or OO approach based on whether anticipated evolution of system involves functions or data – Use Visitor (dual dispatch) Pattern if you think both functions and data might change
  • 87. ...Open-Closed Principle... class Square { double side } class Rectangle { double height, width } class Circle { (c) ASERT 2006-2009 double radius } class Geometry { def area(shape) { switch (shape) { case Square: return shape.side ** 2 case Rectangle: return shape.height * shape.width case Circle: return PI * shape.radius ** 2 } } Can add perimeter() here without shape classes changing but if we } added a Triangle, area(), perimeter() etc. would need to change.
  • 88. ...Open-Closed Principle... interface Shape { If we add perimeter() here, each double area() shape class must change but we can } add new shapes with no changes class Square implements Shape { double side double area() { side ** 2 } } (c) ASERT 2006-2009 class Rectangle implements Shape { double height, width double area() { height * width } } class Circle implements Shape { double radius double area() { PI * radius ** 2 } }
  • 89. ...Open-Closed Principle... class Square { double side double area() { side ** 2 } } We can easily add perimeter() here but for any code requiring the perimeter() method to exist, we should test that code class Rectangle { with all shapes. double height, width double area() { height * width } } (c) ASERT 2006-2009 class Circle { double radius double area() { PI * radius ** 2 } }
  • 90. ...Open-Closed Principle... • “Clean code” [23] recommendation: – Choose procedural or OO approach or Visitor • Agile variation: – Defer moving to complicated solutions, e.g. Visitor Pattern, but have in place sufficient tests so that you can confidently refactor to use one later if needed (c) ASERT 2006-2009 • Dynamic language variation: – You won‟t need an explicit visitor (more on this later) – Duck typing lets you add functions or data without changing existing classes at the expense of static type safety – If you add a function you might need additional tests for each class associated with that function – If you add a new class you might need additional tests for each function associated with that class
  • 91. ...Open-Closed Principle • Sometimes referred to as the Expression Problem: – Independently Extensible Solutions to the Expression Problem by Matthias Zenger Martin Odersky – http://www.scala-lang.org/docu/files/ IC_TECH_REPORT_200433.pdf (c) ASERT 2006-2009
  • 92. • This is the end of the talk (c) ASERT 2006-2009
  • 93. • This is the end of the talk • NO! • We haven’t looked at Advanced Topics yet including Aspects, Testability, Feature Interaction and DSLs! (c) ASERT 2006-2009
  • 94. What & why of dependency injection? • Construction by hand Loose Coupling • Factory pattern Flexibility (c) ASERT 2006-2009 Testability Reusability • Service locator Lifecycle Control Central • Dependency injection Control
  • 95. Dependency Injection vs Metaprogramming … • Dependency Injection – Dependencies are explicitly declared and allowed to be set externally (typically via constructor or setters) • Transparent injection of dependent service objects into other service objects by a controlling container hence the name inversion of control • Why? (c) ASERT 2006-2009 – More flexible • Central configuration of service objects – Can be less work to do • Service objects are instantiated by the dependency injection framework – Improves testability – Improves reusability – Improved lifecycle control
  • 96. … Dependency Injection vs Metaprogramming … • Without dependency injection class PrintService { private printer = new PhysicalPrinter('Canon i9900') } class PrintServiceTest extends GroovyTestCase { def testPrintService() { (c) ASERT 2006-2009 def printService = new PrintService() printService.print() class PrintService // go to the printer and fetch the page def initialise } @printer = } PhysicalPrinter.new('Canon i9900') end end class PrintServiceTest < Test::Unit def test_print_service print_service = PrinterService.new print_service.print # go to the printer and fetch the page end end
  • 97. … Dependency Injection vs Metaprogramming … • Improves testability class TestablePrintService { def printer } class TestablePrintServiceTest extends GroovyTestCase { def testPrintService() { def printService = new TestablePrintService() printService.printer = new StubPrinter() (c) ASERT 2006-2009 printService.print() //... } class TestablePrintService } attr_accessor :printer end class TestablePrintServiceTest < Test::Unit::TestCase def test_print_service print_service = TestablePrintService.new print_service.printer = StubPrinter.new print_service.print #... end end
  • 98. … Dependency Injection vs Metaprogramming … • Improves reuse Without DI class UnreusablePrintService def initialise(printer_name) @printer = PostScriptPrinter.new(ip_address) end # printer logic below # ... end (c) ASERT 2006-2009 class ReusablePrintService # What if I don’t want to use a Physical Printer? 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!
  • 99. … 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 (c) ASERT 2006-2009 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
  • 100. … Dependency Injection vs Metaprogramming … • Improved lifecycle control – Guice and Spring let you declaratively specify Singletons • But see the discussion on Singleton Patterns (c) ASERT 2006-2009
  • 101. … 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? (c) ASERT 2006-2009 – 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
  • 102. … 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 (c) ASERT 2006-2009 organizational discipline across our dependencies – A great example: Dependency Injection in Ruby http://onestepback.org/index.cgi/Tech/Ruby/DependencyInjectionInRuby.rdoc
  • 103. … Dependency Injection vs Metaprogramming … • Traditional class WebApp def initialize @quotes = StockQuotes.new @authenticator = Authenticator.new @database = Database.new @logger = Logger.new @error_handler = ErrorHandler.new end # ... end def create_application (c) ASERT 2006-2009 locator = {} locator[:logger] = Logger.new • Service locator[:error_handler] = ErrorHandler.new(locator) locator[:quotes] = StockQuotes.new(locator) locator[:database] = Database.new(locator) Locator locator[:authenticator] = Authenticator.new(locator) locator[:webapp] = WebApp.new(locator) end class StockQuotes def initialize(locator) @error_handler = locator[:error_handler] @logger = locator[:logger] end # ... end
  • 104. … Dependency Injection vs Metaprogramming • def create_application container = DI::Container.new container.register(:logfilename) { "logfile.log" } Using DI 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.logger app.set_error_handler c.error_handler app } (c) ASERT 2006-2009 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.new errh.logger = c.logger errh } end
  • 105. Dependency Injection Framework Verdict • DI frameworks aren‟t strictly needed when using dynamic languages (c) ASERT 2006-2009 • DI frameworks provide no value for dynamic languages!
  • 106. 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 (c) ASERT 2006-2009 – Closures – Maps – Meta-programming • Same goes for Behavior-Driven Design packages – But that‟s another story
  • 107. … Flexible Mocks Traditionally, we‟d class MyApp { use mocks for factory def factory and logger, here we use def logger Expandos and Maps => def doBusinessLogic(param) { def myObj = factory.instance myObj.doSomething(param) myObj.doSomethingElse(param) logger.log('Something done with: ' + param) } (c) ASERT 2006-2009 } def factory = [instance: businessObj] class ClosureMocking { String methodOne() { def logger = new Expando() methodTwo() } logger.log = { msg -> String methodTwo() { assert msg == 'Something done with: ' + param } "television" } } def emc = new ExpandoMetaClass(ClosureMocking,true) emc.methodTwo = {-> "radio" } emc.initialize() <= „mock‟ using meta-programming assert "radio" == new ClosureMocking().methodOne()
  • 108. Aspects vs Meta-programming class TimingInterceptor extends TracingInterceptor { private beforeTime def 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() - beforeTime writer.write("Duration: $duration msn") writer.flush() (c) ASERT 2006-2009 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
  • 109. 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 (c) ASERT 2006-2009 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/
  • 110. Immutability • Why? – Simpler classes – Good for certain kinds of domain objects – No issues with concurrency (c) ASERT 2006-2009
  • 111. 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 (c) ASERT 2006-2009 – 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
  • 112. Immutability Discussion... • Java – Strict Immutable – Read-only accessors – Read-only super classes • Ruby (c) ASERT 2006-2009 – Consider freeze – But watch out for thaw via dup • Groovy – Even strict can be broken – Read-only through meta-programming
  • 113. ...Immutability Discussion str = 'A simple string. ' str.freeze Adapted from Mutable and Immutable Objects begin rubylearning.com str << 'An attempt to modify.' rescue => err puts "#{err.class} #{err}" end # Output: TypeError can't modify frozen string str2 = str.dup (c) ASERT 2006-2009 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
  • 114. 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 (c) ASERT 2006-2009 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.
  • 115. ...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 (c) ASERT 2006-2009 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.
  • 116. ...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 (c) ASERT 2006-2009 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.
  • 117. ...Feature Interaction... class Phone { def name def respond(number) { println "RING RING ..." println "$name: RECEIVED CALL FROM $number" } def call(number) { println "$name: CALLING $number" } } (c) ASERT 2006-2009 class Exchange { def numbers Alice: CALLING 456 def makeCall(num1, num2) { RING RING ... numbers[num1].call(num2) numbers[num2].respond(num1) Bob: RECEIVED CALL FROM 123 } } def e = new Exchange() e.numbers = [ 123 : new Phone(name: 'Alice'), 456 : new Phone(name: 'Bob') ] e.makeCall(123, 456)
  • 118. ...Feature Interaction... class ForwardingExchange extends Exchange { def forward = [456: 789] def makeCall(num1, num2) { numbers[num1].call(forward[num2]) numbers[forward[num2]].respond(num1) } (c) ASERT 2006-2009 } def fe = new ForwardingExchange() fe.numbers = e.numbers fe.numbers[789] = new Phone(name: 'Carol') fe.makeCall(123, 456) Alice: CALLING 789 RING RING ... Carol: RECEIVED CALL FROM 123
  • 119. ...Feature Interaction... class NoDisturbingPhone extends Phone { def doNotDisturb def acceptOrDeny(number) { if (doNotDisturb) println "UNAVAILABLE" else super.respond(number) } def init(exchange) { def emc = new ExpandoMetaClass(Exchange, true) exchange.metaClass = emc (c) ASERT 2006-2009 emc.makeCall = { num1, num2 -> numbers[num1].call(num2) numbers[num2] instanceof NoDisturbingPhone ? numbers[num2].acceptOrDeny(num1) : numbers[num2].respond(num1) } Alice: CALLING 789 emc.initialize() UNAVAILABLE } } def ndphone = new NoDisturbingPhone(name: 'Carol', doNotDisturb: true) ndphone.init(e); e.numbers[789] = ndphone e.makeCall(123, 789)
  • 120. ...Feature Interaction... • Now let‟s try to add both features fe.numbers[789] = ndphone fe.makeCall(123, 456) Alice: CALLING 789 RING RING ... (c) ASERT 2006-2009 Carol: RECEIVED CALL FROM 123 ndphone.init(fe) fe.makeCall(123, 456) Alice: CALLING 456 RING RING ... Bob: RECEIVED CALL FROM 123
  • 121. ...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 & (c) ASERT 2006-2009 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] Source: Composing Features and Resolving Interactions [16]
  • 122. ...Feature Interaction... <?xml version="1.0"?> {incoming, accept, {{string- <less> switch,organization="ABC Inc."}, <incoming> {address-switch, <address-switch field="origin"> origin="sip:tom@abc.com"}, <address is="sip:t@abc.com"> {string-switch, <accept /> subject="group meeting"}}} </address> <otherwise> <priority-switch> (c) ASERT 2006-2009 <priority equal="emergency"> <accept /> </priority> <otherwise> <reject status="486" /> </otherwise> </priority-switch> </otherwise> </address-switch> </incoming> </less> Source: Handling Feature Interactions in LESS [17]
  • 123. ...Feature Interaction CW = Call Waiting VM = Voice Mail 1 2 Feature CW Feature CW Interaction VM Interaction Manager Manager VM Abstract System State Abstract System State (c) ASERT 2006-2009 3 4 Feature CW Feature CW Interaction Interaction VM VM Manager Manager Abstract System State Abstract System State Source: Composing Features and Resolving Interactions [16]
  • 124. Feature Interaction Verdict • Dynamic languages allow very flexible systems to be developed which allow dynamic behavior to emerge at runtime (c) ASERT 2006-2009 • Dynamic languages help avoiding issues ...
  • 125. 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 (c) ASERT 2006-2009 – 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
  • 126. … 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” (c) ASERT 2006-2009 – 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 Source: [21]
  • 127. … 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 (c) ASERT 2006-2009 – 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 Source: [21]
  • 128. … DSLs … • Fluent APIs – Rhino Mocks/Lisp Macros: using(mocks.Ordered()) { Expect.Call(databaseManager.BeginTransaction()). Return(databaseManager); using(mocks.Unordered()) (defun songs-for-album (playlist album) { accountOne.Withdraw(1000); (select accountTwo.Deposit(1000); :from (songs-table playlist) (c) ASERT 2006-2009 } :where ( matching (songs-table playlist) databaseManager.Dispose(); :album album) } mocks.ReplayAll(); :order-by :track)) Bank bank = new Bank(databaseManager); bank.TransferFunds(accountOne, accountTwo, 1000); Source: http://blog.secosoft.net/2007/03/13/groovy-dsl-presentation-now-online/ Source: http://www.codeproject.com/useritems/Rhino_Mocks_22.asp
  • 129. … DSLs … • Fluent APIs – Custom Java: Event planningMeeting = Plan.Event("Project planning meeting"). RelatedTo(planningTask). WithPriority(1). At("Head office"). OrganizedBy("jane@megacorp.com", "Jane Doe"). StartingAt("12:00").Lasting(45).Minutes. (c) ASERT 2006-2009 Attendants( "peter@megacorp.com", "paul@megacorp.com", "mary@contractor.com").AreRequired. Attendant("john@megacorp.com").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
  • 130. … DSLs … • LINQ: – API form: IEnumerable<string> query = names .Where(s => s.Length == 5) .OrderBy(s => s) .Select(s => s.ToUpper()); (c) ASERT 2006-2009 – Query expression form: IEnumerable<string> query = from s in names where s.Length == 5 orderby s select s.ToUpper(); Source: http://??
  • 131. … 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 } (c) ASERT 2006-2009 Select[:column1].from[:table1].where do equal :column2, 12 equal :column3, 13 end insert into table1 (column1, column2, column3) values (10, 'book', 'start')
  • 132. … DSLs … • Rebol Examples Turn on porch light at 7:30 pm Search for flight from SFO to JFK prefer "American" or "United" (c) ASERT 2006-2009 departs 1-June-2000 arrives before 10:30PM priced less than $450 Buy 1000 shares of "Acme" symbol ACME at or below $4.60 expires on 6-June-2000 account #ACN-456-987 confirm with luke@rebol.com Source: http://www.ddj.com/184404172
  • 133. … DSLs … • Creating a Testing DSL – Ideally, acceptance tests should be written by your customer – Examples: (c) ASERT 2006-2009 add_book_with name: "Groovy In Action", add_book_with :name => "Groovy In Action", author: "Paul King", :author => "Paul King", cost: 10.dollars :cost => 10.dollars create_new_user_with email: "jim@jim.com", create_new_user_with :email => "jim@jim.com", name: "Jim", :name => "Jim", password: "letmein" :password => "letmein" jim = login_as email: "jim@jim.com", jim = login_as :email => "jim@jim.com", password: "letmein" :password => "letmein" jim.add_to_shopping_cart 1.copy, jim.add_to_shopping_cart 1.copy, "Groovy In Action" "Groovy In Action" jim.checkout jim.checkout
  • 134. … DSLs … events doorClosed D1CL drawOpened D2OP lightOn L1ON doorOpened D1OP panelClosed PNCL end resetEvents state idle doorOpened actions {unlockDoor lockPanel} end doorClosed => active end commands (c) ASERT 2006-2009 unlockPanel PNUL state active lockPanel PNLK drawOpened => waitingForLight lockDoor D1LK lightOn => waitingForDraw unlockDoor D1UL end end state waitingForLight lightOn => unlockedPanel end state waitingForDraw drawOpened => unlockedPanel end state unlockedPanel actions {unlockPanel lockDoor} panelClosed => idle end Source: http://martinfowler.com/dslwip/Intro.html
  • 135. … DSLs … <stateMachine start = "idle"> <event name="doorClosed" code="D1CL"/> event ("doorClosed", "D1CL"); <event name="drawOpened" code="D2OP"/> event ("drawOpened", "D2OP"); <event name="lightOn" code="L1ON"/> event ("lightOn", "L1ON"); <event name="doorOpened" code="D1OP"/> event ("panelClosed", "PNCL"); <event name="panelClosed" code="PNCL"/> <command name="unlockPanel" code="PNUL"/> resetEvent ("doorOpened", "D1OP"); <command name="lockPanel" code="PNLK"/> <command name="lockDoor" code="D1LK"/> command("unlockPanel", "PNUL"); <command name="unlockDoor" code="D1UL"/> command("lockPanel", "PNLK"); command("lockDoor", "D1LK"); <state name="idle"> command("unlockDoor", "D1UL"); <transition event="doorClosed" target="active"/> <action command="unlockDoor"/> <action command="lockPanel"/> state("idle") .actions("unlockDoor","lockPanel") (c) ASERT 2006-2009 </state> .transition("doorClosed").to("active") <state name="active"> ; <transition event="drawOpened" target="waitingForLight"/> <transition event="lightOn" target="waitingForDraw"/> state("active") </state> .transition("drawOpened").to("waitingForLight") <state name="waitingForLight"> .transition("lightOn"). to("waitingForDraw") <transition event="lightOn" target="unlockedPanel"/> ; </state> state("waitingForLight") <state name="waitingForDraw"> .transition("lightOn").to("unlockedPanel"); <transition event="drawOpened" target="unlockedPanel"/> </state> state("waitingForDraw") <state name="unlockedPanel"> .transition("drawOpened").to("unlockedPanel"); <action command="unlockPanel"/> <action command="lockDoor"/> state("unlockedPanel") <transition event="panelClosed" target="idle"/> .actions("unlockPanel", "lockDoor") </state> .transition("panelClosed").to("idle"); <resetEvent name = "doorOpened"/> </stateMachine>
  • 136. … 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] (c) ASERT 2006-2009 – 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
  • 137. … 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 (c) ASERT 2006-2009 • 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
  • 138. … DSLs … (c) ASERT 2006-2009
  • 139. … DSLs … (c) ASERT 2006-2009 Source: http://martinfowler.com/dslwip/Intro.html
  • 140. … DSLs … (c) ASERT 2006-2009 Source: http://nealford.com/
  • 141. … 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 (c) ASERT 2006-2009 – 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
  • 142. DSL Verdict • Dynamic languages help writing DSLs (c) ASERT 2006-2009 • Writing DSLs is a trivial and well- understood area of system design! ...
  • 143. Further Information… • [1] Dynamic vs. Static Typing — A Pattern-Based Analysis, Pascal Costanza, University of Bonn, 2004 http://p-cos.net/documents/dynatype.pdf • [2] Interface-Oriented Design, Ken Pugh, Pragmatic Programmers, 2006 • [3] Bruce Eckel, Does Java need Checked Exceptions? www.mindview.net/Etc/Discussions/CheckedExceptions • [4] Null Object, Kevlin Henney, Proceedings EuroPLoP 2002 • [5] Design Patterns in Dynamic Programming, Peter Norvig, March 1998 http://www.norvig.com/design-patterns/ • [6] Advanced Programming Language Features and Software Engineering: (c) ASERT 2006-2009 Friend or Foe?, Greg Sullivan, April 2002 http://people.csail.mit.edu/gregs/proglangsandsofteng.pdf • [7] JunGL: a Scripting Language for Refactoring, Mathieu Verbaere et al, May 2006 http://progtools.comlab.ox.ac.uk/publications/icse06jungl • [8] Rails for Java Developers, Halloway et al, Pragmatic Bookshelf, 2007, Chapter 3, Ruby Eye for the Java Guy • [9] Building DSLs in Static & Dynamic languages http://www.nealford.com/downloads/conferences/canonical/Neal_Ford- Building_DSLs_in_Static_and_Dynamic_Languages-handouts.pdf • [10] Five Easy Pieces: Simple Python Non-Patterns, Alex Martelli http://www.aleax.it/5ep.html • [11] Emergent Design, Scott L. Bain, 2008
  • 144. …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 (c) ASERT 2006-2009 • [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 Zave http://www.research.att.com/~pamela/faq.html • [19] Liskov Substitution Principle and the Ruby Core Libraries, Dean Wampler http://blog.objectmentor.com/articles/tag/liskovsubstitutionprinciple • [20] Liskov Substitution in Dynamic Languages, Michael Feathers http://butunclebob.com/ArticleS.MichaelFeathers.LiskovSubstitutionInDynamicLanguages • [21] Domain-Specific Languages: An Annotated Bibliography, van Deursen et al http://homepages.cwi.nl/~arie/papers/dslbib/ • [22] Agile Principles, Patterns, and Practices in C#, Martin C. Robert et al, 2006 • [23] Clean Code, Robert C. Martin, 2008 • [24] The Craftsman: 51, Ruby Visitor, Robert C Martin, August 2007 http://www.objectmentor.com/resources/articles/Craftsman51.pdf
  • 145. • This is the end of the talk (c) ASERT 2006-2009
  • 146. • This is the end of the talk • Thank you for listening • Any Questions? (c) ASERT 2006-2009

×