Fudge anyone? 80
Back in September, when I was just staring the Slim project, I made a crucial architectural decision. I made it dead wrong. And then life turned to fudge…
The issue was simple. Slim tables are, well, tables just like Fit tables are. How should I parse these tables? Fit parses HTML. FitNesse parses wiki text into HTML and delivers it to Fit. Where should Slim fit in all of this?
Keep in mind that Fit must parse HTML since it lives on the far side of the FitNesse/SUT boundary. Fit doesn’t have access to wiki text. Slim tables, on the other hand, live on the near side of the FitNesse/SUT boundary, and so have full access to wiki text, and the built in parsers that parse that text.
So it seemed to me that I had two options.
- Follow the example of Fit and parse wiki text into HTML, and then have the Slim Tables parse the HTML in order to process the tables.
- Take advantage of the built in wiki text parser inside of FitNesse and bypass HTML altogether.
I chose the latter of the two because the Parsing system of FitNesse is trivial to use. You just hand it a string of wiki text, and it hands you a nice little parse tree of wiki widgets. All I had to do was walk that parse three and process my tables. Voila!
This worked great! In a matter of hours I was making significant progress on processing Slim decision tables. Instead of worrying about parsing HTML and building my own parse tree, I could focus on the problems of translating tables into Slim directives and then using the return values from Slim to colorize the table.
Generating html was no problem since that’s what FitNesse does anyway. All I had to do was modify the elements of the parse tree and then simply tell the tree to convert itself to HTML. What a dream.
Or so it seemed. Although things started well, progress started to slow before the week was out. The problem was that the FitNesse parser is tuned to the esoteric needs of FitNesse. The parser makes choices that are perfectly fine if your goal is to generate HTML and pass it to Fit, but that aren’t quite so nice when you’re goal is to use the parse tree to process Slim tables. As a simple example, consider the problem of literals.
In FitNesse, any camel case phrase fits the pattern of a wiki word and will be turned into a wiki link. Sometimes, though, you want to use a camel case phrase and you don’t want it converted to a link. In that case you surround the phrase with literal marks as follows: !- FitNesse-!
. Anything between literal marks is simply ignored by the parser and passed through to the end.
Indeed, things inside of literals are not even escaped for html! If you put <b>hi</b>
into a wiki page, it will escape the text you’ll see <b>hi</b>
on the screen instead of a bold “hi”. On the other hand, if you put !- <b>hi</b>-!
on a page, then the HTML is left unescaped and a boldfaced “hi” will appear on the screen.
I’m telling you all of this because the devil is in the details—so bear with me a bit longer.
You know how the C and C++ languages have a preprocessor? This preprocessor handles all the #include
and #define
statements and then hands the resulting text off to the true compiler. Well, the wiki parser works the same way! Literals and !define
variables are processed first, by a different pass of the parser. Then all the rest of the wiki widgets are parsed by the true parser. The reason that we had to do this is even more devilishly detailed; so I’ll spare you. Suffice it to say that the reasons we need that preprocessor are similar to the reasons that C and C++ need it.
What does the preprocessor do with a literal? It converts it into a special widget. That widget looks like this: !lit?23?
What does that mean? It means replace me with the contents of literal #23. You see, when FitNesse sees !- FitNesse-!
it replaces it with !lit?nn?
and squirrels FitNesse
away in literal slot nn. During the second pass, that !lit?nn?
is replaced with the contents of literal slot nn. Simple, right?
OK, now back to SLIM table processing. There I was, in Norway, teaching a class during the day and coding Slim at night, and everything was going just great. And then, during one of my tests, I happened to put a literal into one of the test tables. This is perfectly normal, I didn’t want some camel case phrase turned into a link. But this perfectly normal gesture made a unit test fail for a very strange reason. I got this wonderful message from junit: expected MyPage but was !lit?3?
I knew exactly what this meant. It meant that the value MyPage
had been squirreled away by the first pass of the parser. I also knew that I had utterly no reasonable way of getting at it. So I did the only thing any sane programmer would do. I wrote my own preprocessor and used it instead of the standard one. This was “safe” since in the end I simply reconvert all the colorized tables back into wiki text and let the normal parser render it into HTML.
It was a bit of work, but I got it done at one am on a cold Norwegian night. Tests passing, everything great!
Ah, but no. By writing my own preprocessor, I broke the !define
variable processing – subtly. And when I found and fixed that I had re-broken literal processing – subtly.
If you were following my tweets at the time you saw me twitter ever more frantically about literals. It was the proverbial ball of Mercury. Every time I put my thumb on it1 it would squirt away and take some new form.
I fell into a trap. I call it the fudge trap. It goes like this:
forever do {
“I can make this work! Just one more little fudge right here!”}
I was in this trap for two months! I was making progress, and getting lots of new features to work, but I was also running into strange little quirks and unexpected bizarre behaviors caused by the fudging I was doing. So I’d fudge a little more and then keep working. But each little fudge added to the next until, eventually, I had a really nasty house of cards (or rather: pile of fudge) ready to topple every time I touched anything else. I started to fear my own code2. It was time to stop!
I knew what I had to do. I had to go back to my original architectural decision and make it the other way. There was no escape from this. The FitNesse parser was too coupled to the wiki-ness, and there was no sane way to repurpose it for test table processing.
I dreaded this. It was such a big change. I had built so much code in my house of fudge. All of it would have to be changed or deleted. And, worse, I needed to write an HTML parser.
I was lamenting to Micah about this one day in late November, and he said: “Dad, there are HTML parsers out there you know.”.
Uh…
So I went to google and typed Html Parser
. Duh. There they were. Lots and lots of shiny HTML parsers free for the using.
I picked one and started to fiddle with it. It was easy to use.
Now comes the good part. I had not been a complete dolt. Even when I was using the FitNesse parse tree, I ate my own dogfood and wrapped it in an abstract interface. No part of the Slim Table processing code actually knew that it was dealing with a FitNesse parse tree. It simply used the abstract interface to get its work done.
That meant that pulling out the wiki parser and putting in the HTML parser was a matter of re-implementing the abstract interface with the output of the new parser (which happened to be another parse tree!). This took me about a day.
There came a magic moment when I had both the wiki text version of the parser, and the HTML version of the parser working. I could switch back and forth between the two by changing one word in one module. When I got all my tests passing with both parsers, I knew I was done. And then the fun really began!
I deleted ever stitch of that wiki parse tree pile of fudge. I tore it loose and rent it asunder. It was gone, never to darken my door with it’s fudge again.
It took me a day. A day. And the result is 400 fewer lines of code, and a set of Slim tables that actually work the way they are supposed to.
Moral #1: “Fudge tastes good while you are eating it, but it makes you fat, slow, and dumb.”
Moral #2: “Eat the damned dog food. It’ll save your posterior from your own maladroit decisions.
1 I do not recommend that you actually put your thumb on any Mercury. Never mind that I used to play with liquid Mercury as a child, sloshing it around from hand to hand and endlessly stirring it with my finger. Wiser heads than I have determined that merely being in the same room with liquid Mercury can cause severe brain damage, genetic corruption, and birth defects in your children, grandchildren, and pets.
2 Fearing your own code is an indicator that you are headed for ruin. This fear is followed by self-loathing, project-loathing, career-loathing, divorce, infanticide, and finally chicken farming.
How to Guarantee That Your Software Will Suck 57
This blog is a quick comment about Justin Etheredge’s blog by the same name.
I thought the blog was good. Really. No, I did. It’s a pretty good blog. Honestly.
My problem with is is that it points the finger outwards. It’s as though software developers have no responsibility. The blog seems to suggest that projects fail because managers do dumb-ass things like not buying dual monitors, setting deadlines, and requiring documentation.
Reading a blog like Justin’s may make you feel like high-five-ing and doing a little touch-down jig. OK, fine. And, after all, there’s some truth to what Justin has to say. But there’s another side of the coin too. A pretty big side.
Your software will suck if you write it badly. Yes, you should have good tools. Yes, you should work under realistic schedules. Yes, you should have time for social interaction. But these aren’t the things that make software suck. YOU, make your software suck.
Can you write good software with just one monitor? Of course you can. It might not be ideal, but what is?
Can you write good software if the deadlines are unreasonable? Of course you can! The definition of an unreasonable deadline is a deadline you won’t make, so you might as well make the code as good as it can be in the time you’ve got. If we’ve learned anything in the last 50 years it’s that rushing won’t get you to the deadline faster.
Can you write good software if you also have to write documentation? Can you write good software if your machine isn’t top-of-the-line? Can you write good software while standing on your head under water? (er, well, I’ll give you that might be tough, but for all the others:) Of course you can!
Don’t get me wrong. I think short-shrifting on tools, monitors, and schedules is stupid. I think Justin’s points are all valid. But the burden doesn’t fall solely upon management. We also have to do our jobs well.
Marick's Law 51
A month ago I was deep in the throes of shipping the current release of FitNesse. I just wanted to get it done. I was close to delivery when I spotted a subtle flaw. To fix this flaw I decided to insert identical if
statements into each of 9 implementations of an abstract function.
My green wrist band was glowing a nasty shade of puke. I knew I was duplicating code. I knew that I should use the Template Method pattern. But that just seemed too hard. I was convinced that it would be faster to spew the duplicated code out into the derivatives, get the release done, and then clean it up later.
So this morning I was doing something else, and I spotted this duplicated code. I sighed, as I looked down at my green wrist band, and thought to myself that I’d better eat my own dog food and clean this mess up before it gets any worse. I was dreading it.
I made sure that every occurrence of the statement was identical. Then I went to the base class with the intention of refactoring a Template Method. When, what to my wondering eyes should appear, but a Template Method that was already there.
I sheepishly copied and pasted the if
statement from one of the derivatives into the Template Method, and then deleted the other eight instances.
I ran the tests.
They all passed.
Damn.
My green wrist band is shouting: “I TOLD YOU SO!”
For my penance I did 20 recitations of Marick’s law. “When it comes to code it never pays to rush.”
The Truth about BDD 158
I really like the concept of BDD (Behavior Driven Development). I think Dan North is brilliant, and had done us all a great service by presenting the concept.
OK, you can “feel” the “but” coming, can’t you?
It’s not so much a “but” as an “aha!”. (The punch line is at the end of this article, so don’t give up in the middle.)
BDD is a variation on TDD. Whereas in TDD we drive the development of a module by “first” stating the requirements as unit tests, in BDD we drive that development by first stating the requirements as, well, requirements. The form of those requirements is fairly rigid, allowing them to be interpreted by a tool that can execute them in a manner that is similar to unit tests.
For example,
GIVEN an employee named Bob making $12 per hour. WHEN Bob works 40 hours in one week; THEN Bob will be paid $480 on Friday evening.
The Given/When/Then convention is central to the notion of BDD. It connects the human concept of cause and effect, to the software concept of input/process/output. With enough formality, a tool can be (and has been) written that interprets the intent of the requirement and then drives the system under test (SUT) to ensure that the requirement works as stated.
The argued benefit is that the language you use affects the way you think (See this. and so if you use a language closer to the way humans think about problems, you’ll get better thought processes and therefore better results.
To say this differently, the Given/When/Then convention stimulates better thought processes than the AssertEquals(expected, actual);
convention.
But enough of the overview. This isn’t what I wanted to talk about. What struck me the other day was this…
The Given/When/Then syntax of BDD seemed eerily familiar when I first heard about it several years ago. It’s been tickling at the back of my brain since then. Something about that triplet was trying to resonate with something else in my brain.
Then yesterday I realized that Given/When/Then is very similar to If/And/Then; a convention that I have used for the last 20+ years to read state transition tables.
Consider my old standard state transition table: The Subway Turnstile:
Current State | Event | New State | Action |
LOCKED | COIN | UNLOCKED | Unlock |
LOCKED | PASS | LOCKED | Alarm |
UNLOCKED | COIN | UNLOCKED | Thankyou |
UNLOCKED | PASS | LOCKED | Lock |
- If we are in the LOCKED state, and we get a COIN event, then we go to the UNLOCKED state, and we invoke the Unlock action.
- If we are in the LOCKED state, and we get a PASS event, then we stay in the UNLOCKED state, and we invoke the Alarm action.
- etc.
This strange similarity caused me to realize that GIVEN/WHEN/THEN is simply a state transition, and that BDD is really just a way to describe a finite state machine. Clearly “GIVEN” is the current state of the system to be explored. “WHEN” describes an event or stimulus to that system. “THEN” describes the resulting state of the system. GIVEN/WHEN/THEN is nothing more than a description of a state transition, and the sum of all the GIVEN/WHEN/THEN statement is nothing more than a Finite State Machine.
Perhaps if I rephrase this you might see why I think this is a bit more than a ho-hum.
Some of the brightest minds in our industry, people like Dan North, Dave Astels, David Chelimsky, Aslak Hellesoy, and a host of others, have been pursuing the notion that if we use a better language to describe automated requirements, we will improve the way we think about those requirements, and therefore write better requirements. The better language that they have chosen and used for the last several years uses the Given/When/Then form which, as we have seen, is a description of a finite state machine. And so it all comes back to Turing. It all comes back to marks on a tape. We’ve decided that the best way to describe the requirements of a system is to describe it as a turing machine.
OK, perhaps I overdid the irony there. Clearly we don’t need to resort to marks on a tape. But still there is a grand irony in all this. The massive churning of neurons struggling with this problem over years and decades have reconnected the circle to the thoughts of that brave pioneer from the 1940s.
But enough of irony. Is this useful? I think it may be. You see, one of the great benefits of describing a problem as a Finite State Machine (FSM) is that you can complete the logic of the problem. That is, if you can enumerate the states and the events, then you know that the number of paths through the system is no larger than S * E. Or, rather, there are no more than S*E transitions from one state to another. More importantly, enumerating them is simply a matter of creating a transition for every combination of state and event.
One of the more persistent problems in BDD (and TDD for that matter) is knowing when you are done. That is, how do you know that you have written enough scenarios (tests). Perhaps there is some condition that you have forgotten to explore, some pathway through the system that you have not described.
This problem is precisely the kind of problem that FSMs are very good at resolving. If you can enumerate the states, and events, then you know the number of paths though the system. So if Given/When/Then statements are truly nothing more than state transitios, all we need to do is enumerate the number of GIVENs and the number of WHENs. The number of scenarios will simply be the product of the two.
To my knowledge, (which is clearly inadequate) this is not something we’ve ever tried before at the level of a business requirements document. But even if we have, the BDD mindset may make it easier to apply. Indeed, if we can formally enumerate all the Givens and Whens, then a tool could determine whether our requirements document has executed every path, and could find those paths that we had missed.
So, in conclusion, TDD has led us on an interesting path. TDD was adopted as a way to help us phrase low level requirements and drive the development of software based on those requirements. BDD, a variation of TDD, was created to help us think better about higher level requirements, and drive the development of systems using a language better than unit tests. But BDD is really a variation of Finite State Machine specifications, and FSMs can be shown, mathematically, to be complete. Therefore, we may have a way to conclusively demonstrate that our requirements are complete and consistent. (Apologies to Godel).
In the end, the BDDers may have been right that language improves the way we think about things. Certainly in my silly case, it was the language of BDD that resonated with the language of FSM.
Dirty Rotten ScrumDrels 222
So we’ve finally found the answer. We know who’s to blame. It’s SCRUM! SCRUM is the reason that the agile movement is failing. SCRUM is the reason that agile teams are making a mess. SCRUM is the root cause behind all the problems and disasters. SCRUM is the cause of the “Decline and Fall of Agile.”
Yeah, and I’ve got a bridge to sell you.
Scrum is not the problem. Scrum never was the problem. Scrum never will be the problem. The problem, dear craftsmen, is our own laziness.
It makes no sense to blame Scrum for the fact that we don’t write tests and don’t keep our code clean. We can’t blame scrum for technical debt. Technical debt was around long before there was scrum, and it’ll be around long after. No it’s not scrum that’s to blame. The culprits remain the same as they ever were: us.
Of course it’s true that a two day certification course is neither necessary nor sufficient to create a good software leader. It’s also true that the certificate you get for attending a CSM course is good for little more than showing that you paid to attend a two day CSM course. It’s also true that scrum leaves a lot to be desired when it comes to engineering practices. But it is neither the purpose of scrum nor of CSMs to make engineers out of us, or to instill the disciplines of craftsmanship within us. That’s our job!
Some have implied that if all those scrum teams had adopted XP instead of scrum, they wouldn’t be having all the technical debt they are seeing. BALDERDASH!
Let me be more precise. ASININE, INANE, ABSURDITY. BALONEY. DINGOES KIDNEYS.
Let me tell you all, here, now, and forevermore, it is quite possible to do XP badly. It’s easy to build technical debt while going through the motions of TDD. It’s a no brainer to create a wasteland of code with your pair partner. And, believe me, you can such a Simple Design that You Aint Gonna Maintain It. And I’m not speaking metaphorically.
Do you want to know the real secret behind writing good software? Do you want to know the process that will keep your code clean? Do you want the magic bullet, the secret sauce, the once and for all one and only truth?
OK, here it is. Are you ready? The secret is…
The secret is…
Do a good job.
Oh, yeah, and stop blaming everything (and everybody) else for your own laziness.
Upcoming Speaking Engagements 40
I’m speaking this Friday at RubyConf on Better Ruby Through Functional Programming. I’ll introduce long-overlooked ideas from FP, why they are important for Ruby programmers, and how to use them in Ruby.
In two weeks, I’m speaking on Wednesday, 11/19 at QCon San Francisco on Radical Simplification Through Polyglot and Poly-paradigm Programming. The idea of this talk is that combining the right languages and modularity paradigms (i.e., objects, functions, aspects) can simplify your code base and reduce the amount of code you have to write and manage, providing numerous benefits.
Back in Chicago, I’m speaking at the Polyglot Programmer’s meeting on The Seductions of Scala, 11/13. It’s an intro to the Scala language, which could become the language of choice for the JVM. I’m repeating this talk at the Chicago Java User’s Group on 12/16. I’m co-writing a book on Scala with Alex Payne. O’Reilly will be the publisher.
Incidently, Bob Martin is also speaking in Chicago on 11/13 at the APLN Chicago meeting on Software Professionalism.
A Scala-style "with" Construct for Ruby 108
Scala has a “mixin” construct called traits, which are roughly analogous to Ruby modules. They allow you to create reusable, modular bits of state and behavior and use them to compose classes and other traits or modules.
The syntax for using Scala traits is quite elegant. It’s straightforward to implement the same syntax in Ruby and doing so has a few useful advantages.
For example, here is a Scala example that uses a trait to trace calls to a Worker.work
method.
// run with "scala example.scala"
class Worker {
def work() = "work"
}
trait WorkerTracer extends Worker {
override def work() = "Before, " + super.work() + ", After"
}
val worker = new Worker with WorkerTracer
println(worker.work()) // => Before, work, After
Note that WorkerTracer
extends Worker
so it can override the work
method. Since Scala is statically typed, you can’t just define an override
method and call super
unless the compiler knows there really is a “super” method!
Here’s a Ruby equivalent.
# run with "ruby example.rb"
module WorkerTracer
def work; "Before, #{super}, After"; end
end
class Worker
def work; "work"; end
end
class TracedWorker < Worker
include WorkerTracer
end
worker = TracedWorker.new
puts worker.work # => Before, work, After
Note that we have to create a subclass, which isn’t required for the Scala case (but can be done when desired).
If you know that you will always want to trace calls to work
in the Ruby case, you might be tempted to dispense with the subclass and just add include WorkerTracer
in Worker
. Unfortunately, this won’t work. Due to the way that Ruby resolves methods, the version of work
in the module will not be found before the version defined in Worker
itself. Hence the subclass seems to be the only option.
However, we can work around this using metaprogramming. We can use WorkerTracer#append_features(...)
. What goes in the argument list? If we pass Worker
, then all instances of Worker
will be effected, but actually we’ll still have the problem with the method resolution rules.
If we just want to affect one object and work around the method resolution roles, then we need to pass the singleton class (or eigenclass or metaclass ...) for the object, which you can get with the following expression.
metaclass = class << worker; self; end
So, to encapsulate all this and to get back to the original goal of implementing with
-style semantics, here is an implementation that adds a with
method to Object
, wrapped in an rspec example.
# run with "spec ruby_with_spec.rb"
require 'rubygems'
require 'spec'
# Warning, monkeypatching Object, especially with a name
# that might be commonly used is fraught with peril!!
class Object
def with *modules
metaclass = class << self; self; end
modules.flatten.each do |m|
m.send :append_features, metaclass
end
self
end
end
module WorkerTracer
def work; "Before, #{super}, After"; end
end
module WorkerTracer1
def work; "Before1, #{super}, After1"; end
end
class Worker
def work; "work"; end
end
describe "Object#with" do
it "should make no changes to an object if no modules are specified" do
worker = Worker.new.with
worker.work.should == "work"
end
it "should override any methods with a module's methods of the same name" do
worker = Worker.new.with WorkerTracer
worker.work.should == "Before, work, After"
end
it "should stack overrides for multiple modules" do
worker = Worker.new.with(WorkerTracer).with(WorkerTracer1)
worker.work.should == "Before1, Before, work, After, After1"
end
it "should stack overrides for a list of modules" do
worker = Worker.new.with WorkerTracer, WorkerTracer1
worker.work.should == "Before1, Before, work, After, After1"
end
it "should stack overrides for an array of modules" do
worker = Worker.new.with [WorkerTracer, WorkerTracer1]
worker.work.should == "Before1, Before, work, After, After1"
end
end
You should carefully consider the warning about monkeypatching Object
! Also, note that Module.append_features
is actually private, so I had to use m.send :append_features, ...
instead.
The syntax is reasonably intuitive and it eliminates the need for an explicit subclass. You can pass a single module, or a list or array of them. Because with
returns the object, you can also chain with
calls.
A final note; many developers steer clear of metaprogramming and reflection features in their languages, out of fear. While prudence is definitely wise, the power of these tools can dramatically accelerate your productivity. Metaprogramming is just programming. Every developer should master it.
Traits vs. Aspects in Scala 91
Scala traits provide a mixin composition mechanism that has been missing in Java. Roughly speaking, you can think of traits as analogous to Java interfaces, but with implementations.
Aspects, e.g., those written in AspectJ, are another mechanism for mixin composition in Java. How do aspects and traits compare?
Let’s look at an example trait first, then re-implement the same behavior using an AspectJ aspect, and finally compare the two approaches.
Observing with Traits
In a previous post on Scala, I gave an example of the Observer Pattern implemented using a trait. Chris Shorrock and James Iry provided improved versions in the comments. I’ll use James’ example here.
To keep things as simple as possible, let’s observe a simple Counter
, which increments an internal count variable by the number input to an add
method.
package example
class Counter {
var count = 0
def add(i: Int) = count += i
}
The count
field is actually public, but I will only write to it through add
.
Here is James’ Subject trait that implements the Observer Pattern.
package example
trait Subject {
type Observer = { def receiveUpdate(subject:Any) }
private var observers = List[Observer]()
def addObserver(observer:Observer) = observers ::= observer
def notifyObservers = observers foreach (_.receiveUpdate(this))
}
Effectively, this says that we can use any object as an Observer
as long as it matches the structural type { def receiveUpdate(subject:Any) }
. Think of structural types as anonymous interfaces. Here, a valid observer is one that has a receiveUpdate
method taking an argument of Any
type.
The rest of the trait manages a list of observers and defines a notifyObservers
method. The expression observers ::= observer
uses the List
::
(“cons”) operator to prepend an item to the list. (Note, I am using the default immutable List
, so a new copy is created everytime.)
The notifyObservers
method iterates through the observers, calling receiveUpdate
on each one. The _
that gets replaced with each observer during the iteration.
Finally, here is a specs file that exercises the code.
package example
import org.specs._
object CounterObserverSpec extends Specification {
"A Counter Observer" should {
"observe counter increments" in {
class CounterObserver {
var updates = 0
def receiveUpdate(subject:Any) = updates += 1
}
class WatchedCounter extends Counter with Subject {
override def add(i: Int) = {
super.add(i)
notifyObservers
}
}
var watchedCounter = new WatchedCounter
var counterObserver = new CounterObserver
watchedCounter.addObserver(counterObserver)
for (i <- 1 to 3) watchedCounter.add(i)
counterObserver.updates must_== 3
watchedCounter.count must_== 6
}
}
}
The specs library is a BDD tool inspired by rspec in Rubyland.
I won’t discuss it all the specs-specific details here, but hopefully you’ll get the general idea of what it’s doing.
Inside the "observe counter increments" in {...}
, I start by declaring two classes, CounterObserver
and WatchedCounter
. CounterObserver
satisfies our required structural type, i.e., it provides a receiveUpdate
method.
WatchedCounter
subclasses Counter
and mixes in the Subject
trait. It overrides the add
method, where it calls Counter
’s add
first, then notifies the observers. No parentheses are used in the invocation of notifyObservers
because the method was not defined to take any!
Next, I create an instance of each class, add the observer to the WatchedCounter
, and make 3 calls to watchedCounter.add
.
Finally, I use the “actual must_== expected
” idiom to test the results. The observer should have seen 3 updates, while the counter should have a total of 6.
The following simple bash shell script will build and run the code.
SCALA_HOME=...
SCALA_SPECS_HOME=...
CP=$SCALA_HOME/lib/scala-library.jar:$SCALA_SPECS_HOME/specs-1.3.1.jar:bin
rm -rf bin
mkdir -p bin
scalac -d bin -cp $CP src/example/*.scala
scala -cp $CP example/CounterObserverSpec
Note that I put all the sources in a src/example
directory. Also, I’m using v1.3.1 of specs, as well as v2.7.1 of Scala. You should get the following output.
Specification "CounterObserverSpec"
A Counter Observer should
+ observe counter increments
Total for specification "CounterObserverSpec":
Finished in 0 second, 60 ms
1 example, 2 assertions, 0 failure, 0 error
Observing with Aspects
Because Scala compiles to Java byte code, I can use AspectJ to advice Scala code! For this to work, you have to be aware of how Scala represents its concepts in byte code. For example, object declarations, e.g., object Foo {...}
become static final classes. Also, method names like +
become $plus
in byte code.
However, most Scala type, method, and variable names can be used as is in AspectJ. This is true for my example.
Here is an aspect that observes calls to Counter.add
.
package example
public aspect CounterObserver {
after(Object counter, int value):
call(void *.add(int)) && target(counter) && args(value) {
RecordedObservations.record("adding "+value);
}
}
You can read this aspect as follows, after calling Counter.add
(and keeping track of the Counter object that was called, and the value passed to the method), call the static method record
on the RecordedObservations
.
I’m using a separate Scala object RecordedObservations
package example
object RecordedObservations {
private var messages = List[String]()
def record(message: String):Unit = messages ::= message
def count() = messages.length
def reset():Unit = messages = Nil
}
Recall that this is effectively a static final Java class. I need this separate object, rather than keeping information in the aspect itself, because of the simple-minded way I’m building the code. ;) However, it’s generally a good idea with aspects to delegate most of the work to Java or Scala code anyway.
Now, the “spec” file is:
package example
import org.specs._
object CounterObserverSpec extends Specification {
"A Counter Observer" should {
"observe counter increments" in {
RecordedObservations.reset()
var counter = new Counter
for (i <- 1 to 3) counter.add(i)
RecordedObservations.count() must_== 3
counter.count must_== 6
}
}
}
This time, I don’t need two more classes for the adding a mixin trait or defining an observer. Also, I call RecordedObservations.count
to ensure it was called 3 times.
The build script is also slightly different to add the AspectJ compilation.
SCALA_HOME=...
SCALA_SPECS_HOME=...
ASPECTJ_HOME=...
CP=$SCALA_HOME/lib/scala-library.jar:$SCALA_SPECS_HOME/specs-1.3.1.jar:$ASPECTJ_HOME/lib/aspectjrt.jar:bin
rm -rf bin app.jar
mkdir -p bin
scalac -d bin -cp $CP src/example/*.scala
ajc -1.5 -outjar app.jar -cp $CP -inpath bin src/example/CounterObserver.aj
aj -cp $ASPECTJ_HOME/lib/aspectjweaver.jar:app.jar:$CP example.CounterObserverSpec
The ajc
command not only compiles the aspect, but it “weaves” into the compiled Scala classes in the bin
directory. Actually, it only affects the Counter
class. Then it writes all the woven and unmodified class files to app.jar
, which is used to execute the test. Note that for production use, you might prefer load-time weaving.
The output is the same as before (except for the milliseconds), so I won’t show it here.
Comparing Traits with Aspects
So far, both approaches are equally viable. The traits approach obviously doesn’t require a separate language and corresponding tool set.
However, traits have one important limitation with respect to aspects. Aspects let you define pointcuts that are queries over all possible points where new behavior or modifications might be desired. These points are called join points in aspect terminology. The aspect I showed above has a simple pointcut that selects one join point, calls to the Counter.add
method.
However, what if I wanted to observe all state changes in all classes in a package? Defining traits for each case would be tedious and error prone, since it would be easy to overlook some cases. With an aspect framework like AspectJ, I can implement observation at all the points I care about in a modular way.
Aspect frameworks support this by providing wildcard mechanisms. I won’t go into the details here, but the *
in the previous aspect is an example, matching any type. Also, one of the most powerful techniques for writing robust aspects is to use pointcuts that reference only annotations, a form of abstraction. As a final example, if I add an annotation Adder
to Counter.add
,
package example
class Counter {
var count = 0
@Adder def add(i: Int) = count += i
}
Then I can rewrite the aspect as follows.
package example
public aspect CounterObserver {
after(Object counter, int value):
call(@Adder void *.*(int)) && target(counter) && args(value) {
RecordedObservations.record("adding "+value);
}
}
Now, there are no type and method names in the pointcut. Any instance method on any visible type that takes one int
(or Scala Int
) argument and is annotated with Adder
will get matched.
Note: Scala requires that you create any custom annotations as normal Java annotations. Also, if you intend to use them with Aspects, use runtime retention policy, which will be necessary if you use load-time weaving.
Conclusion
If you need to mix in behavior in a specific, relatively-localized set of classes, Scala traits are probably all you need and you don’t need another language. If you need more “pervasive” modifications (e.g., tracing, policy enforcement, security), consider using aspects.
Acknowledgements
Thanks to Ramnivas Laddad, whose forthcoming 2nd Edition of AspectJ in Action got me thinking about this topic.
Type Scum 35
Getting existing code under test is hard work, but it is fruitful. Yes, you get code that is easier to change, but more importantly, you get knowledge – you learn things about programming which make you better at avoiding common traps. Sadly, many of these traps aren’t well recognized yet.
The trap that I am going to write about today is one that I call type scum. It’s most prevalent in C and C++ but it can attack in any of the traditional statically typed languages.
Type Scum is the cruft in a code base which makes it impossible to compile a single file without an entire sub-stratum of defined types. I’m not talking about the primary, or even the secondary abstractions in your system, but rather the 200 or so basic types and structs that your abstractions depend upon.
Again, the problem is worst in C++ and C. At some point, every C or C++ developer feels the urge to isolate him or herself from the basic types of the language. The unsigned int type becomes uint32 and unsigned char * becomes uchar16_ptr. And, if that was all, it would be okay. But no, people define data transfer objects which aggregate these type pseudonyms together into large muddles. No file can compile without bringing in a world of types which cushion the code from dangerous things like the platform and testing harnesses.
No wait, testing harnesses are good. What can we do?
The unfortunate thing is that it is very hard to pull type scum out of a system once it’s been infected, but we can learn how to avoid it or at least manage it better:
- If you must provide a sub-stratum of basic types in your system, do it in one place. There should be a single library (and associated headers) that you include whenever you need it. This library (and headers) should contain nothing else.
- If you must create DTO (Data Transfer Object) types, minimize them. A good general purpose structure can carry a wide variety of different types of data and simplify testing.
- Push the DTOs to the edges. There are some systems where you really do care whether an internal computation happens in unsigned long int or unsigned long long int but they are rare. Basic data types and tolerances matter when two systems need to agree upon them, and that happens at component boundaries. In many systems, the internal code can use platform types directly.
There you go. Type scum bad.
I’m sure that some people reading this will say “Hey, isn’t this the exact opposite of the advice that people give for a system with the primitive obsession code smell?” The answer is “yes.” But, to me, primitive obsession is a different problem. It’s something which is the result of a lack of real behavioral abstractions in a system, not the lack of larger data holders.
Different problem.
Type scum bad.
The Open-Closed Principle for Languages with Open Classes 127
We’ve been having a discussion inside Object Mentor World Design Headquarters about the meaning of the OCP for dynamic languages, like Ruby, with open classes.
For example, in Ruby it’s normal to define a class or module, e.g.,
# foo.rb
class Foo
def method1 *args
...
end
end
and later re-open the class and add (or redefine) methods,
# foo2.rb
class Foo
def method2 *args
...
end
end
Users of Foo
see all the methods, as if Foo
had one definition.
foo = Foo.new
foo.method1 :arg1, :arg2
foo.method2 :arg1, :arg2
Do open classes violate the Open-Closed Principle? Bertrand Meyer articulated OCP. Here is his definition1.
Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.
He elaborated on it here.
... This is the open-closed principle, which in my opinion is one of the central innovations of object technology: the ability to use a software component as it is, while retaining the possibility of adding to it later through inheritance. Unlike the records or structures of other approaches, a class of object technology is both closed and open: closed because we can start using it for other components (its clients); open because we can at any time add new properties without invalidating its existing clients.
Tell Less, Say More: The Power of Implicitness
So, if one client require
’s only foo.rb
and only uses method1
, that client doesn’t care what foo2.rb
does. However, if the client also require
’s foo2.rb
, perhaps indirectly through another require
, problems will ensue unless the client is unaffected by what foo2.rb
does. This looks a lot like the way “good” inheritance should behave.
So, the answer is no, we aren’t violating OCP, as long as we extend a re-opened class following the same rules we would use when inheriting from it.
If we use inheritance instead:
# foo.rb
class Foo
def method1 *args
...
end
end
...
class DerivedFoo < Foo
def method2 *args
...
end
end
...
foo = SubFoo.new # Instantiate different class...
foo.method1 :arg1, :arg2
foo.method2 :arg1, :arg2
One notable difference is that we have to instantiate a different class. This is an important difference. While you can often just use inheritance, and maybe you should prefer it, inheritance only works if you have full control over what types get instantiated and it’s easy to change which types you use. Of course, inheritance is also the best approach when you need all behavioral variants simulateneously, i.e., each variant in one or more objects.
Sometimes you want to affect the behavior of all instances transparently, without changing the types that are instantiated. A slightly better example, logging method calls, illustrates the point. Here we use the “famous” alias_method
in Ruby.
# foo.rb
class Foo
def method1 *args
...
end
end
# logging_foo.rb
class Foo
alias_method :old_method1, :method1
def method1 *args
p "Inside method1(#{args.inspect})"
old_method1 *args
end
end
...
foo = Foo.new
foo.method1 :arg1, :arg2
Foo.method1
behaves like a subclass override, with extended behavior that still obeys the Liskov-Substitution Principle (LSP).
So, I think the OCP can be reworded slightly.
Software entities (classes, modules, functions, etc.) should be open for extension, but closed for source modification.
We should not re-open the original source, but adding functionality through a separate source file is okay.
Actually, I prefer a slightly different wording.
Software entities (classes, modules, functions, etc.) should be open for extension, but closed for source and contract modification.
The extra and contract is redundant with LSP. I don’t think this kind of redundancy is necessarily bad. ;) The contract is the set of behavioral expectations between the “entity” and its client(s). Just as it is bad to break the contract with inheritance, it is also bad to break it through open classes.
OCP and LSP together are our most important design principles for effective organization of similar vs. variant behaviors. Inheritance is one way we do this. Open classes provide another way. Aspects provide a third way and are subject to the same design issues.
1 Meyer, Bertrand (1988). Object-Oriented Software Construction. Prentice Hall. ISBN 0136290493.