Spoiler alert: This article is about Plain Old Groovy Objects. Since they're so intellectually simple, yet powerful, it's also the source of many POGO-related non sequiturs. You've been warned.
Deep in the mists of time when Java was shiny and new (circa 1996), Java added the “JavaBeans Specification” to the language. That was part of the migration from Java 1.00-A and 1.01 to the final JDK 1.1 APIs. If you're really, really bored (why else are you reading this blog post?), you can still find the change history online at http://www.oracle.com/technetwork/java/javase/documentation/spec-141065.html. If you go there, you'll notice that every mention of JavaBeans actually has a little ™ next to it because Microsoft got there first with COM for components, so you might as well get lawyers involved if you're forced to make a pun (Java beans, get it?).
The JavaBeans spec is where we first started referring to getters and setters (or, in the obscure parlance of object-oriented programming, accessors and mutators, as if they have been exposed to gamma radiation or something) using the naming convention we've adopted ever since.
public class MyBean { String name public void setName(String name) { this.name = name; } public String getName() { return name; } }
JavaBeans were really intended to be a component architecture, so that you could assemble them into a program just by wiring them together. The idea was to purchase a library of third-party beans from some commercial provider, import them into a visual editing environment, and wire them together through their properties (using the aforementioned getters and setters) and code without ever really coding, which has always been a software manager's dream.
Back in 1997 I even managed to convince my boss at the time to purchase the recommended editing environment, called Sun Java Workbench, for which I am profoundly sorry. Comparable IDEs at the time were Symantec's Visual Cafe, IBM's VisualAge for Java, and Borland's Java IDE. It's probably not a coincidence that not only do none of those IDEs exist anymore, neither do the companies that created them.
(Yeah, I said it! IBM and Symantec don’t exist anymore! Go ahead, prove me wrong!)
Sun Java Workbench was actually pretty cool. It had a pipe metaphor that you dragged your “beans” onto the visual page, and dragged pipes as lines from one bean's output into another bean's input. As I recall, the pipes could only go in rectangular directions, because, well, I have no idea, but it looked good.
I'd insert a picture here, but even Google's Image Search couldn't find one. It's that obscure.
Getting back to the JavaBeans specification, it gave us a few interesting innovations:
The spec also defined java.beans.PropertyVetoException, one of my favorite exceptions because it sounded like it was involved in some real estate deal that went bad.
(Some of my other favorite exceptions were TooManyListenersException, which hopefully doesn't happen during class, UnsupportedFlavorException, as in chocolate and vanilla are okay but strawberry is out, CloneNotSupportedException, which has all sorts of ethical quandaries, and the tragic MalformedURLException, please give generously. As a Connecticut resident, I can also assure you that the IllegalStateException refers to New York. My favorite subclass of java.lang.Error, btw, is the same as everybody else's: ThreadDeath.)
Needless to say, the whole “let's pretend to be Visual Basic” phase of Java didn't work out, and the JavaBeans specification faded into obscurity.
By 1998, nobody cared about poor JavaBeans any more, until suddenly everybody wanted to write web apps. (Let's just pretend the dalliance into applets never happened, okay? I was there. IT NEVER HAPPENED.)
When Sun first proposed an architecture for web apps, they suggested a variation of Model-View-Controller they called “Model 1″:
The JavaBeans at the time were largely a way of getting the controller code out of the view layer and into classes. Even when Sun gave up and adopted Model 2, where the controllers were suddenly servlets, the JavaBeans were still part of the Model.
The problem was, in a web app nobody cared about bound and constrained properties or property change listeners, vetoable or otherwise. JavaBeans were just dumb data structures with an occasional method to do business logic, needed because nobody had thought of services yet.
In other words, the JavaBeans everybody was using weren't really JavaBeans, and even pedants finally got tired of saying, “they're JavaBeans but not by the spec”, so another term was needed.
Thus was born the POJO.
According to the Wikipedia article (https://en.wikipedia.org/wiki/Plain_Old_Java_Object), the term POJO for Plain Old Java Object came from Martin Fowler, Rebecca Parsons, and Josh MacKenzie in September 2000.
The relevant quote is: “We wondered why people were so against using regular objects in their systems and concluded that it was because simple objects lacked a fancy name. So we gave them one, and it’s caught on very nicely.”
Go figure. POJOs are just Java classes, and since there's no specification, they can be defined however you want. In practice, though, they have getters and setters that follow the naming convention and pretty much nothing else. Here is one in all its glory:
import java.util.Date; public class JavaTask { private String name; private int priority; private Date startDate; private Date endDate; private boolean completed; public JavaTask() { // default constructor } public JavaTask(String name, int priority, Date start, Date end, boolean completed) { this.name = name; this.priority = priority; this.startDate = start; this.endDate = end; this.completed = completed; } public void setName(String name) { this.name = name; } public String getName() { return name; } public void setPriority(int priority) { this.priority = priority; } public int getPriority() { return priority; } public void setStartDate(Date start) { this.startDate = start; } public Date getStartDate() { return startDate; } public void setEndDate(Date end) { this.endDate = end; } public Date getEndDate() { return endDate; } public void setCompleted(boolean completed) { this.completed = completed; } public boolean isCompleted() { return completed; } @Override public String toString() { return "JavaTask{" + "name='" + name + '\'' + ", priority=" + priority + ", startDate=" + startDate + ", endDate=" + endDate + ", completed=" + completed + '}'; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; JavaTask javaTask = (JavaTask) o; if (completed != javaTask.completed) return false; if (priority != javaTask.priority) return false; if (endDate != null ? !endDate.equals(javaTask.endDate) : javaTask.endDate != null) return false; if (name != null ? !name.equals(javaTask.name) : javaTask.name != null) return false; if (startDate != null ? !startDate.equals(javaTask.startDate) : javaTask.startDate != null) return false; return true; } @Override public int hashCode() { int result = name != null ? name.hashCode() : 0; result = 31 * result + priority; result = 31 * result + (startDate != null ? startDate.hashCode() : 0); result = 31 * result + (endDate != null ? endDate.hashCode() : 0); result = 31 * result + (completed ? 1 : 0); return result; } }
That's nearly 100 lines of code just to wrap five attributes. If you're going to write all that code, even if it's generated by your IDE, you might as well write a test, too. Here's a test for the JavaTask class, implemented in Groovy.
class JavaTaskSpec extends spock.lang.Specification { Date now = new Date() JavaTask t = new JavaTask(name: 'name', priority: 3, startDate: now, endDate: now + 1, completed: true) def 'get and set name work'() { when: t.name = 'other' then: t.name == 'other' } def 'get and set priority work'() { when: t.priority = 4 then: t.priority == 4 } def 'get and set startDate work'() { when: t.startDate = new Date() + 1 then: (t.startDate - (new Date() + 1)) < 1 } def 'get and set endDate work'() { when: t.endDate = new Date() + 2 then: (t.endDate - (new Date() + 2)).abs() < 1 } def 'is and set completed work'() { when: t.completed = false then: !t.completed } def 'toString method works'() { given: String expected = "JavaTask{name='${t.name}', " + "priority=${t.priority}, startDate=$now, " + "endDate=${now + 1}, completed=${t.completed}}" expect: t.toString() == expected } def 'equals method is correct'() { given: JavaTask t1 = new JavaTask(name: t.name, priority: t.priority, startDate: t.startDate, endDate: t.endDate, completed: t.completed) expect: t == t1 } def 'hashCode method is correct'() { given: JavaTask t1 = new JavaTask(name: t.name, priority: t.priority, startDate: t.startDate, endDate: t.endDate, completed: t.completed) expect t.hashCode() == t1.hashCode() } }
This is a test written using the Spock testing framework (http://spockframework.org), which is based on ideas from JUnit, but is superior to JUnit in pretty much every way possible. The test has to be written in Groovy, but the class being tested can be in Groovy, Java, or any other JVM-based language.
Spock tests are readable even to people who don't know the framework. Each test method has a descriptive name surrounded by quotes. The body of the test contains blocks like given, expect, when, and then. Each statement in the then and expect blocks is evaluated according to the Groovy Truth, which means non-null references are true, non-zero numbers are true, non-empty collections are true, non-empty strings are true, the boolean value true is true, and so on. Spock is a logical (sorry) framework for enterprise (sorry again) testing.[1]
Spock allows you to test well and prosper (sorry yet again). It has been, and always shall be, your friendly testing framework (okay, no more, I'm done).
The point is, now I have a test, so now I am free to refactor my POJO into a POGO.
Try googling the word “pogo” some time. I figured all I'd find was something about pogo sticks, but boy was I in for a surprise. Here's just a sample:
The list goes on and on. I had no idea. Of course the one I mean is Plain Old Groovy Objects.
Since this post has already gone way beyond any reasonable expectations, let me just present the defining characteristics of POGOs:
Getters and Setters
Any attribute that is not declared public or private has auto-generated getters and setters.
Constructors
POGOs have a default, no-arg constructor, plus a “map-based” constructor that allows you to set attributes using a colon notation.
@Canonical
The @Canonical Abstract Syntax Tree (AST) transformation, when applied to a POGO[4], generates a toStringmethod, an equals method, a hashCode method, and a tuple (consecutive arguments) constructor based on the attributes of the class. Whoa. In other words, here's the complete POGO equivalent to the POJO given earlier:
import groovy.transform.Canonical @Canonical class GroovyTask { String name int priority Date startDate Date endDate boolean completed }
That's it. Seriously, that's the whole class. The same Spock test from before passes here[5].
Which would you rather write, the 100-line monstrosity from Java, or the 10-line (including the import statement) Groovy version? Sure, the IDE generated most of the Java code, but in Groovy all that code comes for free, and any code not present won't develop bugs later. The Groovy attitude is that if the IDE can do it, why can't the compiler? Even better, you can read this one, without swimming through all that generated code, and it works with the rest of your Java code simply by compiling it. So please consider using POGOs for all your mapped data.[6]
Hopefully this gives you an idea of what kind of productivity gains you can get by adding Groovy to your Java projects.
For more details, check out the book Making Java Groovy (http://manning.com/kousen). Oh, and Accelebrate offers training courses in Groovy (/training/groovy), Grails (/training/groovy-grails), and even Gradle (/training/gradle), all of which are awesome.
Respectfully submitted by Ken Kousen <[email protected]>, who teaches this stuff.
Written by Ken Kousen
We offer private, customized training for 3 or more people at your site or online.
Our live, instructor-led lectures are far more effective than pre-recorded classes
If your team is not 100% satisfied with your training, we do what's necessary to make it right
Whether you are at home or in the office, we make learning interactive and engaging
We accept check, ACH/EFT, major credit cards, and most purchase orders
Alabama
Birmingham
Huntsville
Montgomery
Alaska
Anchorage
Arizona
Phoenix
Tucson
Arkansas
Fayetteville
Little Rock
California
Los Angeles
Oakland
Orange County
Sacramento
San Diego
San Francisco
San Jose
Colorado
Boulder
Colorado Springs
Denver
Connecticut
Hartford
DC
Washington
Florida
Fort Lauderdale
Jacksonville
Miami
Orlando
Tampa
Georgia
Atlanta
Augusta
Savannah
Hawaii
Honolulu
Idaho
Boise
Illinois
Chicago
Indiana
Indianapolis
Iowa
Cedar Rapids
Des Moines
Kansas
Wichita
Kentucky
Lexington
Louisville
Louisiana
New Orleans
Maine
Portland
Maryland
Annapolis
Baltimore
Frederick
Hagerstown
Massachusetts
Boston
Cambridge
Springfield
Michigan
Ann Arbor
Detroit
Grand Rapids
Minnesota
Minneapolis
Saint Paul
Mississippi
Jackson
Missouri
Kansas City
St. Louis
Nebraska
Lincoln
Omaha
Nevada
Las Vegas
Reno
New Jersey
Princeton
New Mexico
Albuquerque
New York
Albany
Buffalo
New York City
White Plains
North Carolina
Charlotte
Durham
Raleigh
Ohio
Akron
Canton
Cincinnati
Cleveland
Columbus
Dayton
Oklahoma
Oklahoma City
Tulsa
Oregon
Portland
Pennsylvania
Philadelphia
Pittsburgh
Rhode Island
Providence
South Carolina
Charleston
Columbia
Greenville
Tennessee
Knoxville
Memphis
Nashville
Texas
Austin
Dallas
El Paso
Houston
San Antonio
Utah
Salt Lake City
Virginia
Alexandria
Arlington
Norfolk
Richmond
Washington
Seattle
Tacoma
West Virginia
Charleston
Wisconsin
Madison
Milwaukee
Alberta
Calgary
Edmonton
British Columbia
Vancouver
Manitoba
Winnipeg
Nova Scotia
Halifax
Ontario
Ottawa
Toronto
Quebec
Montreal
Puerto Rico
San Juan