简体   繁体   中英

Is there design pattern to update hundreds of attributes of a Java class?

We have a use case where there is a class that has more than 130 attributes. There is a method that populates all the attributes and it is just very long just because of large number of attributes. Just the setters would make the method very long.

Just to give context about the source for these attributes, these are populated by different datasources. Few of them are from different micro services and few of them are from a mysql tables.

Our current solution is to group them into different groups based on the source or some similar business trait and update them in different methods. This has made some of the setters in different methods and just makes it very hard to read.

I have read else where about the builder pattern. But most of these attributes need some transformation after fetching from source and assigning each of the attributes to a temp variables and using them in the builder doesn't help much.

"Is there a design pattern...?" Nope.

First and foremost, reconsider whether this object even needs that many fields. "Our current solution is to group them into different groups" this suggests it doesn't.

If it genuinely needs that many fields and if the fields are identically named on both the source and target objects, ie the main instance with 130 fields is basically a composite of all the others, then you could consider using reflection.

eg for the following value objects

class Main {
    private String a;
    private Number b;
    private String c;
    private String d;
}
class A {
    private final String a;

    A(String a) {
        this.a = a;
    }
}
class B {
    private final Integer b;

    B(Integer b) { this.b = b; }
}
class C {
    private final String c;

    C(String c) { this.c = c; }
}

A simple example would be

public static void main(String[] args) throws Exception {
    Main main = new Main();

    A a = new A("a1");
    B b = new B(2);
    C c = new C("c3");

    // Get all values from all sources
    Map<String, Object> fieldToValue = new HashMap<>();
    Stream.of(a, b, c).forEach(obj -> {
        Field[] fields = getFieldsForInstance(obj);
        for (Field field : fields) {
            try {
                Object prevValue = fieldToValue.put(field.getName(), field.get(obj));
                if (prevValue != null) {
                    throw new RuntimeException("Duplicate source field " + field.getName());
                }
            }
            catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }
    });

    // Set the values on main instance
    Field[] mainFields = getFieldsForInstance(main);
    for (Field field : mainFields) {
        if (fieldToValue.containsKey(field.getName())) {
            Object value = fieldToValue.get(field.getName());
            if (value != null) {
                if (field.getType().isAssignableFrom(value.getClass())) {
                    field.set(main, value);
                }
                else {
                    throw new RuntimeException("Incompatible types for field " + field.getName());
                }
            }
        }
        else {
            System.out.println("warning: field " + field.getName() + " is unset on main");
        }
    }
}

private static Field[] getFieldsForInstance(Object obj) {
    Field[] fields = obj.getClass().getDeclaredFields();
    for (Field field : fields) {
        field.setAccessible(true);
    }
    return fields;
}

I'd also consider whether you could use code generation at compile time, eg JavaPoet.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM