Sponsored Link •
|
Summary
To what extent does the mindset encouraged by a language and its surrounding culture influence people's perceived productivity when they use that language? In this weblog post, I take a look at this question in the context of the static versus dynamic typing debate.
Advertisement
|
There has been much debate concerning the relative merits of static and dynamic languages. Dynamic language enthusiasts feel they are far more productive when using languages such as Smalltalk, Python, and Ruby compared to static languages like Java or C++. Static language enthusiasts feel that although dynamic languages are great for quickly building a small prototype, languages such as Java and C++ are better suited to the job of building large, robust systems. I recently noticed that in my own day-to-day programming with Java and Python, I approach programming with a different mindset depending upon the language I'm using. I began to wonder to what extent the mindset encouraged by a language and its surrounding culture influences people's perceived productivity when they use that language.
A year ago at Artima I had a large number of JSPs that weren't organized in an model-view-controller (MVC) architecture. In these JSPs, the business logic was mixed with logic responsible for generating the HTML view. I wanted to start making incremental improvements to these JSPs by refactoring each JSP into an MVC architecture whenever I needed to make a change to that JSP. I didn't want to change any existing URLs, and wasn't pleased with the frameworks I investigated such as Struts and WebWork, so I adopted an interim approach I called "poor man's struts."
For each JSP I refactored to the poor man's struts architecture, I created a Page
class (the controller)
with a static process
method. I moved the business logic from the JSP into
the process
method, and called the process
method from
the top of the JSP, like this:
// Imagine this is the top of a JSP,... ExamplePage.Context context = ExamplePage.process(request, response, session);
After executing the business logic, the process
method returned a context object filled with objects
that the remainder of the JSP needed to render the page. Because the business logic often included explicit redirects, I decided to
let the context object indicate whether or not the process method had already redirected
the client. If the so, the JSP simply returned:
// Imagine this is the top of a JSP,... ExamplePage.Context context = ExamplePage.process(request, response, session); if (context.isRedirected()) { return; }The remainder of the JSP, the view portion, usually depended on variables that were declared in the business logic portion. Now that the business logic had been moved to the
process
method, I needed to redeclare those variables.
Therefore, I declared the missing variables next and initialized them with values and objects extracted from the context object, like this:
// Imagine this is the top of a JSP,... ExamplePage.Context context = ExamplePage.process(request, response, session); if (context.isRedirected()) { return; } long forumID = context.getForumID(); boolean reply = context.isReply(); String subject = context.getSubject(); String body = context.getBody();
Context
Classesprocess
method to the JSP. In many web MVC frameworks, this
information is moved by placing the information in a context object, whose sole
purpose in life is to move the information from the controller to whatever entity is rendering the view. Similarly, in the case of poor man's struts, the process
method populates a context object and returns it to the JSP. Because all poor man's struts context objects would need to indicate to the JSP whether or not the process
method had performed a redirect, I created ControllerContext
, a superclass extended by all context classes:
public class ControllerContext { private boolean redirected; public ControllerContext(boolean redirected) { this.redirected = redirected; } public boolean isRedirected() { return redirected; } }
For each Page
class, I created a nested class called Context
.
The constructor of this class accepted a boolean redirected
flag
and a parameter for each value or object needed by the JSP. Here's an example:
// Declared inside the ExamplePage controller class public static class Context extends ControllerContext { private long forumID; private boolean reply; private String subject; private String body; public Context(boolean redirected, long forumID, boolean reply, String subject, String body) { super(redirected); if (forumID < 0) { throw new IllegalArgumentException(); } if (subject == null || body == null) { throw new NullPointerException(); } this.forumID = forumID; this.reply = reply; this.subject = subject; this.body = body; } public long getForumID() { return forumID; } public boolean isReply() { return reply; } public String getSubject() { return subject; } public String getBody() { return body; } }
The context object is a vehicle that enables the process
method to return multiple values to the JSP. In several Java MVC frameworks, the context object is essentially a Map
. The controller places objects into the context Map
identified by named keys. Had I taken this approach in poor man's struts, for example, the process
method of ExamplePage
could have placed the subject string "A Tale of Two Cities"
into the Map
with the key "subject"
. I rejected this approach primarily because I felt creating a specific Context
class for each controller would allow me to employ the type system to enforce constraints. The ControllerContext
superclass, for example, enforces that all context objects supply a boolean redirected
value. The ExamplePage.Context
subclass enforces that ExamplePage.process
always provides a non-negative forumID
. My theory was that such constraint checking would help me achieve robust code.
In practice, however, I found I was always in a hurry when I refactored a JSP to poor man's struts, because I only performed this kind of refactoring when I otherwise had some enhancement or bug fix to make to the JSP. I also found that
this approach to the context object required a lot of code. To speed up the process,
I wrote a Python script that, given a simple list of types and variable names, generated much of the needed Java code. The Python script did not generate any validation of input parameters in the Context
constructor. I
needed to write those by hand, and I discovered that I rarely felt it worth the time to
do so.
Given this experience, I ultimately decided that the added type safety wasn't really worth
the extra effort it required in this situation. In our new MVC architecture, the controllers return essentially a Map
context, and we enforce constraints with unit tests. The wierd thing is that I realized that if I had been designing poor man's struts in Python, I probably wouldn't have thought twice about just returning all that information in a tuple, as in:
(redirected, forumID, reply, subject, body) = ExamplePage.process(req, resp, session)
From a safety perspective, a tuple seems even more error prone than a Map
, because I have to get the order correct on both sides, not just the names.
If the thirteenth element in the tuple is a message ID, then I have to make sure the thirteenth element in both the return and assignment tuples is messageID
.
While order is generally random, names of keys and variables are usually similar, such as:
String subject = context.get("subject");In Java, the multi-valued approach equivalent to Python's tuple would be toss everything into an array of
Object
and pull it out by index on the
other side. I would never do that in Java, but I don't hesitate to do it in
Python. Why?
One reason is that Python supports this multi-valued return technique better in its syntax than Java, but I realized that the availability of syntax isn't the main reason I do it in Python. The main reason is that's the way it's usually done in Python. By contrast, that's not the way it's usually done in Java. The cultures are different. The culture that guided the design of a language influences my attitudes and mindset when I use the language. The Java culture encourages me to build solid bricks that can be combined to build large, dependable systems. The Python culture encourages me to glide smoothly and quickly to a solution.
This realization got me wondering, to what extent is this perceived increase in productivity with languages such as Python, Ruby, and Smalltalk due to the culture of those communities, and the mindset that the culture engenders in programmers, versus the actual languages themselves? Do you find yourself performing acts with reckless abandon in one language that would make you feel guilty to do in another? How easily can you switch mindsets when you switch languages? Do you find yourself fighting the mindset in one language, and feeling at home in another? What is the real source of the differences in the static versus dynamic language debate?
Have an opinion? Readers have already posted 22 comments about this weblog entry. Why not add yours?
If you'd like to be notified whenever Bill Venners adds a new entry to his weblog, subscribe to his RSS feed.
Bill Venners is president of Artima, Inc., publisher of Artima Developer (www.artima.com). He is author of the book, Inside the Java Virtual Machine, a programmer-oriented survey of the Java platform's architecture and internals. His popular columns in JavaWorld magazine covered Java internals, object-oriented design, and Jini. Active in the Jini Community since its inception, Bill led the Jini Community's ServiceUI project, whose ServiceUI API became the de facto standard way to associate user interfaces to Jini services. Bill is also the lead developer and designer of ScalaTest, an open source testing tool for Scala and Java developers, and coauthor with Martin Odersky and Lex Spoon of the book, Programming in Scala. |
Sponsored Links
|