Showing posts with label java. Show all posts
Showing posts with label java. Show all posts

3/13/2009

Careful, Java Programmers going to Ruby

Good evening everyone, I am a Java programmer.
It happened quickly, I didn't even notice.
One day I was merrily strncpy'ing and the next it was immutable.


We joke about languages of old while new shinny languages are paraded in front of us, but in the end they all teach us something and make us forget other things.

Today I came across the first serious change that the Java world thrust upon my weak mind. I am working a Ruby project, a great new shinny language which is lots of fun to learn and work with.

When working in Java I use the usual Agile development process; write your test, write some code and repeat. As I progress, my code, and indeed the API design, evolve into a nice usable, stable set of libraries.

Working in Java, with Eclipse, the simplest technique is to start by writing your test, reference a public method, press a key to create it and flush out the implementation. As you go, you will naturally refactor pieces of code into new methods to keep it simple.

Well, this is where things get a little too easy for Java developers in Eclipse. To refactor a block of code into a new method just select it, press a couple of keys and Eclipse creates the new method for you. A new private method in the same class.

Sounds good right? Well it is, except, that you have now learned to not thing about the scoping your your methods when you create them. Your first is always public or protected because your test is driving it and subsequent methods are private by default as created by the IDE. It's a beautiful system.

Now you switch to Ruby.

You write your test and implement your public method, you refactor with the cut/paste/rename/suffle and low and behold, because you have learned not to think about it, you have loads of public methods in your class.

The project I am working on takes it one step further and has tests for every method, examining all its nuances, mocking all its dependencies and going to town making the implementation as couple to the test as possible. It's easy to do, they are all public, what else is a programmer to do?

Unit testing philosophy does vary depending on who you talk to but my basic goal is to:

  • Test all public methods,

  • Test all protected methods,

  • If a private method is particularly complex make it protected and test it,

  • If I have loads of "untested" private methods I need to move some to new classes and make them public. I leave it to you to quantify "loads".

  • Trust your coverage tool to tell you the private code you never use and delete it.


So, starting tomorrow, I change my Ruby development process and consider all new methods private unless part of a test driven process. This will do lots of things to improve the final product:

  1. Simplify the test/code dependencies making the tests less brittle.

  2. Prevent call sequence bugs where you have to call one method before another or it all falls over. Too many publics will do that you know.

  3. Make the calling conventions exposed by the class easier to grok by new programmers reading the code.

  4. Improve Class API design which is one of the primary benefits of TDD, lost to the lazy public manic people like me.



Oh, yes, an stop putting java semicolons in Ruby code. That really upsets the Ruby people.

10/20/2008

Eclipse, bad UI change

I am sure that I am not the first to notice but Eclipse has gone through some changes that are causing a significant slow down in my productivity.

This has been the premiere IDE for many years, offering best of breed user interface design concepts to optimize the life of the Java developer.

I now find it increasingly hard to use for one simple fact. The changes have made it really hard to run unit tests.

One of the magical features was that ctrl-F11 would run the last test I ran so I could continue coding, in any source file, and press this vital key combination to see if I had broken anything yet.

Now, this keystroke, and the action that it is associated with, have been changed in two ways.

Firstly, if you have to be in the test file to reliably run the last test. It is inconsistent in this regard. Most of the time it just asks "How would you like to run ''? On the server or blah blah blah".

Secondly, if it does remember the test I want to run, it now also remembers that last time I ran it in the debugger. So each subsequent run promptly stops on a breakpoint.

This is a flow problem and it is possible that non-test driven developers use the debugger a great deal. I understand, even sympathize, but couldn't Eclipse support both flows? Especially the original, and in my opinion, the far superior. Just run my tests, that's all I ask.

Now if I could just get NetBeans to stop deleting my "build" directory when it creates a project I could say goodbye to this annoying change.

7/28/2008

Eclipse 3.4 to NetBeans 6.5 M1

It is time to get back into NetBeans. It used to be my primary IDE before Eclipse hit the scene and now with its excellent Ruby and now PHP support it seem appropriate to start re-learning how it works.

It is certainly faster now and the UI is cleaner than it used to be. Steps in the right direction.

This is where things went horribly wrong.

Import an eclipse java project into a netbeans java project. Netbeans decided that I didn't want my "build" directory anymore so replaced it with it's own. How assignign is that.

So, first hint, do not, import an eclipse project into a netbeans project if you have a build directory.

9/21/2007

Maven: Why does it have to be so hard?

Every project I start I try to use Maven. It seems like the right choice but I consistently fail. Every step is hard. It's not just that there are infrastructure hurdles, but the commands are hard, the POM is complex and nothing is easy.

This J bites.

Ok, enough whining. Maven is the right way to manage your build environment simply because it is the only tool that manages your dependencies. This is a simple concept.

We can argue that ant has sufficed for a long time and that is true. So, we can argue that the dependency complexities that we face are actually not that hard since we appear to be able to work it out without another tool, but wouldn't it be great if we could leverage the learning that others have already put into setting up the correct dependencies?

Well I am installing grails, an ant solution, and integrating CXF and Mule. I am facing continuous versioning problems with all the xml/soap/ws etc jars. I am sure that when I have solved this problem, I will promptly forget it and have to solve it all over again for the next project.

I would like to be able to capture the solution in a POM so that I could reuse it even if no one else does.

So, what is step one. Create a maven project. This is so hard. I can't believe they make us do this. No wonder this tool is taking so long to gain acceptance.

