在《[读书笔记]《Effective Java》第二章》[ http://www.flyml.net/2017/02/05/effective-java-chapter-1/ ] 的Item2 提到:
正好, 我要基于Selenium WebDriver 写一个爬虫。因为实际情况不一样, 有的时候需要使用不同的userAgent, 比如模拟移动浏览器, 有的时候需要挂不同的代理。 而且, 未来还很有可能通过更多的参数构建不同的WebDriver实例。其中,最重要的是要构建出不同的 DesiredCapabilities
这是一个实际使用Builder模式的好场景,因为 DesiredCapabilities
的参数会越来越多
如果不使用Builder模式, 在当前情况下, 其实也是可以的:
import org.openqa.selenium.phantomjs.PhantomJSDriverService; import org.openqa.selenium.remote.CapabilityType; import org.openqa.selenium.remote.DesiredCapabilities; public class DesiredCapabilitiesDemo { public DesiredCapabilitiesbuild(String strProxy, String strUserAgent) { DesiredCapabilitiescaps = new DesiredCapabilities(); if(strProxy != null) { org.openqa.selenium.Proxyproxy = new org.openqa.selenium.Proxy(); proxy.setHttpProxy(strProxy) .setFtpProxy(strProxy) .setSslProxy(strProxy); caps.setCapability(CapabilityType.PROXY, proxy); } if(strUserAgent != null) { caps.setCapability( PhantomJSDriverService.PHANTOMJS_PAGE_SETTINGS_PREFIX + "userAgent", strUserAgent); } return caps; } }
不过如果当参数更多的时候, 我们有两种选择:
这个时候, 使用Builder模式就比较灵活了。
本文原创,原文链接:http://www.flyml.net/2017/02/09/effective-java-builder-webdriver-demo/
import org.openqa.selenium.phantomjs.PhantomJSDriverService; import org.openqa.selenium.remote.CapabilityType; import org.openqa.selenium.remote.DesiredCapabilities; public class DesiredCapabilitiesBuilder { private String proxy; private String userAgent; public DesiredCapabilitiesBuilder(){} public DesiredCapabilitiesBuilderproxy(String proxy) { this.proxy = proxy; return this; } public DesiredCapabilitiesBuilderuserAgent(String userAgent) { this.userAgent = userAgent; return this; } public DesiredCapabilitiesbuild() { DesiredCapabilitiescaps = new DesiredCapabilities(); if(this.proxy != null) { org.openqa.selenium.Proxyproxy = new org.openqa.selenium.Proxy(); proxy.setHttpProxy(this.proxy) .setFtpProxy(this.proxy) .setSslProxy(this.proxy); caps.setCapability(CapabilityType.PROXY, proxy); } if(this.userAgent != null) { caps.setCapability( PhantomJSDriverService.PHANTOMJS_PAGE_SETTINGS_PREFIX + "userAgent", userAgent); } return caps; } }
在使用的时候, 如下:
DesiredCapabilitiescaps = new DesiredCapabilitiesBuilder() .userAgent("ua") .proxy("my proxy") .build(); // PhantomJSDriver driver = ...
这样, 当我们增加了新的参数, 就再也不怕了!
如果我们需要build的对象, 是我们自己的类, 而不是第三方的类, 更推荐的方式是将Builder作为 嵌套类 的方式进行应用。
注意: 第一次看到这里的时候, 我也挺困惑的:
只不过另外一个是public static class
实际上, 这个builder 只是作为了外部public class的一个静态成员, 属于 嵌套类 。
有一个写得还不错的帖子: Java方法参数太多怎么办—Part3—Builder模式 里面的使用方法更加复杂,感兴趣的可以去尝试一下。
原文代码太长(参数真的好多),对总览全貌不太方便, 我把参数删除还剩2个,可以看看下面精简之后的代码:
public class Person { // 注意, 这里都加了final的限制 private String lastName; private String firstName; // 注意: 这里我将其改成了私有的构造函数,原文为public // 这样外部调用者就只能通过PersonBuilder,不会直接new Person() 的方式构造了 private Person(final String newLastName, final String newFirstName) { // 更复杂的逻辑可以在这里进行处理 // 比如最简单的,处理各种null值 // 也可以在各自的build方法之中执行, 比如PersonBuilder.lastName() 处理lastName的特殊情况 this.lastName = newLastName; this.firstName = newFirstName; } @Override public String toString() { return String.format("firstName: %s, LastName: %s", this.firstName, this.lastName); } public static class PersonBuilder { private String requiredID; // required private String nestedLastName; // optional private String nestedFirstName; // optional // required的类成员变量, 需要在这里就直接强制要求了 public PersonBuilder(String requiredID) { this.requiredID = requiredID; } public PersonBuilderlastName(String newLastName) { this.nestedLastName = newLastName; return this; } public PersonBuilderfirstName(String newFirstName) { this.nestedFirstName = newFirstName; return this; } public PersoncreatePerson() { return new Person(nestedLastName, nestedFirstName); } } }
本文原创,原文链接:http://www.flyml.net/2017/02/09/effective-java-builder-webdriver-dem
如果我们要实现跟上面的 DesiredCapabilitiesBuilder
类似的复杂逻辑控制, 可以在Person 的构造函数之中进行控制。
一个问题:为什么使用final来限制Person的类成员变量?
其实我看到也有不少Sample 并没有使用final来限制。
如果不加final的限制,实际上是可变的时候, 推荐另外自己在Persion之中加上getters/setters
第二个问题:有没有什么实际使用Builder的例子?
stackoverflow 上面依然有我们想知道的问题的答案:
http://stackoverflow.com/questions/2169190/example-of-builder-pattern-in-java-api
我在guava上面找到一个例子(其他例子看起来就很复杂) :
https://github.com/google/guava/blob/master/guava/src/com/google/common/collect/Interners.java