学习设计模式不光要学习设计模式的思想,还要去深入理解,为什么要用这个设计模式。
如何深入理解?读优秀的框架代码,看别人代码,了解它们的使用场景。 - - - 博主老师(感谢他)
本文先介绍了Builder模式的概念及简单实现。再介绍了netty中对Builder模式的实现。最后总结了一点点思考。
1、概念
定义:将复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
场景:
- 相同的方法,不同的执行顺序,产生不同的事件结果时。
- 多个部件或零件,都可以装配到一个对象中,但是产生的运行结果又不相同时。
- 类中方法调用顺序不同产生了不同的作用。
- 初始化一个对象特别复杂,参数过多,且很多参数都具有默认值时。
2、实现
builder实现分为标准的builder模式和builder模式变种
个人浅见:博主觉得标准的builder模式太过复杂了,本来就一个构造对象这么个简单需求,整了太多类…
在实际的研发当中,还是builder模式的变种用的多点。标准的builder模式只做个了解就行…
2.1 标准的builder模式实现
书本的实现:纸张是用户必选的。装订方式是非必选的(不装订~就是这么任性)。另外提供了两个构建者:精装书构建着和平装书构建者。
public class Book {
private String paper; // 纸张
private String binding; // 装订
public void setPaper(String paper) {
this.paper = paper;
}
public void setBinding(String binding) {
this.binding = binding;
}
@Override
public String toString() {
return "Book{" +
"paper='" + paper + '\'' +
", binding='" + binding + '\'' +
'}';
}
}
public abstract class AbstractBookBuilder {
public abstract void binding();
public abstract Book create();
}
public class HardbackBuilder extends AbstractBookBuilder {
private Book book = new Book();
public HardbackBuilder(String paper) {
book.setPaper(paper);
}
@Override
public void binding() {
book.setBinding("精装");
}
@Override
public Book create() {
return book;
}
}
public class PaperbackBuilder extends AbstractBookBuilder {
private Book book = new Book();
public PaperbackBuilder(String paper) {
book.setPaper(paper);
}
@Override
public void binding() {
book.setBinding("平装");
}
@Override
public Book create() {
return book;
}
}
public class Director {
AbstractBookBuilder bookBuilder;
public Director(AbstractBookBuilder bookBuilder) {
this.bookBuilder = bookBuilder;
}
public void construct() {
bookBuilder.binding();
}
}
Director统一组装的过程
public class Director {
AbstractBookBuilder bookBuilder;
public Director(AbstractBookBuilder bookBuilder) {
this.bookBuilder = bookBuilder;
}
public void construct() {
bookBuilder.binding();
}
}
public static void main(String[] args) {
AbstractBookBuilder hardbackBuilder = new HardbackBuilder("A级纸张");
Director director = new Director(hardbackBuilder);
director.construct();
System.out.println(hardbackBuilder.create());
AbstractBookBuilder paperbackBuilder = new PaperbackBuilder("B级纸张");
Director director2 = new Director(paperbackBuilder);
director2.construct();
System.out.println(paperbackBuilder.create());
}
Book{paper='A级纸张', binding='精装'}
Book{paper='B级纸张', binding='平装'}
2.2 builder模式的变种
public class Book2 {
private String paper; // 纸张
private String binding; // 装订
public Book2(String paper, String binding) {
this.paper = paper;
this.binding = binding;
}
@Override
public String toString() {
return "Book{" +
"paper='" + paper + '\'' +
", binding='" + binding + '\'' +
'}';
}
static class Book2Builder {
private String paper;
private String binding;
public Book2Builder setPaper(String paper) {
this.paper = paper;
return this;
}
public Book2Builder setBinding(String binding) {
this.binding = binding;
return this;
}
public Book2 createBook2() {
return new Book2(paper, binding);
}
}
}
测试
public static void main(String[] args) {
Book2 book2 = new Book2.Book2Builder()
.setBinding("精装")
.setPaper("1级")
.createBook2();
System.out.println(book2.toString());
}
输出
Book{paper='1级', binding='精装'}
netty中的builder模式
用过netty的同学应该知道,这里build方法实现服务端绑定端口,监听网络的功能。
b.group().channel().option()… 这就是builder模式的体现。
private void build(int port) throws InterruptedException {
// 配置服务端的NIO线程组
// EventLoopGroup 是个线程组,包含了一组NIO线程,专门用于网络事件处理,实际上它们就是Reactor线程组。
// 一个用于服务端接收客户端的连接,另一个用于SocketChannel的网络读写
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChildChannelHandler());
// 绑定端口,同步等待成功
ChannelFuture f = b.bind(port).sync();
// 等待服务器监听端口关闭
f.channel().closeFuture().sync();
} finally {
// 退出,释放线程池资源
bossGroup.shutdownGracefully();
workGroup.shutdownGracefully();
}
}
由于builder模式实现比较简单,我们这里不对netty源码做分析。我们只分析为什么他要用builder模式。
ServerBootstrap 这个是专门用于引导服务端的启动工作,而服务启动需要太多,且很多参数可选可不选。 如果我们直接用构造函数传参的方式,容易写错,可读性查等等。如果调用set方法一个个塞,似乎代码看着比较繁琐,也不是特别雅观。所以这里使用builder是比较合适的。
3、思考
其实开放了setter破坏了对象的不可变性(如果需要实现一个不可变对象):
effective java中有这么一段话:
遗憾的是,JavaBeans模式自身有着很严重的缺点。因为构造过程被分到了几个调用中,在构造过程中JavaBean可能处于不一致的状态。类无法仅仅通过检验构造器参数的有效性来保证一致性。试图使用处于不一致状态的对象,将会导致失败,这种失败与包含错误的代码大相径庭,因此它调试起来十分困难。与此相关的另一点不足在于,JavaBeans模式阻止了把类做成不可变的可能(见第15条),这就需要程序员付出额外的努力来确保它的线程安全。
就是说,开放setter破坏了对象的不可变性。而我们使用builder模式,是set完之后调用构造方法去构建对象。对象本身不需要开放set方法,使对象保持不可变性。
另外,Intellij IDEA可以为bean生成builder类,不用我们手打…比较方便
代码里面右击(需要有构造方法):Refactor -> Replace Constructor with Builder -> 点击Refactor