Data-Centric Applications with Vaadin 8
上QQ阅读APP看书,第一时间看更新

Implementing support classes

The previous interface won't compile. There are two classes that need to be implemented: WorkingAreaComponent and MenuOption. The addWorkingAreaComponent(WorkingAreaComponent) method expects a WorkingAreaComponent that encapsulates a caption and the corresponding Vaadin component to be shown. This interface is defined as follows:

public interface ApplicationLayout extends Component {

public static class WorkingAreaComponent
implements Serializable {

private final String caption;
private final Component component;

public WorkingAreaComponent(String caption,
Component component) {
this.caption = caption;
this.component = component;
}

... hashCode(), equals(), and getters
}
...
}
The WorkingAreaComponent class implements Serializable . Vaadin is mostly a server-side framework. Components are stored in HTTP sessions. In order to serialize the session, all contained objects must be Serializable . This serialization is done, for example, when you stop a web container such as Jetty or Tomcat. All HTTP sessions are serialized to the disk, and the next time the server starts, sessions are restored. Notice also how SerializableConsumer was used in the ApplicationLayout interface for the same reasons.

Why is that needed? Why not simply let the addWorkingAreaComponent(WorkingAreaComponent) method have the parameters for the caption and component, as shown in the following snippet of code?

void addWorkingAreaComponent(String caption, Component component);

If you were a hundred percent sure that a caption and a component are the only things you need to have when you add a new component to the working area, that would be fine. However, you don't know how concrete ApplicationLayouts will evolve. What if an icon is needed? What about a color or a help text?

Suppose you have decided to implement the method as addWorkingAreaComponent(String, Component) and some months after the component is released, some application that uses the component needs to have an icon for each component added into the working area. A possible solution is to modify the method to accept a new parameter for the icon, as follows:

void addWorkingAreaComponent(String caption, Component component,
Resource icon);

This modification will break any existing client that references the old method's signature. Another approach is to overload the method by adding a new parameter. However, this will break all current implementations of ApplicationLayout. Encapsulating what is subject to change is always a good idea.

Another reason to encapsulate the parameters of addWorkingAreaComponent(WorkingAreaComponent) is the getWorkingAreaComponents() method. Suppose you want to implement a concrete ApplicationLayout that allows users to switch between tabs and windows. In order to implement this functionality, you need to get all the current components shown in the working area (using the getWorkingAreaComponents(WorkingAreaComponent) method) and place them in tabs or windows accordingly. For each component, you need to create a tab or a window, set its caption, and add the corresponding Vaadin component. You need both, the caption and the component. Encapsulating these objects in a single class greatly simplifies this task; otherwise, we would need to have an extra method that returns the captions as an ordered collection. Additionally, the getWorkingAreaComponents() method should return an ordered collection as well.

The last thing to notice about the ApplicationLayout class is the addMenuOption(MenuOption, SerializableConsumer<MenuOption>) method. This method expects a MenuOption (that encapsulates the caption to render) and a SerializableConsumer that serves as a click listener for the menu option. When the user clicks the option, the Consumer.accept(MenuOption) method is called, passing the clicked MenuOption as its parameter.

SerializableConsumer is a serializable version of the Consumer class, a functional interface introduced in Java 8. A functional interface has only one abstract method. This allows clients to create instances of the interface using lambda expressions. For more information about functional interfaces see:
http://docs.oracle.com/javase/8/docs/api/java/lang/FunctionalInterface.html .

The MenuOption class can be implemented as follows:

public interface ApplicationLayout extends Component {

public static class MenuOption implements Serializable {
private final String caption;

public MenuOption(String caption) {
this.caption = caption;
}

public String getCaption() {
return caption;
}
}
}