HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavaMinor

Showing object's data on GUI and HTML without accessors and mutable objects

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
withoutshowingobjectsaccessorsdataandobjectguimutablehtml

Problem

I have been searching for a solution to show data of class with three assumptions:

  • Class Employee is not responsible for showing itself



  • Class Employee does not expose its data with getters



  • Classes responsible for showing data are immutable



So I created Employee class with its View interface:

public final class Employee {

  private String name;
  private String salary;

  public interface View {
    View addData(String name, String value);
    void show();
  }

  public void showOn(View view) {
    view.addData("Name", name.toString())
        .addData("Salary", salary.toString())
        .show();
  }
}


And two classes to show it to user. HTML:

final class HtmlPage implements Employee.View {
  private final String content;

  public HtmlPage() {
    content = "";
  }

  public HtmlPage(String str) {
    content = str;
  }

  @Override
  public Employee.View addData(String name, String value) {
    String newData = "\t";
    newData += name;
    newData += "";
    newData += value;
    newData += "\n";

    HtmlPage updated = new HtmlPage(content + newData);
    return updated;
  }

  @Override
  public void show() {
    System.out.println("\n" + content + "");
  }
}


and GUI:

```
import javax.swing.*;
import java.awt.*;
class OutputWindow implements Employee.View {

private final JComponent panel;

public OutputWindow() {
this.panel = new JPanel();
}

public OutputWindow(JComponent panel) {
this.panel = panel;
}

@Override
public Employee.View addData(String name, String value) {
panel.add(new JLabel(name));
panel.add(new JLabel(value));

return this;
}

@Override
public void show() {
JFrame outputFrame = new JFrame("Okienko do wypisania danych");
outputFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
outputFrame.getContentPane().add(this.getJComponent());
outputFrame.pack();
outputFrame.show();
}

JComponent getJComponent() {
panel.setLayout(new GridLayo

Solution

Your outset is wrong. The articles you've linked to present a nice story, but what you're running into here is the issue with that ideology:

When your data structures AND your views are rigid, you can't change the system to do anything else.

You're now stuck!

Say I had to show a listing of Employees. I can't! There is no way to use the data stored in Employee to make a screen that contains a listing of employees. I also cannot make a REST API with this Employee object without some serious magic - I'd have to make a "View" which takes multiple Employees and ignores calls to "show" unless they're from an authorized source.

You say "Class Employee is not responsible for showing itself".

public final class Employee {

  //...
  public void showOn(View view) {
    view.addData("Name", name.toString())
        .addData("Salary", salary.toString())
        .show();
  }
}


What's the show() doing here? Why does the Employee decide when the view is ready? Maybe I have a page consisting of a Project and the Employees that work on it. I'd have to make custom subviews.

You're right in that the Employee does not decide HOW it is shown, but it does decide WHAT is shown and WHEN it is shown. Which is not a desired property.

That, or I'd have to alter Employee to add View logic to support multiple or partial employee views. Which is bad, because standards constantly change, and you don't want to alter all your model classes to be able to give a view that can be used for that standard (e.g. something that you can convert to JSON).

Additionally, by limiting yourself to those constraints, you can no longer leverage certain libraries! It's fun and all to harp on the gigantic projects and code cruft that comes with using big libraries like Hibernate and Spring and such, but if you need to build a REST API and a phone app, being able to have libraries handle your object conversion to json and back (and in case of android, maybe even SHARE the model objects as a library!)... you can't do it in your setup! There are no getters, objects are immutable, libraries don't support this.

Employee.View is oddly specific class; it doesn't make any mention of Employee:

public interface View {
    View addData(String name, String value);
    void show();
  }


Let's say we had projects later - will there also be Project.View? What methods will it contain? The same? Mostly the same?

This leads to code duplication, which is really bad!

In your HTML page you use a table, and for that, String name, String value will do. But for non table situations, like a stackexchange profile, you have graphs for reputation, lists of answers and questions, it's partially sortable, some are numbers, others are text, some of the numbers are rounded (reputation is shown as ##.#k, rather than a 5 digit amount)...

All of that is either HARD or NOT POSSIBLE just because you're following the dogma of "You should never have Getters/Setters near your code.".

Such an idea is DANGEROUS in this case!

But you link to more than just 1 article of this guy, so maybe you really like what he says and I won't get through to you by just pointing at your code. Let's point at his:

Dog dog = new Dog();
dog.setBall(new Ball());


You could say this can be improved; Dog can have 0 to many objects to play with. Maybe it's carrying up to one object at a time. You could either use setCarriedObject (but that's secretly setBall), or, perhaps better, take(PlayObject). I call it PlayObject but basically anything that you could give to the dog to carry around would work. We're not using a very strict object hierarchy here.

Then, of course, if we're gonna play fetch, we'd have to retrieve the ball.

Dog dog = new Dog();
Ball ball = dog.getBall();


Because we've rephrased setBall to take (the dog takes the ball... because you've ordered him to), getBall is obviously going to become give.

But they're still getters and setters, we just call them differently now. They do the same.

The author comes to the same conclusion:

Dog dog = new Dog();
dog.take(new Ball());
Ball ball = dog.give();


So far, I get the point; getters and setters show that you might not have thought about the usecases and are treating your objects as dumb things, and maybe you want to emphasize that there's actual work being done.

But then the author says this:


Now, we're treating the dog as a real animal, who can take a ball from us and can give it back, when we ask. Worth mentioning is that the dog can't give NULL back. Dogs simply don't know what NULL is :) Object thinking immediately eliminates NULL references from your code.

How?!

If I do this:

Ball ballInMyHand = new Ball();
Dog dog = new Dog();
Ball otherBall = dog.give();
dog.take(ballInMyHand);


What's otherBall gonna be? The author says it's not going to be null. In that case, the options we've got left are these:

  • Null-representative value; the dog gives you "a

Code Snippets

public final class Employee {

  //...
  public void showOn(View view) {
    view.addData("Name", name.toString())
        .addData("Salary", salary.toString())
        .show();
  }
}
public interface View {
    View addData(String name, String value);
    void show();
  }
Dog dog = new Dog();
dog.setBall(new Ball());
Dog dog = new Dog();
Ball ball = dog.getBall();
Dog dog = new Dog();
dog.take(new Ball());
Ball ball = dog.give();

Context

StackExchange Code Review Q#122607, answer score: 7

Revisions (0)

No revisions yet.