如何创建可以构建多种Java对象的Builder?

时间:2014-09-18 12:30:46

标签: java design-patterns

我正在尝试创建一种Multi Strategy Builder。我的意思是这个构建器应该是通用的,并且应该有不同的方法取决于实体类型。让我解释一下。

取决于实体我们需要有不同的方法。例子:

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

并且此构建器必须没有其他方法!

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

我应该使用什么样的模式?以及如何组织我的设计?

3 个答案:

答案 0 :(得分:4)

如果您要求单个Builder类型不需要了解它所操作的实体的任何信息,那么Java就不能像您描述的那样轻松地完成。在Builder<User>示例中,需要在编译时知道withNamewithAge方法。

由于这些方法实际上并不存在,但需要动态添加,因此您必须编写自定义类加载器。链接Builder类后,您无法更改它,并且因为在传入实体之前它不知道您想要什么方法,所以在您需要添加方法时更改为时已晚。

你可以通过使用反射来获得大部分。例如,您可以在Builder中使用单个方法,例如with(String field, Class<?> c, Object value)。然后,假设您正在构建的对象实例有一个名为“set __”的方法,您可以使用Method类来调用传入的值的相应set方法。

这看起来像是:

 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

更好的想法是为您要构建的内容创建类:AccountBuilderUserBuilder,等等。这更清晰,可能是Java中最好的,而不需要使用自定义类加载器等异国情调的解决方案。 (是的,你必须为每个人写下属性。)

但是,如果您决定需要进行大量构建,则可能需要考虑使其更容易实现的替代语言,例如RubyElixir。动态方法不是Java的强项,并且试图绕过语言的限制只是为了实现特定的API通常是一个糟糕的想法。

答案 1 :(得分:3)

根据在Java中传递给它的类,通用接口不可能有不同的方法。这意味着Builder接口可以具有:

  • withNamewithAge方法
  • withCountrywithNumberwithCode种方法
  • 所有这些

没有可以克服Java语言限制的设计模式。我建议的是使用通用构建器界面,如:

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

然后为每个实体类型创建单独的子类:

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;
    }
}

然后像

一样使用它
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");

就是这样。使用Fluent Interface模式可以略微改善这一点,但无论如何,在Java中不可能实现它的工作方式与您的问题完全相同。

希望这会有所帮助......

答案 2 :(得分:1)

首先,添加动态方法不是很像java。

Builder是一种创造性模式,策略是行为式的。我不会在这里混淆它们。

Builder将创建给定TYPE的对象。您的用户和帐户是否具有相同的类型? IMO与其他设计模式书不同,GoF书非常有效地解释了一种对象的类型。请阅读讨论。鉴于代码片段,我不会说用户和帐户具有相同的类型。我会使用不同的构建器来构建用户和帐户的对象。类型讨论也将在战略中派上用场。