Ryan's Blog
Patterns of Persistence
Patterns of Persistence, and article written by myself, Jim Majure and Travis Klotz has been published.
It can be found at the below links:
Patterns of Persistence, Part 1: Strategies and best practices for modern ORM tools
Patterns of Persistence, Part 2: Increase code reuse and enhance performance
Posted at 09:59AM Apr 22, 2008 by Ryan Senior in Design | Comments[0]
JiBX Flexible Collections
In a previous post I talked about some of the difficulties I had with JiBX. I was using JiBX to map to the vendor neutral Acord Life Insurance schemas. One recurring theme in the Acord schemas is repeating elements without a wrapping tag:
<tag>
<tagA/>
<tagB/>
</tag>
<tag>
<tagA/>
<tagB/>
</tag>
A downside of using Acord is that there are so many tags (since it has to fit so many business models and domains) that it is not feasible to handle every possible tag in Acord. To allow this, JiBX has an attribute called "flexible" and "ordered" that will skip tags that are in the XML, but are not bound (the default behavior is to throw an exception when this happens) and will allow tags in any order. Since I didn't want to write code for the thousands of tags, I set the flexible attribute to true and the ordered attribute to false. The problem happens where theres a repeating tag like the one above, used with the flexible feature. Once the tags are marked flexible, JiBX complains with this error:
[bind] Error: All child components must define element names for flexible='true'
In this mailing list post form the JiBX creator, this is apparently a known issue: http://marc.info/?l=jibx-users&m=118584611515298&w=2.
The options are to modify the message format or create a custom marshaller. I am not able to modify the Acord message format,
so I was left with a custom marshaller. I wasn't very happy with that idea, since it meant that I would basically be hand parsing the XML (the exact
thing I was trying to avoid by using JiBX). I tried a couple of solutions and what seemed to be the best was a hybrid approach of a custom marshaller,
and a JiBX marshaller. To do this I created a JiBX mapping for that class that I was going to write the custom marshaller for. It looked something like this:<mapping prefix="ns" uri="http://namespace/here" default="none" class="com.company.product.Address" name="Address">
<value name="AddressLine1" field="addressLine1" usage="optional" />
<!--...-->
</mapping>
This is a normal JiBX mapping, the only difference is that although the mapping is defined, it isn't connected to anything. Technically
this should have been reference in the "Person" structure, since, for this example that is where the Addresses reside. Insteadof putting the reference to the Address in the person, I deferred that to the custom marshaller:
<structure field="addresses" name="Address" usage="optional" marshaller="com.company.product.AddressMarshaller" unmarshaller="com.company.AddressMarshaller"/>
This reference doesn't reference a collection. It just references a single address and then the marshaller takes care of the rest.
Now the marshalling and unmarshalling code looks like:
public void marshal(Object addresses, IMarshallingContext ctx) throws JiBXException {
MarshallingContext mctx = (MarshallingContext)ctx;
List addressList = (List) addresses;
for(Iterator it = addressList.iterator();it.hasNext();){
((IMarshallable)it.next()).marshal(mctx);
}
}
public Object unmarshal(Object obj, IUnmarshallingContext uctx) throws JiBXException {
UnmarshallingContext unmarshalContext = (UnmarshallingContext)uctx;
List list = (List) obj;
if (list == null) {
list = new ArrayList();
}
while (unmarshalContext.isAt(getUri(), getName())) {
list.add(unmarshalContext.unmarshalElement(Address.class));
}
return list;
}
Note that in the above code there is no hand parsing of the XML elements. Instead the actual marshalling and unmarshalling of the
individual Address tags are deferred to JiBX. Basically the custom marshaller is just finding the one or more Address tags
and deferring the marshalling/unmarshalling of those tags to the regular JiBX marshaller/unmarshaller.This allows unordered/flexible mappings to collections that do not have a wrapping tag. I was able to genericize this approach a
little more and created custom mappings for HashMaps and a couple of other structures that were useful handling the Acord documents.
Posted at 12:48PM Mar 21, 2008 by Ryan Senior in Design | Comments[2]
The JiBX Conundrum
Overview
JiBX is a binding framework from XML to Java Objects and back. JiBX could be used in place of frameworks like XMLBeans, Castor or just plain JDOM parsing. JiBX works through a JiBX binding file. It essentially connects a particular tag value or attribute value to a field in a Java class. This XML mapping file is read into the JiBX compiler and then the Java classes that the XML is bound to, are instrumented to handle the XML. Basically this boils down to many methods being created on the Java class that when passed a Marshalling or Unmarshalling context will set the appropriate variables with the correct value. I'm still not convinced that JiBX has saved me time, or whether it will be
better in the long run over manual DOM style XML parsing.
Setting the Stage
The end result of this XML parsing is going to be web services. Both requesting information from web services developed by vendors and providing web services to those vendor systems. To facilitate this, the Acord XML format was chosen. To those not familiar with Acord, it is a large insurance XML specification (by large, if I remember correctly, the schemas for this spec was about 800 KB total). The specification is so large because it would theoretically handle all of the insurance related transactions and messages that any organization would need. Putting this requirement in a technical context, this throws out frameworks like XMLBeans. Generating code for all of these tags and the awkward Acord structure is not an option. Next is that this must work on JDK 1.4, with Websphere. It must be JDK 1.4 because these need for this code to be ran from IBM portlets and the current IBM portlet engine only runs on JDK 1.4. Websphere also presents some concerns because XML parsing using the frameworks not already included in Websphere has always gone bad for me. The final requirement is that it must have Spring-WS support. Spring-WS works well in a 1.4 environment and we were planning on using that for the web service framework.
JiBX is the Solution?
With the requirements above, the solution came down to Castor or JiBX. Both looked like they did what we needed to do, however we had quite a few problems getting Castor, Spring-WS and Websphere along with the old version of the JDK. Oddly enough the fix for our problems was switching to 1.5 (not an option). So we were left with JiBX or parsing the XML by hand using DOM or something similar. Obviously not wanting to go back to the bad old days of hand parsing we went with JiBX. It supports mapping to/from an XML and Object model. There's no code generation based on schema and it works well in 1.4. Before going into some of the problems I had with JiBX, I'll start by covering what JiBX does well. The benefits of JiBX over XMLBeans for me is that most of the Acord schema, I don't care about. Most of the tags I won't use and even for a particular Acord transaction, I don't care about many of the tags. By "don't care" I mean that either my system can't supply that value, or if that value is supplied, I can't do anything with it, so it might as well not even be there from my perspective. Another key point of JiBX is that I can keep my object model how I would like it. JiBX maps an XML to my object model. So in the case of Acord, the XML structure is pretty obscure and difficult to mimic as an object model. Mapping the awkward Acord structure to something that makes more sense from my application's perspective is a great feature. Another benefit of JiBX is that it performed pretty well with the XMLs that I have sent it. I don't have any benchmarks, but I also haven't had any complaints about speed.
Initial JiBX Pains
So part of the initial pain of JiBX is how to instrument the bytecode. Since the JiBX compiler needs to read in the XML mapping file and modify the Java bytecode, this needs to happen as part of the build process. This isn't a big deal for Ant because JiBX has a nice set of Ant targets for it. Where this becomes a problem is in the day to day development in the IDE. I was developing a web service which, for local development at least I was deploying to Tomcat. Instrumenting the bytecode before it was deployed in Tomcat ended up being quite a task. I was using Rational Application Developer (which might have played a role in the problem) and had a difficult time with the solutions listed on the JiBX site. They recommend creating a custom Builder in Eclipse or using a plugin that they wrote that is still in the early stages. I tried both of these and didn't have any luck. Even when the compiling worked, it would always try to autodeploy before the bytecode manipulation was done. So I ended up manually running the Ant target that did the instrumenting and then either deploying via an Ant target or manually publishing to Tomcat through the Eclipse WTP Server plugin.
Reusability Problems
Probably my biggest complaint about JiBX is around the reusability of JiBX code. I was developing many web services and clients for the vendor web services. Since these messages were Acord based, there were many common things. For example, there are Acord code values all over the message. These Acord codes have two parts, one is the numeric value for the code and then the text value for the code. My first reaction was to create a shared JAR file with all of these common things in it. So it would have base classes that had common header pieces, and these Acord Codes and more. The problem is, JiBX cannot bind into Java classes that are in a JAR. So for example, I created the AcordCode classes and put it into a JAR, JiBX would not bind it even if it was in the classpath. The answer to this is to create a "do nothing" subclass of AcordCode. It defines no methods, no variables etc, just extends it. The reason this worked is that JiBX would manipulate the subclass and trap calls to the superclasses variables. Another gotcha here was that for this to work AcordCode needed to have it's variables set to protected or public. Since JiBX by default goes after the variables directly, if it's marked as private, it can't get to it through the subclass. It's important here to note that JiBX does support using getters and setters vs. using the variables directly. This however makes the XML binding file much more verbose. When used with a field like the Acord code fields, it can become very cumbersome.
Another point of non-resuability is around the binding files themselves. When you are working with JiBX, especially with a big spec like Acord, you find yourself spending a lot of time on the bindings. There's a lot of time and a lot of code that goes into these binding files. The problem is there is very limited reuse on these binding files. The binding files can be reused inside the same project, but they cannot be reused inside of other projects. For example, there are several "header" type of fields in an Acord message. Fields like a GUID for the transaction, an indication of which transaction is being used etc. I put these fields in a base Acord message (marking them as protected). This worked out fine because it made sense to subclass them anyway. The problem here is that the binding to those tags has to be repeated on each project. So each project needs to define that the GUID needs to map to the GUID field on that base class. JiBX does not allow reading in these binding files from a JAR. In my opinion, this is a big deal. Now I am forced to either copy and paste the bindings or reinvent the wheel each time.
One more problem I had was on the reusability of the binding files within a project. Knowing that it was difficult to share common classes across projects, I combined a couple of clients that used a similar Acord message into one client jar. Often with web services you want to wrap the message in a tag that maps to the operation. So for this, I ended up having three messages that were basically the same, except the tag that wraps it was different. This is actually quite hard in JiBX. What I wanted was to define a mapping, of a bunch of fields and tags that didn't map directly to a particular class. Then I wanted to include that in a mapping to a class. This isn't possible in JiBX, so I ended up moving as much of it as I could to their own mappings. In the end, I was able to cut down on some of the duplication, but I sill needed to duplicate about 20-30 lines of the binding file to each of the three messages.
Quirks
I also ran into quite a few quirks of JiBX. Basically these quirks came down to the creators of JiBX made design decisions that were different from what I needed using Acord. One example of this is using a non-wrapped repeating tag with flexible elements. JiBX has a setting called "flexible" that basically tells JiBX that if there is a tag there that is not in the binding file, to ignore it. This is definitely necessary when working with Acord because we can't handle all possible tags and there are many tags that can be added and still validate against the schema. The problem occurs when there is a repeating tag, that has a flexible parent tag. As an example:
<tag>
<tagA/>
<tagB/>
</tag>
<tag>
<tagA/>
<tagB/>
</tag>
Attempting to bind to this in JiBX will cause an error. The easy way to fix this (from the JiBX developers) is to create a wrapping tag like below:
<wrappingtag>
<tag>
<tagA/>
<tagB/>
</tag>
<tag>
<tagA/>
<tagB/>
</tag>
</wrappingtag>
Since it's not an option to change the Acord specification, I had to find a workaround here. Basically I needed to write a custom marshaller and unmarshaller for these tags. I'll post my solution in a follow up blog entry. I also ran into a known bug with JiBX around optionally populated structure elements. So in JiBX a structure is just a grouping of tags, or an aggregate. So in the above example, <tag/> could be a structure or <wrappingtag/>. JiBX has logic built in that will not populate a tag if there is no value for the field associated with that tag. So in the above example, if the Java variable associated with tagA was null, tagA would not appear in the output XML. This does not work properly with structure attributes. Where this caused me problems is with the Acord codes. So above I stated that Acord codes correspond to an attribute and a tag value. This requires a structure in JiBX. So in the event that there isn't a value for that Acord code, it would still output an empty tag for that Acord code. To get around this, I had to create what JiBX calls a "test-method" that returns true or false as to whether or not it should output that tag. This wasn't a huge deal, but it was quite a bit of extra work when there is ten fields that require it.
Conclusion
I ended up continuing to use JiBX despite the pains of using it. The marks against it weren't compelling enough for me to rework what I had already done in JiBX. I think that once we are able to move to JDK 1.5, perhaps the XML parsing options should be resurveyed. I will however definitely give quite a bit of thought to whether or not to use JiBX the next time around.
Posted at 09:58AM Feb 18, 2008 by Ryan Senior in Programming | Comments[0]
Build Automation - Too Easy to Pass Up
This week I setup continuous builds and manual click-to-build sort of process using Luntbuild and Ant. I was really surprised at how easy it is to use and maintain. Then I began wondering why these automated build systems aren't more popular. Most places that I have worked at did not have automated, "too easy to mess up" sort of builds. I think that it might be the perception of the tool. Maybe people think it is much harder to get up and running than it actually is? Below is information on what I did.
Luntbuild Conceptually
Luntbuild (http://luntbuild.javaforge.com/) is build automation software that is open source. Through Luntbuild you can setup continuous builds (i.e. build every 5 minutes if there is new code checked in), manual builds (click a button and it creates an EAR) and most things that are related to creating that build. Luntbuild is a Java based application that can be deployed on a normal application server and can communicate with many different source control systems. Many of the details that would otherwise be manual process or messy Ant tasks like checking code out of repositories, is handled by the software.
The Setup
Luntbuild handles all of the previously manual process that was in place before. I have it configured to pull the code from the repository (Subversion in my case). For this particular scenario, it was reading from a branch. It can determine if there were any updates to the repository since the last time it built and then decide to run or not based on that. Next it compiles the source. This is the one part that I had to do any real work. Here I needed to create a minimal Ant build file that would compile the files and move the configuration files and libraries that the application needed etc. In Luntbuild I can define variables such as build number and location of files etc that are passed into the executing Ant script and used in the build file I'm creating. Then in Luntbuild I would specify where the build file was stored in the repository. What is important to note here is that I didn't upload the build file. It's stored in the repository just like everything else. This is good for several reasons. A big reason is that it will also allow me to build locally with the same Ant build file as is being used on the Luntbuild server. Another is that it will automatically get updated whenever a new build script is checked in, automatically. Once I have specified the build file, it's time to set up a schedule. I had two kinds of builds. One for continuous integration and another for release builds. For the CI build I had it check every 5 minutes for new code and build if it is there. As a part of both builds, version numbers have been set up. So I set the initial version number to 1.0.0. For each CI build or release build, that build number is automatically incremented. The release build was not made to be executed automatically, but when there is a release. This is not something that is known until some new feature is implemented or something similar. In that case, the developer would log in to Luntbuild and after a couple of clicks, would start the build process. It's important to note that this build is the exact same as the CI build, the only difference is the schedule and the configuration options of the build. Part of that configuration is tagging. For release builds, I wanted the ability to recompile that build at anytime. To do this I just pointed Luntbuild to the proper tags directory, configured it to enable tagging and it automatically creates a tag with the proper version number as its name whenever the release build succeeds.End Result
The end result here is saving a ton of time. For releases, with just a couple of clicks, the software will pull down the latest code, compile it, tag it and put it on the site for easy download. The benefits of Continuous Integration are great as well. Things like automated tests, style checking, complexity checking can be integrated in relatively easy. In my usage of this, we're still working towards style checking, automated unit testing etc, but a lot of benefits can still be gained from this minimal usage of Luntbuild. So that brings me back to my question. Why aren't more people using automated build software like Luntbuild? I was new to Luntbuild and far from an Ant expert and I was able to get the whole project automated and building in probably about a day. Probably the biggest time consumer of getting automated builds in an organization is the political time. Sometimes it can be difficult to change a release process, even if it is more efficient. People are often resistant to it and it is necessary to "sell the idea". It also requires an application server that can be used for the builds. To get that space on a server, it may require selling to even more people. That said, automated build software setup is definitely time well spent on any project.
Posted at 08:24AM Oct 15, 2007 by Ryan Senior in Programming | Comments[0]
Learn a New Language
In the Pragmatic Programmer, Andy and Dave suggest learning a new programming language every year. I definitely agree with this! I
think that it is very important for developers not to be stuck with just one language and another way to keep current. Specifically it is
important to learn languages that are different than the one you're currently programming in. There is still benefit to learning similar
languages, like C++ if your a Java developer. But I think that there is more to be gained by learning languages completely different from
what you're currently using.
It's the little things
There are little things that make languages different that helps you to appreciate it. Working in the land of Java gets us stuck ina curly brace and semi-colon mindset. Is there another way? Of course! Ruby uses the newline as the end of a statement (or you
can use a semi-colon if you want), Squeak uses a period (yes just like your English sentences), OCaml uses nothing or a single or
double semicolon, depending on the context and the list goes on. What about when you want to test out something small in your
language? Like, for example, what's faster for a given situation, a Linked List or an Array List? In Java, I write a new class with
a main method, compile it and then run it. Some languages have a nice runtime read-eval-print-loop (REPL) program that allows you to
enter your code in a continually evaluated loop. It's like a minimal programming environment. I just pull up irb in Ruby, type
in my code and hit enter. It interprets it and spits out the result. In Squeak, that REPL environment is integrated into
everything in the IDE, so all you have to do is highlight code anywhere in the environment and right-click, then click to run it.
Is it a killer feature of the language? Maybe not, but it's one of those little things that is nice to have.
It's the big things
When you learn a different language, if you've chosen a very different one, there are conceptual differences that could helpchange the way you think. An example from C++ to Java is garbage collection. From Java to Ruby or Smalltalk it's the lack of
typing. From Java to ML it's the type inference. These things are very significant when switching a language. These things can
change the way that you think. Who knows, maybe even the cool killer feature of that language will be put into the language
you're working in down the road (closures, generics, dynamic language support etc). Other things outside of the syntax of the
language is how it is developed. Eclipse is nice, but Squeak has some really advanced IDE tools that are integrated into the
language. With Perl you need to dust off the text editor.
Learning a new language doesn't always have immediate practical value. In fact, many of the "mainstream" languages are pretty similar. For
example C, C++, Java and C# all have similar syntax. Sure, Java has reflection, C++ has templates, there are benefits to learning similar
language, but there are more benefits to learning ones with very significant differences. Ruby has become more popular lately and is
different from the C based language to make it worth-while. There are two reasons to learn a new language, one is for a very tangible result
like: Java is very popular right now and I need a job, therefore I'm going to learn Java. The other is to improve your skills as a
developer, just like you would read a book like The Pragmatic Programmer or Mythical Man Month or a patterns book. None of those
will get you a job, but they will help improve your skills in writing and supporting code, which will get you a job.
Posted at 09:01AM Oct 04, 2007 by Ryan Senior in Programming | Comments[0]
Ruby Unit Test Framework
I have been writting quite a few unit tests in Ruby using the WATIR
(Web App Testing in Ruby) framework. On the whole I like WATIR and
using the Ruby unit test framework, however I did run into some odd
behavior. The WATIR framework is basically a set of libraries for
Ruby that provide hooks into Internet Exporer. Through these hooks
you can do things like go to a web page, insert text into a field,
click a button etc. WATIR in and of itself doesn't test anything.
WATIR just puts text somewhere on a page, visits a link etc. For
the actual testing part, a unit test framework is used. I just used
the unit testing framework that comes standard with Ruby. This is
very similar to the rest of the unit test frameworks in the xUnit
family, they use similar terminology, concepts etc. So there's a
TestCase class that contains the tests (having method names prefaced
with "test"), a TestSuite class that can contain multiple TestCases
etc.
Refactoring Commonality
In writing a lot of these WATIR and based tests, I found that there
were quite a few common things. Things like insert a certain
string into a certain text field, click a particular link etc.
Knowing this, I created lots of small methods with common actions.
The methods were pretty small an looked something like:
def assert_text_field_blank(field_name)
assert(text_field(field_name).getContents.length == 0)
end
def set_text_field(id, value_to_set)
text_field(id).set(value_to_set)
end
I approached this how I would in Java and created a common
superclass. So I had a BaseTest class that extended
Test::Unit::TestCase and then all my test classes extended that
BaseTest class.
Getting in the Ruby Mindset
Originally I set up a TestSuite with the 10 or 15 TestCases that I
wanted to run. While running in the TestSuite, all of my TestCases
worked properly, everything passed. However, as I started to add
more tests, I quickly ran out of patience running all of the tests
all of the time. I just wanted to run one TestCase. What I was
surprised to find was that when I ran my tests individually, I
encountered an error that was not there when I ran the test as a
suite. As a minimal example of this problem, below is the code to
recreate the problem:
require 'test/unit'
class BaseTest < Test::Unit::TestCase
def common_method
puts "does nothing\n"
end
end
class RealTest < BaseTest
def test_my_test
common_method
assert(true)
end
end
Running the above code will result in the following error:
default_test(BaseTest) [c:/dev/TestClasses.rb:10]:
No tests were specified.
After getting this error, I started to look into how these tests
were being ran. The above code doesn't contain anything that
references a TestRunner, or anything else to run the test.
Normally, if I were creating a TestSuite, I'd have code that
looked like:
Test::Unit::UI::Console::TestRunner.run(MyTestSuite)
Since the above code did not have a test runner specified, Ruby will
create it's own, including in it the current TestCase. When I figured
out that this was happening, I started looking into the Ruby source code to
figure out how it was determining which TestCases to run.
Object Space
In looking into how it determines which TestCase(s) to include in
the auto generated TestSuite, I found that it was referencing an
ObjectSpace class. I hadn't seen this before and upon looking into
it further, I found that ObjectSpace is a reference to all of the
objects in the running Ruby system. So in my case, the unit
testing code was iterating through all of the objects in the
running system, attempting to determine which ones were subclasses
of TestCase. If the class was a subclass of TestCase, it was added
to a TestSuite to be executed later. In most cases this is ideal
an saves code by not having to added the TestRunner code. In my
case it had some undesired behavior. As it was iterating through
all of the objects in the running system, it found an instance of
the BaseTest class and an instance of the RealTest class. Both
were subclasses of TestCase and both were included in the TestSuite
to be executed. Now in looking at BaseTest, it's just used to
house common functionality, but the Ruby code simply looks for all
subclasses of TestCase (which both tests are) and then adds them to
the test suite, not taking into account subclasses of classes that
extend TestCase. Then when it attempts to execute the BaseTest
tests, there are no tests found and so the test fails.
Solution to the Problem
I fixed this problem by changing the BaseTest class. Rather than
having BaseTest subclass the TestCase class, I instead added the
methods to the TestCase class. In Ruby, it's possible to redefine
and add methods to existing classes in the system. In my example,
I just added the common methods I wrote to the TestCase class and
then had RealTest just extend the TestCase class. The code looks
like:
require 'test/unit'
class Test::Unit::TestCase
def common_method
puts "does nothing\n"
end
end
class RealTest < Test::Unit::TestCase
def test_my_test
common_method
assert(true)
end
end
What is happening here is that our BaseTest class has now changed
to just add a method to the TestCase class in the Test::Unit
library. Now, whenever this Ruby file is loaded, the TestCase
class will incorporate the new methods that I have added and make
them available to the rest of the system. This approach allows me
to use all of the common methods that I created and also allows the
execution of the tests individually without having to add the test
runner code to each of the test cases.
Posted at 09:44AM Aug 14, 2007 by Ryan Senior in Programming | Comments[2]
Closures - Part 2
Blocks in Action
So in the previous post, I described what blocks are about why they're useful. On this post, I'll show so examples of using blocks. Blocks are extremely useful when dealing with collections (as discussed earlier). Smalltalk has a very rich Collections API related to this. Let's take a very common example. Let's say we have a collection of Person objects, and we want to retrieve all of the last names associated to those Person objects. In Java, this would typically be handled with a for loop:
List<String> lastNames = new ArrayList<String>();
for(Person person : listOfPersons){
lastNames.add(person.getLastName());
}
Now to use the Jakarta Commons Collections API, this can be broken down a little:
Collection lastNames = CollectionUtils.collect(person,
TransformerUtils.invokerTransformer("getLastName"));
Similar functionality in Smalltalk would be:
lastNames := listOfPersons collect: [:each | each lastName].
Many methods in the Collections API exist for code I write by hand in Java daily. For example, let's expand the Person example to have gender and maybe we want to select the males from the list. In Java we would write:
List<Person> males = new ArrayList<Person>();
for(Person p : persons){
if(p.isMale()){
males.add(p);
}
}
The same functionality could be achieved with the select: method
males := persons select: [:each | each isMale].
It is also possible to have two parameters to a block. Lets say that we want to add up the ages of all of the Person objects. In Java we could write the below code:
public int calculateTotalAge(List<Person> people){
int total = 0;
for(Person p : people){
total += p.getAge();
}
return total;
}
With Smalltalk, we can just the inject: into: method on all instances of Collection. Below is an example of the code.
totalAge := people inject: 0 into: [:runningTotal :each | runningTotal + each age].
The above statement is a little more complex than the previous ones, so I'll bread it down a little bit. The people collection is equivalent to a List in Java. I'm calling the inject: into: method. This method takes two parameters, one that indicates the initial value for the running total. In our case, we're adding up all of the ages, so the initial value is 0. The second parameter is the block to execute for each of the elements in the List. The block listed has two inputs rather than just one. The first input is the variable to use to store the value of each of the executions of the block and each is just like when we used it previously. So runningTotal + each age is executed for each element in people and the result of that is added to runningTotal (just like in the Java program).
An important point to make is that Smalltalk blocks are just objects. They can be passed as method parameters, stored as instance variables, class variables etc just as any other object in the system. This point is useful in certain situations, especially when it comes to situations like databases and file streams where connections or streams need to be opened. In Java, code would be created like this:
try{
//open connection
//...
//do stuff, maybe call a method that does the stuff
}catch(Exception e){
}finally{
//close connection
}
Blocks can be used for more than just one liners, you can have significant code in there (beyond just adding things and returning things). So if the do stuff above was in a block, the block to be passed into the method:
executeAround: aBlock
"open connection code goes here"
aBlock value: databaseContextInfo.
"close connection code goes here"
More information on that can be found in Kent Beck's "Smalltalk Best Practice Patterns" book under the Execute Around Method pattern.
Posted at 07:30AM Apr 02, 2007 by Ryan Senior in Programming | Comments[0]