mercredi 3 janvier 2018

Elegant way to encapsulate changes to a POJO?

I'm rewriting the batch change functionality for a system that takes in a list of Entity objects and a list of changes to apply to all of those objects passing everything by (air quotes) reference (/air quotes), but I'm not quite sure on the best approach.

I have two goals: 1. explicitly return objects and 2. Clean up the function that maps the changes to the object instead of having a giant if-else matching a string to a setter function.
I feel like there has to be a better way to do this that doesn't necessarily require reflection.

My initial idea is to break out the validation into a separate function that would run first, each change would get passed into a validate(entity, change); function that could return an error description (not throw an error), then if the changes passed they are applied to the Entity object in a single go and then return the altered Entity to be written vs the current pattern of applying field by field.

I don't have any ideas on how to map the BulkEditChange field to the Entity field/setter function besides keeping the giant if-else block in 'setValueForObjectX', I would appreciate any ideas.

Below is a over simplification of the way it is written now:

POJO that contains the change

public class BulkEditChange {

    private String field;  //name of field to change on Entity Object
    private String modifier; // 'append', 'add' or 'subtract' used for integer values
    private String value; //String representation of the value

    public String getField() {
        return field;
    }

    public String getModifier() {
        return modifier;
    }

    public void setModifier(String modifier) {
        this.modifier = modifier;
    }


    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }
}

Then in the service class the changes are applied to the given object

private Entity updateEntityValues(List<Entity> entities, List<BulkEditChange> changes, String batchId) {
    List<ValidationError> errors = new ArrayList();

    for(Entity entity : entities) {
            for (BulkEditChange change : changes) {
                if (refreshedEntity instanceof ObjectX) {
                    setValueForObjectX((ObjectX) refreshedEntity, fieldView, errors);
                } else { //instance of another object
                    setValueForObjectY((ObjectY) refreshedEntity, fieldView, errors);
                }
            }
    }

    if(ArrayUtils.isNotEmpty(errors)){
        throw new CombinedValidationError("Changes could not be applied because reasons", errors);
    }

    entityDAO.writeAll(entities);

}

//GOAL 1: Stop attempting to pass the Entity and Errors list by 'reference'
private void setValueForObjectX(ObjectX obj, BulkEditChange change, List<ValidationError> errors){

//GOAL 2: See if I can make this mapping of field name to setter less brute force?
       if(change.getField().equals("name")){
           if(change.getValue() == null){
               ValidationError error = new ValidationError("name cannot be blank");
               errors.add(error);
           } else {
               entity.setName(change.getValue());
           }
       } else if( change.getField.equals("type")){
           //do business logic to check the type, add to errors list if doesn't pass
           //if valid set on the entity object
       } else if( // Is other field)...
       // Goes on and on for each field that can be altered
}


private void setValueForObjectY(ObjectY obj, BulkEditChange change, List<ValidationError> errors){
    //Same idea with other object type value setting functions
    //Just with different validations
}





Aucun commentaire:

Enregistrer un commentaire