简体   繁体   中英

How do I create a Builder that can build more than one kind of Java object?

I'm trying to create a kind of Multi Strategy Builder . I mean this builder should be generalized and should have different method depends on entity type. Let me explain it.

Depends on entity we need to have different methods. Examples:

Builder<User> userBuilder;
userBuilder.withName(«Bob»);
userBuilder.withAge(17);

and this builder must have no others method!

Builder<Account> accountBuilder;
accountBuilder.withCounry(«Fr»);
accountBuilder.withNumber(2846218354);
accountBuilder.withCode(«X34»);

What pattern should I use? And how to organize my design?

If you're asking for a single Builder type that doesn't need to know anything about the entities it operates on, Java can't easily do it the way you describe. In the Builder<User> example, the withName and withAge methods would need to be known at compile-time.

Since those methods don't actually exist, but would need to be dynamically added, you'd have to write a custom class-loader. Once the Builder class is linked, you can't change it, and since it doesn't know what methods you want until you pass in an entity, it's too late to change at the point you need to add methods.

You could get mostly there by using reflection. For instance, you could have a single method in Builder called, say, with(String field, Class<?> c, Object value) . Then, assuming the object instance you're building has a method named "set___", you can use the Method class to invoke the corresponding set method with the value that was passed in.

That would look something like:

 Builder<Account> b = new Builder<>();
 b.with("AccountNumber", String.class, "5897-1048-2949");
     // this invokes "setAccountNumber" method on internal
     //   Account instance with "5897-1048-2949" cast to String

A better idea is to have classes for thing you're going to build: AccountBuilder , UserBuilder , and so on. This is clearer and is probably the best you can do in Java without resorting to exotic solutions like custom class-loaders. (Yes, you'll have to write the properties for each one.)

But if you decide you need to do a lot of building, you may want to consider an alternative language that makes this easier to pull off, like Ruby or Elixir . Dynamic methods are not Java's strong suit, and trying to circumvent a language's limitations just to achieve a particular API is generally a terrible idea.

It's not possible for generic interface to have different methods depending on passed to it class in Java. This means that Builder interface can have either:

  • withName and withAge methods
  • withCountry , withNumber and withCode methods
  • all of them

There is no design pattern that can overcome Java language limitations. What I would advise is to have generic builder interface like:

public interface Builder<T> {
    T build();
}

and then create separate subclasses for each entity type:

public class UserBuilder implements Builder<User> {

     public void withName(String name) {
         // ...
     }

     public void withAge(int age) {
         // ...
     }

     public User build() {
         User user = new User();
         user.setName(...);
         user.setAge(...);
         return user;
     }

}

public class AccountBuilder implements Builder<Account> {

    public void setCountry(String country) {
         // ...
    }

    public void setNumber(String number) {
         // ...
    }

    public void setCode(String code) {
         // ...
    }

    public Account build() {
         Account account = new Account();
         account.setCountry(...);
         account.setNumber(...);
         account.setCode(...);
         return account;
    }
}

and then use it like

UserBuilder userBuilder = new UserBuilder();
userBuilder.setName("John");
userBuilder.setAge(20);
User user = userBuilder.build();

AccountBuilder accountBuilder = new AccountBuilder();
accountBuilder.setCountry("UA");
accountBuilder.setNumber("333-22-1");
accountBuilder.setCode("123");

And that's it. This can be slightly improved by using Fluent Interface pattern but anyway in Java it's impossible to implement it to work exactly like in your question.

Hope this helps...

First, adding dynamic methods is not very java like.

Builder is a creational pattern and strategy is behavioural. I would not mix them here.

Builder will create objects of a given TYPE. Do your USER and Account have a same type? IMO, unlike other design patterns books, the GoF book very effectively explains what a type of an object is. Do read the discussion. Given the code snippets, I wouldn't say user and account have same type. I would use different builders for building the objects of user and account. The type discussion will come in handy for strategy as well.

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