Java设计模式实战-Builder
来源:互联网 发布:ip网络广播控制中心 编辑:程序博客网 时间:2024/05/22 13:18
1.使用场景
Builder模式主要用于复杂对象(构造方法的参数过多)的创建、对象构造参数可选配置、创建不可变对象。实际开发多用于开源框架中配置对象的创建。详见 3.范例演示
下面首先介绍为啥要用Builder模式创建对象?
>>使用共有构造方法的问题:
- 不能灵活控制可选构造参数,使用包含所有参数的构造方法,不需要的参数也必须传,同时容易出现参数错位,不很容易发现。(构造方法重载可解决)
- 通过重载的构造方法可定制不同参数的构造方法,但可读性差,因为构造方法名完全相同,缺乏语义性,很难区别,只能根据参数列表选择。(使用JavaBean方式可解决)
>>使用JavaBean设置参数的问题:
- 通过set方法可灵活设置参数,可读性不错,但参数设置分为几次调用,不能保证对象状态的一致性,需要人为处理额外努力保证线程安全。
- public set方法阻止把类做成不可变类的可能。我们可能希望一个对象创建后不能被修改
>>Builder模式创建对象好处?
- 拥有JavaBean模式的可读性,可选设置对象参数,可以实现链式调用,代码简洁
- 拥有重载构造方法创建对象的安全性
- 构造参数校验和对象创建分离,创建对象前可完成参数校验
2.使用方式
- 不直接生成想要的对象,通过必要参数调用构造器,返回一个构造器对象Builder(此构造器对象拥有和目标对象的相同的成员变量)
- 通过构造器的不同参数的set方法,设置参数给构造器
- 最后通过Builder.build()方法,返回目标对象的实例,完成对象创建。
//设置远程过程调用Rpc请求的参数 RpcRequest request = new RpcRequest.Build() .interfaceName(method.getDeclaringClass().getName()) .serviceVersion("0.0.1") .methodName(method.getName()) .parameterTypes(method.getParameterTypes()) .parameters(args).build();
3.范例演示
3.1 RpcRequest
场景描述:RpcRequest对象封装rpc(远程过程调用)的调用请求信息,包含远程服务的接口名、服务版本号、请求调用方法、请求参数类型、请求参数信息。RpcRequest对象的构造参数较多,包含一些可选参数,比如请求超时timeout信息,另外未来可能增加其他请求参数。同时请求对象一旦创建,对象信息不希望被修改。因此通过Builder构建者模式,为其创建对象实例。
package com.myron.netty.rpc;import java.io.Serializable;import java.util.UUID;/** * 封装 RPC 请求 * @author lin.r.x * */public class RpcRequest implements Serializable { private static final long serialVersionUID = 1L; private String requestId; private String interfaceName; private String serviceVersion; private String methodName; private Class<?>[] parameterTypes; private Object[] parameters; public RpcRequest(Builder build) { this.requestId = build.requestId; this.interfaceName = build.interfaceName; this.serviceVersion = build.serviceVersion; this.methodName = build.methodName; this.parameterTypes = build.parameterTypes; this.parameters = build.parameters; } // 目标对象只有get方法无set方法,保证对象创建后,私有成员变量不被修改 public String getRequestId() { return requestId; } public String getInterfaceName() { return interfaceName; } public String getServiceVersion() { return serviceVersion; } public String getMethodName() { return methodName; } public Class<?>[] getParameterTypes() { return parameterTypes; } public Object[] getParameters() { return parameters; } //RpcRequest解决Request参数过多,未来会添加新的参数,引入Build模式 public static class Builder { private String requestId; private String interfaceName; private String serviceVersion; private String methodName; private Class<?>[] parameterTypes; private Object[] parameters; // 默认Build构造方法,用于初始化默认参数 public Builder() { this.requestId = UUID.randomUUID().toString().replace("-", ""); } // 带参数的Build构造方法,用于初始化必选参数 public Builder(String requestId) { this.requestId = requestId; } // 传递构造器对象给目标类的构造方法,返回目标对象实例 public RpcRequest build() { RpcRequest request = new RpcRequest(this); return request; } // 设置参数,并返回构造器本身,用于链式调用设置可选参数 public Builder interfaceName(String interfaceName) { this.interfaceName = interfaceName; return this; } public Builder serviceVersion(String serviceVersion) { this.serviceVersion = serviceVersion; return this; } public Builder methodName(String methodName) { this.methodName = methodName; return this; } public Builder parameterTypes(Class<?>[] parameterTypes) { this.parameterTypes = parameterTypes; return this; } public Builder parameters(Object[] parameters) { this.parameters = parameters; return this; } }}
使用Builder创建RpcRequest实例
//设置远程过程调用Rpc请求的参数 RpcRequest request = new RpcRequest.Build() .interfaceName(method.getDeclaringClass().getName()) .serviceVersion("0.0.1") .methodName(method.getName()) .parameterTypes(method.getParameterTypes()) .parameters(args).build();
说明:以上对象创建的构造器,是通过类的静态内部类来实现,将构造器封装在类内部,这种方式使用Builder,使得代码冗长,在实际开发中也有将构造器独立于外部类,下面通过开源框架netty和strom,两个例子了解Builder设计模式运用。
3.2 netty中运用场景
netty 是提供nio的网络通信框架,这里主要关注Netty服务端启动需要先创建服务器启动辅助类ServerBootstrap,它提供了一系列的方法用于设置服务器端启动相关的参数。然而在创建ServerBootstrap实例时,发现ServerBootstrap只有一个无参的构造函数,事实上它需要与多个其它组件或者类交互。ServerBootstrap构造函数没有参数的原因是因为它的参数太多了,Netty创造者为了解决这个问题,就引入了Builder模式。
package com.myron.netty;import io.netty.bootstrap.ServerBootstrap;import io.netty.channel.ChannelFuture;import io.netty.channel.ChannelInitializer;import io.netty.channel.ChannelOption;import io.netty.channel.EventLoopGroup;import io.netty.channel.nio.NioEventLoopGroup;import io.netty.channel.socket.SocketChannel;import io.netty.channel.socket.nio.NioServerSocketChannel;public class TimeServer { public void bind(int port) { //配置服务端的NIO线程组 EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG, 1024) //绑定I/O事件的处理类主要用于处理网络I/O事件,例如记录日志、对消息进行编解码 .childHandler(new ChildChannelHandler()); try { //绑定端口,同步等待成功 ChannelFuture f = b.bind(port).sync(); //等待服务端监听端口关闭 f.channel().closeFuture().sync();//方法阻塞等待服务端链路关闭之后main函数才退出 } catch (InterruptedException e) { e.printStackTrace(); } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } private class ChildChannelHandler extends ChannelInitializer<SocketChannel> { @Override protected void initChannel(SocketChannel channel) throws Exception { channel.pipeline().addLast(new TimeServerHandler()); } } public static void main(String[] args) { int port = 8080; if (args != null && args.length > 0) { try { port = Integer.parseInt(args[0]); } catch (NumberFormatException e) { // 使用默认值 } } new TimeServer().bind(port); }}
3.3 storm中运用场景
storm是一个流式实时计算的框架,主要用于大数据领域的实时计算。storm中有个重要的对象,Topology拓扑对象,用于描述实时计算任务中数据流的拓扑节点结构。计算拓扑 根据具体业务需要创建,因此非常适合Builder模式构建对象
演示计算wordcount的拓扑对象创建代码
package com.myron.storm.wordcount.topology;import org.apache.storm.Config;import org.apache.storm.LocalCluster;import org.apache.storm.topology.TopologyBuilder;import org.apache.storm.tuple.Fields;import com.myron.storm.wordcount.bolt.ReportBolt;import com.myron.storm.wordcount.bolt.SplitSentenceBolt;import com.myron.storm.wordcount.bolt.WordCountBolt;import com.myron.storm.wordcount.spout.SentenceSpout;/** * Topology定义计算所需要的spout和bolt * * @author Administrator * */public class WordCountTopology { private static final String SENTENCE_SPOUT_ID = "sentence-spout"; private static final String SPLIT_BOLT_ID = "split-bolt"; private static final String COUNT_BOLT_ID = "count-bolt"; private static final String REPORT_BOLT_ID = "report-bolt"; private static final String TOPOLOGY_NAME = "topology-bolt"; public static void main(String[] args) throws Exception { //实例化spout和bolt SentenceSpout spout = new SentenceSpout(); SplitSentenceBolt splitBolt = new SplitSentenceBolt(); WordCountBolt countBolt = new WordCountBolt(); ReportBolt reportBolt = new ReportBolt(); //TopologyBuilder提供流式接口风格API定义topology组件之间的数据流 TopologyBuilder builder = new TopologyBuilder(); builder.setSpout(SENTENCE_SPOUT_ID, spout); // SentenceSpout --> SplitSentenceBolt // BoltDeclarer.shuffleGrouping():告诉Storm,sentenceSpout 发射的tuple随机均匀分发给SplitBolt builder.setBolt(SPLIT_BOLT_ID, splitBolt).shuffleGrouping(SENTENCE_SPOUT_ID); // SplitSentenceBolt --> WordCountBolt // BoltDeclarer.fieldsGrouping():告诉Storm,所有"word"字段值的tuple被路由到同一个WordCountBolt builder.setBolt(COUNT_BOLT_ID, countBolt).fieldsGrouping(SPLIT_BOLT_ID, new Fields("word")); // WordCountBolt --> ReportBolt // BoltDeclarer.globalGrouping():告诉Storm,所有的WordCountBolt 路由到唯一ReportBolt任务中 builder.setBolt(REPORT_BOLT_ID, reportBolt).globalGrouping(COUNT_BOLT_ID); // Config对象代表了对topology所有组件全局生效的配置参数集合 Config config = new Config(); //Storm本地模式模拟一个完整的集群 LocalCluster cluster = new LocalCluster(); //提交拓扑到集群 cluster.submitTopology(TOPOLOGY_NAME, config, builder.createTopology()); Thread.sleep(50000); cluster.killTopology(TOPOLOGY_NAME); cluster.shutdown(); }}
4.原理分析
Builder模式构建复杂对象,同时拥有的公共构造方法和JavaBean方式(成员变量setter方法)优点,其实不难发现,Builder模式本质其实只是把上面两种方式组合起来,通过Builder对象的setter方法可选接受参数,调用Builder.build()方法时,将Builder对象接收到客户端参数,一次性调用目标类的带有全部参数的构造方法,并返回创建对象。这种通过第三方辅助类来协助处理,在其他设计模式也很常见。
最后想说,Builder模式增加Builder对象创建的开销,也增加代码量,因此在创建简单对象时,没必要使用。深刻理解每个设计模式针对的场景,比硬记重要。另外了解在开源产品中使用,可以大大加深理解,看看大咖是怎么用的。
- Java设计模式实战-Builder
- JAVA设计模式-Builder
- java设计模式-Builder
- Android 设计模式实战 Builder模式
- java设计模式:Builder模式
- Java设计模式---Builder模式
- Java设计模式--Builder模式
- Java设计模式--builder模式
- 读Android Builder设计模式后实战
- Java设计模式—Builder
- java设计模式4:builder
- Java:设计模式之Builder
- java设计模式--Builder设计模式
- 设计模式解析与实战之Builder模式
- Java设计模式:Builder(建设者模式)
- Java设计模式-----Builder建造者模式
- Java设计模式-----Builder建造者模式
- java设计模式 之生成器模式 Builder
- 网络编程学习之——TCP通信协议
- pthread_once()
- poj2299 B
- NSF服务 -- 文件共享 (windows挂载centos)
- poj2559 Largest Rectangle in a Histogram (单调栈)
- Java设计模式实战-Builder
- MyBatis实现简单的用户查询
- 散乱知识点1
- 没有临时变量的情况下,交换两个值。
- SWD调试与ULINK2的对应接线
- 利用 vue-cli 构建一个 Vue 项目
- web10天mysql
- STM32成长记之USART--232串口通信
- DateUtils工具类