Generic Builder Pattern class using reflection [cl

2020-08-09 05:38发布

问题:

It would be great to make a implementation of the builder pattern with generics. In theory it would be possible to use reflection to make the following possible:

    MyClass myClass = GenericBuilder<MyClass>.aObject()
            .withThisProperty("foo")
            .withThatProperty(4)
            .build();

I already made the following code:

    public class CursistBuilder {
        private Cursist cursist = null;

        private CursistBuilder() {
            cursist = new Cursist("username not set", "email not set");
        }

        public static CursistBuilder aCursist() {
            return new CursistBuilder();
        }

        public CursistBuilder withNaam(String name) {
            cursist.setGebruikersnaam(name);
            return this;
        }

        public CursistBuilder withEmail(String email) {
            cursist.setEmail(email);
            return this;
        }

        public Cursist build() {
            return cursist;
        }
    }

How can this be accomplished?

回答1:

it is not proper builder pattern, as object is not created in create function, but you could use it as reference for improvement

public static class Builder<T> {
        public final T instance;

        public Builder(Class<T> clazz) throws InstantiationException,
                IllegalAccessException {
            super();
            this.clazz = clazz;
            this.instance = clazz.newInstance();
        }

        private final Class<?> clazz;

        Builder<T> setProperty(String name, Object value)
                throws IllegalAccessException, IllegalArgumentException,
                InvocationTargetException, NoSuchMethodException,
                SecurityException {

            Method method = clazz.getMethod("set"
                    + name.substring(0, 1).toUpperCase() + name.substring(1),
                    value.getClass());
            method.invoke(instance, value);
            return this;
        }

        T create() {
            return instance;
        }
    }

how to use it: yo are creating instance of builder by passing class to constructor

Builder<MyClass> builder = new Builder<>(MyClass.class);

and then use method setProperty(String name, Object value) to call setter on your object,

what you coul done? for example pass some default values for your class and dont use non-args constructor



回答2:

I do not know this is what you wanted, however if I were you I will implement like below//

main >>

public class Main {
    public static void main(String[] args) {
        GenericBuilder gb = GenericBuilder.getInstance();
        gb=
        gb.withThisProperty("String", "AAAAAA").
        withThisProperty(4, 9).
        withThisProperty(5.8f, 6.7f).
        withThisProperty("Array", new ArrayList<String>()).build();

        System.out.println(gb.toString());
    }
};

VO >>

public class GenericBuilder {

    private Map<Object, Object> map;

    // Implementation of Singleton 
    private GenericBuilder (){
        map = new HashMap<Object, Object>();
    }

    private static class LazyLoader{
        private static final GenericBuilder INSTANCE = new GenericBuilder(); 
    }

    public static GenericBuilder getInstance(){
        return LazyLoader.INSTANCE;
    }
    //End Singleton

    public GenericBuilder withThisProperty(Object key, Object param){
        map.put(key, param);
        return this;
    }

    public Object get(Object key){
        return map.get(key);
    }

    public GenericBuilder build(){
        return this;
    }

    @Override
    public String toString() {
        StringBuffer returnVal = new StringBuffer();
        Iterator<Object> iter = map.keySet().iterator();
        while(iter.hasNext()){
            Object key = iter.next();
            returnVal.append("key >> " + key  + " value >> " + map.get(key) + "\n");
        }
        return returnVal.toString();
    }
}

I overriden toString() to show what is in it, and declare a Map to assign value to somewhere, if you want you can change this with other Collections. Thanks/