1

I created a class following the Java builder pattern and it has been working great for me, however now I find myself needing a little more modularity. Can this be accomplished without scrapping the Builder?

For example I would like to build my Person objects with a configuration array of enums (or any other method that would let me customize the order and selection of fields). The values come in from various nested objects and I need certain fields at certain times in certain orders to create an output CSV.

public static enum FIELDS = { FIRST, MIDDLE, LAST }

List<FIELDS> fields = { FIRST, LAST } creates:
Person person = new Person.builder().first("john").last("doe").build();

List<FIELDS> fields = { LAST, FIRST, MIDDLE } creates:
Person person = new Person.builder().last("doe").first("john").middle("q").build();

Is it possible to do any kind of switch statements on an enum to construct a Person dynamically like this?

Community
  • 1
  • 1
xref
  • 1,707
  • 5
  • 19
  • 41
  • So you need to create your instance using a configuration array of enums? Or do you want to have several variants of a builder, with specific constraints? – mikołak Sep 06 '13 at 22:18
  • @TheTerribleSwiftTomato a config array of enums (or any equivalent way), updated question to be more clear – xref Sep 06 '13 at 22:23
  • alright, but one more thing is unclear. Where do you want to get your values from if all you have is enums mapping to fields? – mikołak Sep 06 '13 at 22:24
  • @TheTerribleSwiftTomato Heh updated again, values come in from a csv and are passed to the method that constructs the Builder. This is a simplified version here, the real one has Lists of Objects and such, not a simple enum->fieldname mapping. – xref Sep 06 '13 at 22:29
  • If you're loading data from a file, what's the point of a builder, which is for source-level creation? – Dave Newton Sep 06 '13 at 22:50
  • @DaveNewton well actually the data comes in from assorted objects and I need to format it to go out to a csv, updated question – xref Sep 06 '13 at 22:55

2 Answers2

3

Well you could do something like that; e.g.

   public Person list2Person(List<FIELDS> fields) {
       Builder builder = new Person.builder();
       for (Field field : fields) {
           switch (field) {
           case FIRST:
              builder.first(getValue(FIRST));
              break;
           case MIDDLE:
              builder.middle(getValue(MIDDLE));
              break;
           ...
           }
           return builder.build();
       }
   }

Of course, this assumes that you have some way to implement getValue(FIELDS).

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • Wow guess I was really over-thinking that one, this is exactly what I needed. Thanks Stephen! – xref Sep 06 '13 at 23:56
0

The work should be done inside the enum itself, probably. You can declare an abstract method in the main enum body and then override it in each Enum element.

import java.util.Iterator;

public class Person {
  public static enum FIELDS {
    FIRST ("john") { protected void addString(PersonBuilder builder) { builder.first(getName()); } },
    MIDDLE ("q") { protected void addString(PersonBuilder builder) { builder.middle(getName()); } },
    LAST ("doe") { protected void addString(PersonBuilder builder) { builder.last(getName()); } };

    private String name;

    protected abstract void addString(PersonBuilder builder);

    private FIELDS(String name) {
      this.name = name;
    }

    protected String getName() {
      return name;
    }
  }

  public static class PersonBuilder {
    public PersonBuilder addIterable(Iterable<FIELDS> iter) {
      Iterator<FIELDS> it = iter.iterator();
      while(it.hasNext()) {
        it.next().addString(this);
      }

      return this;
    }

    // you wrote these already, this is just to make it compile
    public void first(String first) { }
    public void middle(String middle) { }
    public void last(String last) { }
  }
}
durron597
  • 31,968
  • 17
  • 99
  • 158