假设您有一个组件,有很多选项可以修改它的行为。考虑一个具有排序、过滤、分页等功能的数据表。选项可以是 isFilterable、isSortable、defaultSortingKey 等。当然会有一个参数对象来封装这一切,姑且称之为TableConfiguration。当然我们不想有一个巨大的构造函数,或者一组伸缩构造函数,所以我们使用了一个builder,TableConfigurationBuilder。示例用法可以是:
TableConfiguration config = new TableConfigurationBuilder().sortable().filterable().build();
到目前为止一切顺利,大量 SO 问题已经解决了这个问题。
现在有大量的表,每个表都使用自己的TableConfiguration。然而,并不是所有的“配置空间”都被统一使用:假设大多数表格都是可过滤的,并且其中大部分都是分页的。比方说,只有 20 种不同的配置选项组合有意义并实际使用。根据 DRY 原则,这 20 种组合存在于如下方法中:
public TableConfiguration createFilterable() {
return new TableConfigurationBuilder().filterable().build();
}
public TableConfiguration createFilterableSortable() {
return new TableConfigurationBuilder().filterable().sortable().build();
}
如何管理这20个方法,让开发者在添加新表的时候可以很方便的找到自己需要的配置组合,或者在没有的情况下添加一个新的?
以上所有我已经使用过,如果我有一个现有的表来复制粘贴(“它完全像 Customers”),它工作得相当好。然而,每次需要一些不寻常的东西时,都很难弄清楚:
我试图给这些方法一些非常具有描述性的名称来表达内部构建的配置选项,但它并不能很好地扩展......
在思考下面的好答案时,我又想到一件事: 以类型安全的方式对具有相同配置的表进行分组的加分项。换句话说,在查看表格时,应该可以通过转到定义 和找到所有引用 来找到它的所有“双胞胎”。
最佳答案
我认为,如果您已经在使用构建器模式,那么坚持使用构建器模式将是最好的方法。使用方法或枚举来构建最常用的 TableConfiguration 没有任何好处。
不过,关于 DRY,您的观点是正确的。为什么要在许多不同的地方为几乎每个构建器设置最常见的标志?
因此,您需要封装最常用标志的设置(不再重复),同时仍然允许在这个通用基础上设置额外的标志。此外,您还需要支持特殊情况。在您的示例中,您提到大多数表格都是可过滤和分页的。
因此,虽然构建器模式为您提供了灵 active ,但它会让您重复最常见的设置。为什么不制作专门的默认构建器来为您设置最常见的标志?这些仍然允许您设置额外的标志。对于特殊情况,您可以使用老式的构建器模式。
定义所有设置并构建实际对象的抽象构建器的代码可能如下所示:
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
}
}
这将是基础构建器,它什么都不做:
public class BaseTableConfigurationBuilder
extends AbstractTableConfigurationBuilder<BaseTableConfigurationBuilder> {
}
包含 BaseTableConfigurationBuilder 是为了避免在使用构建器的代码中使用泛型。
然后,您可以拥有专门的构建器:
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();
}
}
我们的想法是让构建器设置最常见的标志组合。你可以创建一个层次结构或者它们之间没有继承关系,你的调用。
然后,您可以使用您的构建器创建所有组合,而无需自己重复。例如,这将创建一个可过滤和分页的表配置:
TableConfiguration config =
new FilterablePaginatedTableConfigurationBuilder()
.build();
如果您希望您的 TableConfiguration 可过滤、分页且可排序:
TableConfiguration config =
new FilterablePaginatedTableConfigurationBuilder()
.sortable()
.build();
还有一个特殊的表格配置,具有非常奇怪的设置,但也是可排序的:
TableConfiguration config =
new BaseTableConfigurationBuilder()
.withVeryStrangeSetting()
.sortable()
.build();
关于java - 具有许多参数的类,超出了 Builder 模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35133488/
我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co
我想安装一个带有一些身份验证的私有(private)Rubygem服务器。我希望能够使用公共(public)Ubuntu服务器托管内部gem。我读到了http://docs.rubygems.org/read/chapter/18.但是那个没有身份验证-如我所见。然后我读到了https://github.com/cwninja/geminabox.但是当我使用基本身份验证(他们在他们的Wiki中有)时,它会提示从我的服务器获取源。所以。如何制作带有身份验证的私有(private)Rubygem服务器?这是不可能的吗?谢谢。编辑:Geminabox问题。我尝试“捆绑”以安装新的gem..
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby中使用两个参数异步运行exe吗?我已经尝试过ruby命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何rubygems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除
鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende
我有一些Ruby代码,如下所示:Something.createdo|x|x.foo=barend我想编写一个测试,它使用double代替block参数x,这样我就可以调用:x_double.should_receive(:foo).with("whatever").这可能吗? 最佳答案 specify'something'dox=doublex.should_receive(:foo=).with("whatever")Something.should_receive(:create).and_yield(x)#callthere
我正在为一个项目制作一个简单的shell,我希望像在Bash中一样解析参数字符串。foobar"helloworld"fooz应该变成:["foo","bar","helloworld","fooz"]等等。到目前为止,我一直在使用CSV::parse_line,将列分隔符设置为""和.compact输出。问题是我现在必须选择是要支持单引号还是双引号。CSV不支持超过一个分隔符。Python有一个名为shlex的模块:>>>shlex.split("Test'helloworld'foo")['Test','helloworld','foo']>>>shlex.split('Test"
我不确定传递给方法的对象的类型是否正确。我可能会将一个字符串传递给一个只能处理整数的函数。某种运行时保证怎么样?我看不到比以下更好的选择:defsomeFixNumMangler(input)raise"wrongtype:integerrequired"unlessinput.class==FixNumother_stuffend有更好的选择吗? 最佳答案 使用Kernel#Integer在使用之前转换输入的方法。当无法以任何合理的方式将输入转换为整数时,它将引发ArgumentError。defmy_method(number)
我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/
两者都可以defsetup(options={})options.reverse_merge:size=>25,:velocity=>10end和defsetup(options={}){:size=>25,:velocity=>10}.merge(options)end在方法的参数中分配默认值。问题是:哪个更好?您更愿意使用哪一个?在性能、代码可读性或其他方面有什么不同吗?编辑:我无意中添加了bang(!)...并不是要询问nobang方法与bang方法之间的区别 最佳答案 我倾向于使用reverse_merge方法:option