设计模式:Builder模式 - 实现及netty中的builder模式

学习设计模式不光要学习设计模式的思想,还要去深入理解,为什么要用这个设计模式。
如何深入理解?读优秀的框架代码,看别人代码,了解它们的使用场景。 - - - 博主老师(感谢他)

本文先介绍了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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值