我正在重构前一段时间编写的一些代码,试图通过实现一些设计模式使其更加可靠。具体来说,我尝试使用构建器模式实例化GUI对象。
以下是“产品”的代码:
public class ContainerShell {
private JFrame mainView = new JFrame("Election Simulator");
private JTabbedPane tabbedPane = new JTabbedPane();
private PopulationController populationController;
private CandidateController candidateController;
private ElectionController electionController;
public JFrame getMainView() {
return mainView;
}
public JTabbedPane getTabbedPane() {
return tabbedPane;
}
public void addController(Controller uiController) throws RuntimeException {
switch (uiController.getType()) {
case POPULATION:
this.populationController = (PopulationController) uiController;
case CANDIDATE:
this.candidateController = (CandidateController) uiController;
case ELECTION:
this.electionController = (ElectionController) uiController;
default:
throw new RuntimeException("Unknown controller type");
}
}
public PopulationController getPopulationController() {
return populationController;
}
public CandidateController getCandidateController() {
return candidateController;
}
public ElectionController getElectionController() {
return electionController;
}
}
这是“混凝土建造者”的代码:
public class GuiBuilder implements Builder {
public static final Component contentPaddingX = Box.createRigidArea(new Dimension(10,0));
public static final Component contentPaddingY = Box.createRigidArea(new Dimension(0,10));
public static final Component borderPaddingX = Box.createRigidArea(new Dimension(20,0));
public static final Component borderPaddingY = Box.createRigidArea(new Dimension(0,20));
private ContainerShell voteSimGui = new ContainerShell();
public GuiBuilder() {
}
@Override
public void addPopulationController(PopulationController uiController) {
voteSimGui.addController(uiController);
}
@Override
public void addCandidateController(CandidateController uiController) {
voteSimGui.addController(uiController);
}
@Override
public void addElectionController(ElectionController uiController) {
voteSimGui.addController(uiController);
}
public ContainerShell build() {
voteSimGui.getTabbedPane().addTab("Population", voteSimGui.getPopulationController().getPopPane());
voteSimGui.getTabbedPane().addTab("Candidates", voteSimGui.getCandidateController().getCandPane());
voteSimGui.getTabbedPane().addTab("Election Results", voteSimGui.getElectionController().getElectPane());
voteSimGui.getMainView().setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
voteSimGui.getMainView().getContentPane().add(voteSimGui.getTabbedPane());
voteSimGui.getMainView().pack();
voteSimGui.getMainView().setVisible(true);
return voteSimGui;
}
}
以及“抽象生成器”接口的代码:
public interface Builder {
void addPopulationController(PopulationController popController);
void addCandidateController(CandidateController candController);
void addElectionController(ElectionController electController);
}
现在,这里是“Director”的当前工作构建方法:
public void buildGUI() {
GuiBuilder guiBuilder = new GuiBuilder();
guiBuilder.addPopulationController(ControllerFactory.getPopulationInstance());
guiBuilder.addCandidateController(ControllerFactory.getCandidateInstance());
guiBuilder.addElectionController(ControllerFactory.getElectionInstance());
gui = guiBuilder.build();
}
酷,那有什么问题吗?好吧,这可能最终无关紧要,但这是我对该方法的实际首选实现:
gui = new GuiBuilder()
.addPopulationController(ControllerFactory.getPopulationInstance())
.addCandidateController(ControllerFactory.getCandidateInstance())
.addElectionController(ControllerFactory.getElectionInstance())
.build();
看看它看起来有多干净?唯一的问题是JetBrains无论出于什么原因都在抱怨……我不知道为什么。编译器给出的具体错误是无法解析令牌“addCandidateController”(但它接受addPopulationController)。因此,结合之前的实现工作的事实,我知道这不一定是代码功能的问题,因为它根本不喜欢我试图将方法链接在一起的方式。但是为什么允许Java这么做呢?!(来自另一个项目):
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.kraken.com/0/private/Balance"))
.header("API-Key", auth.getPubKey())
.header("API-Sign", auth.sign("/0/private/Balance", "nonce=" + nonceValue))
.POST(HttpRequest.BodyPublishers.ofString("nonce=" + nonceValue))
.build();
同样,这是另一个项目的一个例子,我在其中实现了Java的HttpRequest。生成器;但是这个实现工作得很好。那么我错过了什么?
问题很简单,您希望“构建器设置器”的返回类型为本身,并且所有此类“构建器设置器”都以返回this;
结尾。换句话说,替换:
public void addCandidateController(CandidateController uiController) {
voteSimGui.addController(uiController);
}
使用:
public GuiBuilder addCandidateController(CandidateController uiController) {
voteSimGui.addController(uiController);
return this;
}
不过,有几个重要的注意事项,因为您没有在这些片段中很好地应用这些“模式”:
>
你在到处侵犯雅格尼。为什么在blazes中为这个构建器制作界面?增加抽象层和间接层是一种罪恶。通常情况下,它是最不邪恶的,也就是说,一个好主意,但这是尽管事实,你正在引入抽象和间接层。间接层混淆了问题,增加了代码量。这些都是成本。你通过支付这些费用来“买”什么?在为构建器提供接口的情况下,答案通常是:完全没有。
构建器通常的命名方案只是属性,而不是添加add表示有一个列表,即使这样,通常的方法仍然是不使用单词add。正确的名称只是candidateController,而不是addCandidateController。
构建器的一个优点是您使用它“构建”的实际对象可以是不可变的。如果您的
ContainerShell
对象不是不可变的,那么构建器有什么意义呢?为什么要有一个构建器(以及接口,现在您拥有的代码是您真正需要的3倍)?为什么不只是:
class ContainerShell {
public ContainerShell setPopulationController(PopulationController c) {
this.populationController = c;
return this;
}
public ContainerShell setCandidateController(CandidateController c) {
this.candidateController = c;
return this;
}
public ContainerShell setElectionController(ElectionController c) {
this.electionController = c;
return this;
}
}
上面的代码可以按照您想要的方式精确使用:
new ControllerShell()
.setPopulationController(ControllerFactory.getPopulationInstance())
.setCandidateController(ControllerFactory.getCandidateInstance())
.setElectionController(ControllerFactory.getElectionInstance())
;
并使用四分之一的代码,显然是赢家。
最好将构建器视为替换大型参数列表。换句话说,构建器实际上是简单方法调用的替代方案——当所述方法(或构造函数,它在几乎所有相关方面都等同于静态方法)需要如此大的参数列表以至于阅读调用它的代码令人困惑时,您可以使用它们。例如,给定此方法:
public Bridge(int buildYear, int carLanes, int bikeLanes, int walkLanes, int span) { ... }
对它的调用看起来像:
new Bridge(1981, 4, 2, 2, 2005)
。任何阅读它的人都不知道发生了什么,每次你尝试编写对它的调用时,你都不知道该做什么。这是标志1:哦,也许构建器是合适的。标志2是:实际上,对于这些参数中的许多,我可能希望它是可选的(java做得不好,因为你所能做的就是组合重载爆炸)。构建器将参数命名为“参数”,并为您提供将它们设为可选的选项。换句话说,构建器会替换它:
new Bridge(1981, 4, 0, 2, 2005)
有了这个:
Bridge.builder()
.buildYear(1981)
.carLanes(4)
// we don't call .bikeLanes; it defaults to 0.
.walkLanes(2)
.span(2005)
.builder()
正如您所见,构建器要“冗长”得多,因此使用它们(更多代码!)是要付出代价的。你从中得到的东西值这个价吗?我可以这么说,在这个例子中,灌篮。这段代码的可读性要高得多。您还可以选择将构建桥的工作拆分,以便各个独立的代码段分别完成部分工作(您不能真正“拆分”新桥(1981,4,0,22005)
,但您可以创建一个桥生成器,调用一半的“setter”,然后将生成器对象交给其他方法来完成工作。很少有人想要这个,但如果这是相关的,那么建设者是很棒的。
从技术上讲,任何时候用它替换方法都可以使用构建器,无论该方法是什么。在实践中,“具有非常长、笨重、令人困惑的参数列表的方法”在制作不可变对象时最常出现:鉴于您需要指定构造函数中的所有字段(没有setters;它是一个不可变对象!),自然,您会得到长列表,然后导致需要构建器。大多数书籍、教程等“捷径”这种逻辑推理,他们不应该这样做。它是这样的:
不要跳过步骤,比如说“不可变最好由构建器生成”。这太简单了。
重点是,你要在这里替换哪个“长argslist”?我看不出来-因此,builder在这里是错的。除非你想深入其中,并将ContainerShell对象更改为不可变。不变性有各种各样的优点,然而,GUI对象并不真正“需要”这些优点中的任何一个,这再次让我们回到:我怀疑你想要一个构建器来实现这一点。我认为你只是想让二传手回归自我,忘记所有关于为这做一个建设者的事情。
请记住,编程风格指南和原则只有一个目标:使您的代码更易于维护(包括在面对未来可能的更改请求时更加灵活)。如果您无法想象规则的应用如何有助于实现这一目标,那么就不要应用它——规则本身本身并不是一个目标。
要启用调用链,请返回正在修改的对象,即生成器对象。
在每个添加…方法中,添加最后一行,并返回它 。并将返回类型从void更改为builder类。
顺便说一句,我不认为需要您的Builder
接口。你真的会有多个实现吗?
@Override
public GuiBuilder addPopulationController(PopulationController uiController) {
voteSimGui.addController(uiController);
return this ;
}
@Override
public GuiBuilder addCandidateController(CandidateController uiController) {
voteSimGui.addController(uiController);
return this ;
}
您可以在Java的OpenJDK实现中找到构建器的开源示例。请参阅DateTimeFormatterBuilder
和HttpRequest. Builder
。
你能帮我在jrxml中设置或更改哪些属性以获得相同的PDF输出吗?
我使用ACR122读卡器已经有一段时间了,它在读取Mifare 1K或Mifare Ultralight NFC卡时都没有问题。 将读卡器升级到最新版本(ACR1251)后,我的程序无法读取Mifare 1K卡的UID。 这是我用来阅读的片段: 使用新版rad阅读器: ResponseAPDU.getSW1()函数返回98 而getSW2()返回130 我试着在网上和读卡器文档中搜索响应代码的解释
我已经从Godaddy购买了SSL,我的网站托管在AWS中。我想在AWS上设置SSL。我绑定使用证书管理器导入证书。它问了我三件事: 证书体*--在这里,我从Godaddy获得了。crt文件内容 证书私钥*--这里我仍然困惑需要输入什么。请帮我做这个 证书链--在这里我输入了sf_bundle-g2-g1.crt代码。 请帮助我启用AWS中的SSL。提前致谢
我试图写一个代码为每个股票价值是75美元或更多添加一个"*"在STK_FLAG列。 ORA-06550:第15行,第21列:PLS-00201:标识符“STK\U FLG”必须声明ORA-06550:第15行,第5列:PL/SQL:SQL语句忽略ORA-06550:第23行,第7列:PL/SQL:ORA-00904:“STK\U FLG”:无效标识符ORA-06550:第17行,第5列:PL/SQ
我需要一些帮助用JSOUP解析这个html。我正在尝试从表中的每一列获取数据值。我一直在看JSoup文档,试图弄清楚我到底需要做什么,但还是不确定。看起来网站使用了CSS和内联格式的组合;其中大部分可以转换为CSS并减小页面大小。 这是html文件的一个小片段(实际上差不多有5 MB大小)。 更新:我已经更新了源代码,以更准确地显示html的结构。我假定tbody将位于表元素中是一个给定的条件。我
我有一个linq查询,我在foreach循环中迭代其结果。 第一个是一个从表布局面板中获取控件集合的查询,然后我遍历该集合并从表布局面板中删除控件,因此: 上面的操作并不像我预期的那样(即抛出一个错误),它只删除了一半的控件(奇数的控件)。似乎在每次迭代中,AllItems都会减少其自身,尽管底层集合正在修改,但不会抛出错误。 如果使用字符串数组执行Similar: 这次Visual studio