Printers are even worse than getters

The well-known disadvantages of getters have already been analyzed by a number of programmers. Yegor Bugayenko has gone even further and suggested a workaround. The pattern called Printer gives indirect access to the internal parts of an object by serializing it into one of the data formats, such XML or JSON and reading it’s content using an XML or a JSON reader. As a result, as Yegor states, the object stays intact and it’s encapsulation is not violated by getters. Although this approach might seem viable at first sight, I believe that it does not solve the fundamental problem of accessor methods.

Viktor Chernomyrdin - the author of the quote We wanted the best, you know the rest
Viktor Chernomyrdin: "We wanted the best, you know the rest" © aif.ru

Let’s start with an example that Yegor uses in his Printer article:

public class Book {
  private final long isbn = 123456;
  private final String title = "Object Thinking";
  public String toJSON() {
    return String.format(
      "{\"isbn\":\"%d\", \"title\":\"%s\"}",
      this.isbn, this.title
    );
  }
}

The Book object provides a JSON printer. However, what this method actually does is that it serializes the object into JSON data format.

The process of reading serialized data is straightforward:

final JsonReader reader = Json.createReader(
    new StringReader(
        book.toJSON()
    )
);
final long isbn = reader.getLong("isbn");
final String title = reader.getString("title");

My argument against the so-called printers is that they do not actually differ from the accessor methods. Does it matter, conceptually, that we access object’s data directly through getters or indirectly through JSON? I believe not.

The real problem is data extraction process itself. The questions we should ask here are: “Why do need to take away data from the Book? Why do we treat the Book as a data storage container? Why does the Book have no behavior or functionality?” After we answer these questions the Book object will no longer exist in its original form.

I have an alternative to the Printer technique. (I am using jcabi-jdbc and jcabi-aspects libraries)

public interface Book {
    String title();
    long isbn();
}
public class BookById implements Book {

	private final DataSource database;
	private final int id;

	public BookById(final int id) {
	    this.id = id;
	}

	public String title() {
	    return this.resultSet().getString("title");
	}

	public long isbn() {
	    return this.resultSet().getLong("isbn");
	}

	@Cacheable(forever = true)
	private ResultSet resultSet() {
	    return new JdbcSession(
	        database.value()
	    ).sql(
            DSL.select(
                DSL.field("isbn"), DSL.field("title")
            ).from(
                DSL.table("books")
            ).where(
                DSL.field("id").equal(this.id)
            ).toString()
        ).select(
            (rset, stmt) -> {
                if (rset.next()) {
                    return rset;
                }
                throw new IllegalStateException(
                	String.format(
                		"Book with id %d not found!",
                		this.id
                	)
                );
            }
        );
    }

}
final Book book = new BookById(1);
String title = book.title();
long isbn = book.isbn();

The BookById object is an entity that encapsulates specific behavior. The object design is clear and transparent: all logic is kept inside the object, and data is not extracted through getters or serialization. The process of retrieving results from the object is as simple as calling one method. A true object is a functionality provider that exposes its behavior through a contract (interface), not a data container with getters or an external microservice that sends you JSON or XML strings.