Jim
Jim

Reputation: 4415

Factory pattern using a hashmap and functional approach

This is a follow up of my earlier question.
I want to create a factory like approach to be able to call different methods depending on the type.
The following code works (note it is long but it is a very simplified version to show the structure. In reality for instance there won't be just one Person added to the result list):

public class TestBuilder <T>{
    private interface Processor <T> {
        List<Person> process(T t, String firstName, String lastName, String id, String cc);
    }

    private class FooProcessor implements Processor <Foo> {
        @Override
        public List<Person> process(Foo foo, String firstName, String lastName, String id, String cc) {
            System.out.println("processFoo");
            Person p = new Person(firstName, lastName);
            p.setId(foo.getId());;
            return new ArrayList<>(Arrays.asList(p));
        }
    }

    private class BarProcessor implements Processor <Bar> {
        @Override
        public List<Person> process(Bar bar, String firstName, String lastName, String id, String cc) {
            System.out.println("processBar");
            Person p = new Person(firstName, lastName);
            p.setCc(bar.getCC());
            return new ArrayList<>(Arrays.asList(p));
        }
    }

    private Map<AnInterface<T>, Processor <T>> map = new HashMap<>();
    private TestBuilder() {
        map.put((AnInterface<T>) new Foo(), (Processor<T>) new FooProcessor());
        map.put((AnInterface<T>) new Bar(), (Processor<T>) new BarProcessor());
    }


    public List<Person> process(T t, String firstName, String lastName, String id, String cc) {
        return map.get(t).process(t, firstName, lastName, id, cc);
    }


    public static void main(String[] args) {
        List<Person> personList = new TestBuilder().process(new Foo(), "John", "Doe", "123", null);
        System.out.println(personList);
    }
}

The issues I have that I'd like help with are the following:

I am passing null in some cases in the process method because not all arguments are relevant for all types. Hence I also want to ask how can I make the approach more flexible i.e. so that I am able (as I add a new type) to pass if possible only the relevant arguments without making the code complicated?

Upvotes: 0

Views: 678

Answers (1)

Sharon Ben Asher
Sharon Ben Asher

Reputation: 14373

I suggest to adopt the builder pattern to build a `Person. This pattern allows you to specify optional args and has the added value of "naming" args through fluent setter methods. something like

private interface Processor <T> {
    public static class Builder {
        private T t = null;  // assign default values
        private String firstName = null;
        private String lastName = null;
        private String cc = null;

        public Builder with(T t) {
            this.t = t;
            return this;
        }
        public Builder withFirstName(String firstName) {
            this.firstName = firstName;
            return this;
        }
        // same fluent setters for all instance vars
        public Processor build() {
            return new Processor(...);
        }
    }

    List<Person> process(T t, String firstName, String lastName, String id, String cc);
}

Processor processor = Processor.Builder
    .with(new Foo())
    .withFirstName("John")
    .withLastName("Doe")
    // no explicit set for cc, so default is used
    .build();
processor.process();

Update Oct 24th

following the comments, I went and re-examined the sample code from the question and realised that the builder pattern can apply to build an instance of Person and that instance can be passed to the process() method:

public class PersonBuilder {
    private Person person = null;

    public PersonBuilder(String firstName, String lastName) {
        person = new Person(firstName, lastName);
    }
    public PersonBuilder withId(String id) {
        person.setId(id);
        return this;
    }
    public PersonBuilder withCc(String cc) {
        person.setCc(cc);
        return this;
    }
    public Person build() {
        return person;
    }
}

the definition of process() is simplified to

private interface Processor <T> {
    List<Person> process(T t, Person person);
}

and the call in main() becomes

public static void main(String[] args) {
    List<Person> personList = new TestBuilder().process(new Foo(),
        // no null arg
        new PersonBuilder("John", "Doe").withId("123").build());
    System.out.println(personList);
}

Upvotes: 2

Related Questions