具有许多参数的类,超出Builder模式

vek*_*tor 9 java constructor builder

上下文

假设您有一个包含许多选项的组件来修改其行为.想与一些排序,过滤,分页等数据,那么这些选项可能是一个表isFilterable,isSortable,defaultSortingKey,等等等等,当然会有一个参数对象封装所有的这些,让我们叫它TableConfiguration.当然我们不想拥有一个巨大的构造函数或一组伸缩构造函数,所以我们使用一个构建器,TableConfigurationBuilder.示例用法可能是:

TableConfiguration config = new TableConfigurationBuilder().sortable().filterable().build();   
Run Code Online (Sandbox Code Playgroud)

到目前为止一直很好,大量的SO问题已经解决了这个问题.

向前进

现在有很多Tables,每个都使用自己的TableConfiguration.但是,并非所有的"配置空间"都是统一使用的:假设大多数表都是可过滤的,而且大多数都是分页的.比方说,只有20种不同的配置选项组合才有意义并且实际使用.根据DRY原则,这20种组合存在于以下方法中:

public TableConfiguration createFilterable() {
  return new TableConfigurationBuilder().filterable().build();
}

public TableConfiguration createFilterableSortable() {
  return new TableConfigurationBuilder().filterable().sortable().build();
}
Run Code Online (Sandbox Code Playgroud)

题

如何管理这20个方法,以便开发人员添加新表可以轻松找到他们需要的配置组合,或者如果它还不存在则添加新表?

以上所有我已经使用了,如果我有一个现有的表来复制粘贴("它就像客户"一样),它的效果相当好.但是,每当需要一些与众不同的东西时,很难弄明白:

  • 有没有一种方法可以完全满足我的需求?(问题A)
  • 如果没有,哪一个是最接近的?(问题B)

我尝试给方法一些非常具有描述性的名称,以表达内部构建的配置选项,但它不能很好地扩展...

编辑

在考虑下面的重要答案的同时,我还想到了另一件事:奖励点用于以类型安全的方式对具有相同配置的表进行分组.换句话说,在查看表格时,应该可以通过定义和查找所有引用来找到所有"双胞胎" .

Fed*_*ner 3

我认为如果您已经在使用构建器模式,那么坚持构建器模式将是最好的方法。使用方法或枚举来构建最常用的TableConfiguration.

不过,关于 DRY,你的观点是有道理的。为什么要在许多不同的地方为几乎每个构建器设置最常见的标志?

因此,您需要封装最常见标志的设置(不要重复自己),同时仍然允许在此公共基础上设置额外的标志。此外,您还需要支持特殊情况。在您的示例中,您提到大多数表格都是可过滤和分页的。

因此,虽然构建器模式为您提供了灵活性,但它使您重复最常见的设置。为什么不制作专门的默认构建器来为您设置最常见的标志呢?这些仍然允许您设置额外的标志。对于特殊情况,您可以以老式的方式使用构建器模式。

定义所有设置并构建实际对象的抽象构建器的代码可能如下所示:

public abstract class AbstractTableConfigurationBuilder
                      <T extends AbstractTableConfigurationBuilder<T>> {

    public T filterable() {
        // set filterable flag
        return (T) this;
    }

    public T paginated() {
        // set paginated flag
        return (T) this;
    }

    public T sortable() {
        // set sortable flag
        return (T) this;
    }

    public T withVeryStrangeSetting() {
        // set very strange setting flag
        return (T) this;
    }

    // TODO add all possible settings here

    public TableConfiguration build() {
        // build object with all settings and return it
    }
}
Run Code Online (Sandbox Code Playgroud)

这将是基础构建器,它不执行任何操作:

public class BaseTableConfigurationBuilder 
    extends AbstractTableConfigurationBuilder<BaseTableConfigurationBuilder> {
}
Run Code Online (Sandbox Code Playgroud)

包含 aBaseTableConfigurationBuilder是为了避免在使用构建器的代码中使用泛型。

然后,您可以拥有专门的构建器:

public class FilterableTableConfigurationBuilder 
    extends AbstractTableConfigurationBuilder<FilterableTableConfigurationBuilder> {

    public FilterableTableConfigurationBuilder() {
        super();
        this.filterable();
    }
}

public class FilterablePaginatedTableConfigurationBuilder 
    extends FilterableTableConfigurationBuilder {

    public FilterablePaginatedTableConfigurationBuilder() {
        super();
        this.paginated();
    }
}

public class SortablePaginatedTableConfigurationBuilder 
    extends AbstractTableConfigurationBuilder
            <SortablePaginatedTableConfigurationBuilder> {

    public SortablePaginatedTableConfigurationBuilder() {
        super();
        this.sortable().paginated();
    }
}
Run Code Online (Sandbox Code Playgroud)

这个想法是让构建器设置最常见的标志组合。您可以创建一个层次结构,或者它们之间没有继承关系,您的电话。

然后,您可以使用构建器来创建所有组合,而无需重复自己。例如,这将创建一个可过滤和分页的表配置:

TableConfiguration config = 
    new FilterablePaginatedTableConfigurationBuilder()
       .build();
Run Code Online (Sandbox Code Playgroud)

如果您希望您的内容TableConfiguration可过滤、分页且可排序:

TableConfiguration config = 
    new FilterablePaginatedTableConfigurationBuilder()
       .sortable()
       .build();
Run Code Online (Sandbox Code Playgroud)

还有一个特殊的表配置,具有非常奇怪的设置,也可以排序:

TableConfiguration config = 
    new BaseTableConfigurationBuilder()
       .withVeryStrangeSetting()
       .sortable()
       .build();
Run Code Online (Sandbox Code Playgroud)