mvn archetype:create -DgroupId=com.company.project-DartifactId=project-name-DarchetypeArtifactId=maven-archetype-webapp


Yes, that is the command to create a project, and I didn't even specify all the other version options that they mention. All it does is create this:

./pom.xml
./src
./src/main
./src/main/resources
./src/main/webapp
./src/main/webapp/index.jsp
./src/main/webapp/WEB-INF
./src/main/webapp/WEB-INF/web.xml


It doesn't do much but it does allow us to enforce standards and, when we develope our own archetypes will improve project structure consistency accross the board. This is all good but why do I need to pass in system properties? Who ever heard of a command line interface that makes you declare system properties.

Perhaps it could prompt for the required fields like "grails create-app". Perhaps there should be a web page on the maven site that will help. I had to go through google to find it. Their quick start doesn't even talk about it.

I know, I know, I am whining. I could probably contribute to the project myself, it's open source and all that.

There is an Eclipse and NetBeans plugin that makes life a little easier.

There comees a point when we learn to love the new tool. I just pasted a glob of xml into my POM file because some CXF/Maven web page told me to, saved it, and the maven eclipse plugin promptly downloaded all the jars required to bring a CXF web service up and running. This took me an hour or so the first time. Maven just made my life easier.

...

And again I am dashed to the jagged rocks of the learning curve. It appears maven doesn't use the JAVA_HOME environment variable to determine the JDK version to use and it is defaulting to JDK 1.3 which is not even installed on my machine. I have annotations so it will not work anyway.

I suppose it makes sense because it is requiring explicit version declarations for all other jars so why not the rt.jar but it is not telling me how to specify it.

This brings up the odd balance in our lives of detail vs default. If I require everything specified I know my build will always be correct. If I allow default selections using JAVA_HOME for example, it may fail but will usually work.

This is why maven is so hard to deal with. Nothing is easy. Specify or die a bloody death and don't expect to block the gushin wound with documentation because even google is lost.

...

Ok, I found it, System Dependencies

...
Oh wait, that is wrong, doesn't work and I don't know what it does though it sounded so close. Infact, we want this Compiling-J2SE-5

Man that took another 30 minutes, I almost gave up on Maven again.

...
I might be back to loving maven again. Once you have run your "mvn package" to create your war file you can run "mvn tomcat:run" which will run tomcat in place from your generated war file. Now that is cool.

...
Well, while I can't expect maven to solve all my problems, since it's intent is to solve dependency problems I did have that expectation. Silly me.

The CXF xml glob that I pasted to get all the CXF jars and dependencies downloaded apparently missed the SAAJ jars. I expect I will have to find and add these to my project file manually.

I am starting to feel as though I might be starting to understand Maven, even if it isn't doing what I want.

12/28/2005

Java Closure Chaining

I am playing with closures in java. Not as simple as the Ruby counter parts but a benefit to our programming arsenal I think.

I have come to realize two new things from this experience.

Testability

Compare


Iterator iter = list.iterator();
while (iter.hasNext())
{
A a = (A)iter.next();
do something
}

With

ListTools.each(list, new ClosureObject()
{
public void call(Object a_object)
{
A a = (A)a_object;
do something
}
});

How many pre-bugs do you see in each version. Both have 4 lines of code but the first requires 3 dot references which adds opportunities for those methods to do something unexpected.

Because of the the "final" requirement in java I found this construct to be a rarity. Instead we need something like this.

ListItemMunger munger = new ListItemMunger();
ListTools.each(list, munger);
System.out.println(munger.something());

class ListItemMunger implements ClosureObject()
{
public void call(Object a_object)
{
A a = (A)a_object;
do something
}
});

The most significant benefit with using Closures in java is that the loop-core is removed and is more testable than ever before.

public void testLoopCore()
{
ListItemMunger munger = new ListItemMunger();
munger.call(new SomeObject());
munger.call(new SomeObject());
munger.call(new SomeObject());
assertTrue(munger.something());
}

Now that I have classes that scan directories recursively, scan the files within them and then the line of text within each file this testability feature has been invaluable.

Closure Chaining

So I can scan directories, files and lines within files. How do I do that with these closure things.

DirectoryRecurseLookup.each("my path", new ClosureFile()
{
public void call(File a_directory)
{
FileLookup.each(a_directory, new ClosureFile()
{
public void call(File a_file)
{
StreamLineLookup.each(a_file, new ClosureIntString()
{
public void call(int a_lineNumber, String a_line)
{
System.out.println(a_lineNumber + " : " + a_line);
}
}
}
}
}
});


Well that's a lot of nesting but relatively simple syntacticly, no pre-bugs waiting to pop out at me. My first version took each of the closures into a separate class to simplify the code and make it more testable and it all worked fine.

Then I realized what I was doing wrong. I am just chaining closures so why can't the closures encapsulate that.

StreamLineLookup lineScanner = new StreamLineLookup();
FileLookup fileScanner = new FileLookup(lineScanner);
DirectoryRecurseLookup dirScanner = new DirectoryRecurseLookup(fileScanner);

DirectoryRecurseLookup.each("my path", dirScanner);

So as an example:

class DirecotryRecurseLookup implements ClosureFile
{
public static each(String a_path, ClosureFile a_closure)
{
// find files and pass them to the closure
}

private ClosureFile m_closure;
public DirectoryRecurseLookup(ClosureFile a_closure)
{
m_closure = a_closure;
}

public void call(File a_directory)
{
each(a_directory, m_closure);
}
}