4 Replies Latest reply on Dec 18, 2012 10:58 AM by Andipa

    Useful design patterns to reduce risk of "bound value cannot be set" error

    Andipa
      The property binding approach of JavaFX is very powerful, but it brings forward a design problem in complex applications that I have no clear idea how to properly tackle.
      If someone calls the setter while the property is bound, a RuntimeException saying "A bound value cannot be set" is thrown.
      That makes sense, as it tells the caller that he (or someone else) is doing something stupid there potentially threatening other functionality.

      However, a runtime exception is quite nasty and in distributed development teams and complex projects there is a risk someone adds a set or a bind call that would crash the application in a certain use case that was not properly covered by unit tests.

      Are you using certain design patterns or coding guidelines to ensure this doesn't happen?

      I could think of adding a call of unbind() to every setter method or requiring the call of unbind() before calling a setter in the coding guidelines.
      Another approach might be to remove the setter altogether or discourage people from using it and bind to a constant anonymous property instead.

      Any ideas? Maybe I'm also missing out on something vital, please enlighten me if that is the case.

      Edited by: Andipa on 14.12.2012 06:07
        • 1. Re: Useful design patterns to reduce risk of "bound value cannot be set" error
          David Grieve-Oracle
          Override the set method to check isBound().
              private DoubleProperty xyzzy = new DoublePropertyBase(0d) {
                  
                  @Override public void set(double newValue) {
                      if (isBound() == false) {
                          super.set(newValue);
                      }
                  }
                  
                  @Override public Object getBean() {
                      return MyClass.this;
                  }
                  
                  @Override public String getString() {
                      return "xyzzy";
                  }
              };
          1 person found this helpful
          • 2. Re: Useful design patterns to reduce risk of "bound value cannot be set" error
            James_D
            Interesting and important question, and I certainly don't have a definitive answer.

            One idea I have is that you really want to avoid exposing a property as writable if it's bound. There's a nice article on the OTN which talks about using JavaFX in a enterprise (specifically distributed) environment here: http://www.oracle.com/technetwork/articles/java/enterprisefxpt2-1676394.html One of the things he does really nicely in that is to create reusable "components" which are encapsulated in classes not extending any JavaFX classes. These expose only what's needed, using a view() method to expose the node to be added to the scene graph and the usual methods for any properties. His Browser class (first thing in the link) is a good example.

            If you break your project into pieces such as this, you can then only expose properties you need. If the properties are bound (or may be bound), you can expose them as read only properties (remember that ObjectProperty<T> is a subclass of ReadOnlyObjectProperty<T>), and this prevents the possibility of calling set(...) on a bound property.

            Here's a completely contrived example. It's a "clearable text field" which comprises a text field and a clear button. The view() method returns the HBox which contains them. There's a textProperty exposed, which is the text in the text field, and this is exposed as a writable property. The button's disable property is bound so that it is enabled if and only if the text field is non-empty. For no apparent reason, I choose to expose the button's disable property, but since it's bound I'll expose it as a ReadOnlyBooleanProperty to avoid the possibility of calling set(...) on a bound property.
            import javafx.beans.property.ReadOnlyBooleanProperty;
            import javafx.beans.property.StringProperty;
            import javafx.event.ActionEvent;
            import javafx.event.EventHandler;
            import javafx.scene.control.Button;
            import javafx.scene.control.TextField;
            import javafx.scene.layout.HBox;
            import javafx.scene.layout.Region;
            
            
            public class ClearableTextField {
              private final TextField textField ;
              private final Button clearButton ;
              private final HBox hbox ;
              public ClearableTextField(String text) {
                textField = new TextField(text);
                clearButton = new Button("Clear");
                clearButton.setOnAction(new EventHandler<ActionEvent>() {
                  @Override
                  public void handle(ActionEvent event) {
                    textField.setText("");
                  }
                });
                hbox = new HBox();
                hbox.getChildren().addAll(textField, clearButton);
                
                // bind the disable property of the clear button so that it's only enabled if the text field has content:
                clearButton.disableProperty().bind(textField.textProperty().isEqualTo(""));
              }
              public Region view() {
                return hbox ;
              }
              
              // expose the text property of the textField
              public StringProperty textProperty() {
                return textField.textProperty();
              }
              public String getText() {
                return textProperty().get();
              }
              public void setText(String text) {
                textProperty().set(text);
              }
              
              // Contrived example:
              // Expose the disableProperty of the button.
              // Since this property is bound, expose it as a read only property:
              public ReadOnlyBooleanProperty buttonDisableProperty() {
                return clearButton.disableProperty() ;
              }
              public boolean isButtonDisable() {
                return buttonDisableProperty().get();
              }
            }
            Here's an equally contrived use of my highly contrived example, where I display the "clearable text field" and a couple of labels; one bound to the text in the text field and one bound to the button's disable property. Note the text can be set, but not the disable property.
            import javafx.application.Application;
            import javafx.beans.binding.StringBinding;
            import javafx.scene.Scene;
            import javafx.scene.control.Label;
            import javafx.scene.layout.VBox;
            import javafx.stage.Stage;
            
            public class ClearableTextFieldTest extends Application {
            
              @Override
              public void start(Stage primaryStage) {
                final VBox root = new VBox();
                final ClearableTextField textField = new ClearableTextField("Some text");
                final Label textLabel = new Label();
                final Label disableLabel = new Label();
                textLabel.textProperty().bind(textField.textProperty());
                disableLabel.textProperty().bind(new StringBinding() {
                  {
                    super.bind(textField.buttonDisableProperty());
                  }
            
                  @Override
                  protected String computeValue() {
                    return textField.isButtonDisable() ? "Button disable" : "Button enable";
                  }
                });
                root.getChildren().addAll(textField.view(), textLabel, disableLabel);
                primaryStage.setScene(new Scene(root));
                primaryStage.show();
            
              }
            
              public static void main(String[] args) {
                launch(args);
              }
            }
            I'm not sure how applicable this is to a large-scale project; obviously you have to figure out the granularity of such classes. But it might give you an idea for some designs.
            1 person found this helpful
            • 3. Re: Useful design patterns to reduce risk of "bound value cannot be set" error
              Knut Arne Vedaa
              You could also consider using a bidirectional binding, then both properties' values can be set freely.
              1 person found this helpful
              • 4. Re: Useful design patterns to reduce risk of "bound value cannot be set" error
                Andipa
                Thanks for your valuable input. I really like the idea of using encapsulation for this purpose and the fact that e.g. ObjectProperty is a subtype of ReadOnlyObjectProperty. It does not completely prevent misuse, but if the classes are well-structured and concise, it becomes quite apparent that a property is not supposed to be set.

                Still there might be some properties that can either be bound or set to a constant value depending on the state of the application, in my case it's a switch between a view and an edit mode that is giving me a headache. I also like the idea of overriding the setter and checking isBound(). Not doing anything in case it's bound seems like a pitfall though. I would rather throw some sort of checked exception, unfortunately that would necessitate introducing a new method and encouraging its use instead of set().