2
\$\begingroup\$

Is my use of Java Reflection an OK design?

Each domain object contains an annotation to declare what controller would be initialized to edit/create the object:

Each field of the domain object has an annotation to declare what control in the specified controller that the user would use to edit its value:

@Edit(controller = EmployeeController.class)
public class Employee extends Item {

     @Node(name = "lastNameField", type = TextField.class)
     private StringProperty lastName = new SimpleStringProperty();

     //Control similar to a TableView   
     @Node(name = "contacts", type = ListBox.class)
     private List <Contact> contacts;

}

public @interface Edit {
     Class<? extends ModifyEntityController> controller();
}

public @interface Node {
     String name();
     Class<?> type();
 }

 public Class EmployeeController {
         @FXML 
         private TextField lastNameField;
    }

Double-clicking on a ListCell would call .edit() on the domain object.

This method is in the parent class Item.


.edit() does: example = employee.edit();

  1. create an instance of the @Edit controller
  2. use @Node to create a binding between the specified control
  3. set the root node of controller to the main window of the application.

//create an instance of the controller
Edit edit = getClass().getAnnotation(Edit.class);
Class<? extends Controller> controller = edit.controller();

Object controllerInstance = null; 

try {
    controllerInstance = edit.controller().newInstance();
} catch (Exception e) {
    e.printStackTrace();
}


//create bindings between the specified controls
List <Field> itemFields = FieldUtils.getFieldsListWithAnnotation(getClass(), Node.class);

for (Field field : itemFields ) {
     Node node = field.getAnnotation(Node.class);
     Object itemObject = FieldUtils.readField(field, this, true);
     Object controllerControl = FieldUtils.readDeclaredField(controllerInstance, node.name(), true);

     if (node.type() == TextField.class) {
          TextInputControl tc = (TextInputControl) controllerControl;
          StringProperty sp = (StringProperty) itemObject;
          tc.textProperty().bindBidirectional(sp);
    }

     else if (node.type() == ListBox.class) {
          ListBox lb =  (ListBox) controllerControl;
          List <CellObject> list = (List<CellObject>) itemObject;
          lb.setList(FXCollections.observableArrayList(list));
    }

}

*The implementation of a ListBox is actually not a binding of properties but setting the value of its ListView to the List Field of the domain object.
All objects that would be the value of a ListCell would implement CellObject.

//set the root node of controller to the main window of the application
AgendaApp.setContent(Controller) controllerInstance);

I went with reflection as the controllers to update/create domain objects consist of mainly TextFields and ListBox's. This would save a lot of time writing the application.

\$\endgroup\$

1 Answer 1

0
\$\begingroup\$

Is my use of Java Reflection an OK design?

Well, no, the usage of reflection is usually an indication of bad design. Honestly, it is almost always. Actually, you have to change the question and ask, when is it okay, to use reflection. The answer is something similar like this: Use reflection, when you do not know how a class looks like during runtime. But this is your code, so, why would you increase complexity and make your code more error prone?


In general, I have some worries, when I read the code:

@Node(name = "lastNameField", type = TextField.class)
private StringProperty lastName = new SimpleStringProperty();

Now, it might be a language or a definition subject, but if Employee is a domain model, it must not have dependencies to the view layer, at least no direct dependencies.


@Edit(controller = EmployeeController.class)
public class Employee extends Item {

The Employee itself is in control, of who controls itself. Shouldn't you tell the EmployeController, which model it shall edit?


Double-clicking on a ListCell would call .edit() on the domain object.

Same thing. The presentation code calls the domain object, then, the domain objects calls someone and says "someone wants to edit me". If I understood it correctly.


There's some other fishy stuff, but I'll conclude now, because in my opinion there are some problems even without the reflection stuff: It seems like an over engineered approach for an easy problem. Or: It looks like the solution of a problem, which probably isn't even a problem. It certainly is, from my point of view, the wrong tool for "the problem". The usage of reflection makes maintaining the code harder and more error prone, at least in my experience. The usage of annotations, too, by the way.

I'd suggest to take a step back to the solution before you implemented reflection and analyze what object oriented principles are violated (e.g. SOLID, coupling, cohesion,...) and what patterns can help you get rid of those problems. Now, that's the major point: Ask what design patterns help you get rid of your design problems, do not ask would reflection get rid of the design problems?.

I recommend fowler's web page about gui architectures: https://martinfowler.com/eaaDev/ (presentation patterns). It's not exactly a "good read", but it certainly is educational. It helps you to understand, which presentation patterns suits your application best and gives you some guidelines about how to implement you presentation layer.

\$\endgroup\$
3
  • \$\begingroup\$ "The presentation code calls the domain object". I think I misled you. Edit() is inside the item class so this is possible: employee.edit(); Thanks for that link. \$\endgroup\$
    – jpell
    Commented Aug 29, 2017 at 22:20
  • \$\begingroup\$ "not have dependencies to the view layer" The TextFields are initialized in the controller, I am using FXML so they are injected. \$\endgroup\$
    – jpell
    Commented Aug 29, 2017 at 22:22
  • \$\begingroup\$ I am using Domain-Driven Design (DDD). \$\endgroup\$
    – jpell
    Commented Aug 29, 2017 at 22:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.