场景
场景1:
(1)构造一个复杂的对象,包含很多的属性,有些属性构造的时候需要做一些校验,格式转换等可能各个部分经常面临着剧烈的变化。
(2)比如:一个对象里面有很多属性:field1,field2,field3,... 我们需要构造这个大对象。
场景2:
(1) 电商项目中,商品中心的商品,采购中心的采购单,仓储中心的:采购入库单,销售出库单,退货入库单;订单中心的订单 等都比较复杂,可以使用构造器模式
内容
1. 不使用设计模式
1.1 类图
1.2 代码
public class NonPattern {
public static void main(String[] args) {
// 构造这个复杂的product对象
Product product = new Product();
// 设置field1属性
System.out.println("在设置field1之前进行复杂的校验逻辑");
product.setField1("值1");
// 设置field2属性
System.out.println("在设置field2之前进行复杂的数据格式转化逻辑");
product.setField2("值2");
// 设置field3属性
System.out.println("在设置field3之前进行复杂的数据处理逻辑,跟其他对象的数据进行关联");
product.setField3("值3");
}
//============需要构造的大对象==============
public static class Product{
private String field1;
private String field2;
private String field3;
public String getField1() {
return field1;
}
public void setField1(String field1) {
this.field1 = field1;
}
public String getField2() {
return field2;
}
public void setField2(String field2) {
this.field2 = field2;
}
public String getField3() {
return field3;
}
public void setField3(String field3) {
this.field3 = field3;
}
@Override
public String toString() {
return "Product{" +
"field1='" + field1 + '\'' +
", field2='" + field2 + '\'' +
", field3='" + field3 + '\'' +
'}';
}
}
}
2.使用设计模式
2.1 一般builder模式
一般builder模式:
- 1.创建一个引导类:Director;然后让引导类依赖Builder;Director属性为Builder,然后让Builder去创建实例
2.1.1 类图
2.1.2 代码
public class BuilderPattern2 {
public static void main(String[] args) {
//1.创建引导类
Director director = new Director(new ConcreteBuilder());
//2.构建器模式构造对象
Product product = director.build("value1", "value2", "value3");
System.out.println(product);
/**
* 输出:
* 在设置field1之前进行复杂的校验逻辑
* 在设置field2之前进行复杂的数据格式转化逻辑
* 在设置field3之前进行复杂的数据处理逻辑,跟其他对象的数据进行关联
* Product{field1='value1', field2='value2', field3='value3'}
*/
}
//==================引导类====================
public static class Director{
private Builder builder;
public Director(Builder builder){
this.builder = builder;
}
Product build(String field1,String field2,String field3){
builder.field1(field1);
builder.field2(field2);
builder.field3(field3);
return builder.create();
}
}
//==================构造器=================
//因为构造器会被引导类引用,我们使用面向接口依赖编程
public interface Builder{
void field1(String field1);
void field2(String field2);
void field3(String field3);
Product create();
}
public static class ConcreteBuilder implements Builder{
private Product product = new Product();
public void field1(String field1) {
System.out.println("在设置field1之前进行复杂的校验逻辑");
product.setField1(field1);
}
public void field2(String field2) {
System.out.println("在设置field2之前进行复杂的数据格式转化逻辑");
product.setField2(field2);
}
public void field3(String field3) {
System.out.println("在设置field3之前进行复杂的数据处理逻辑,跟其他对象的数据进行关联");
product.setField3(field3);
}
public Product create() {
return product;
}
}
//==================商品类============
public static class Product{
private String field1;
private String field2;
private String field3;
public String getField1() {
return field1;
}
public void setField1(String field1) {
this.field1 = field1;
}
public String getField2() {
return field2;
}
public void setField2(String field2) {
this.field2 = field2;
}
public String getField3() {
return field3;
}
public void setField3(String field3) {
this.field3 = field3;
}
@Override
public String toString() {
return "Product{" +
"field1='" + field1 + '\'' +
", field2='" + field2 + '\'' +
", field3='" + field3 + '\'' +
'}';
}
}
}
2.2 优化后的构造器模式
- 省略Director,每次构建完对象的一个属性之后返回目标对象,直到属性构建完毕。
- Builder属性设置返回目标对象:Builder field3(String field3);
2.2.1 类图
2.2.2 代码
public class OptimizedBuilderPattern2 {
public static void main(String[] args) {
Builder builder = new ConcreteBuilder();
Product product = builder.field1("value1")
.field2("value2")
.field3("value3")
.create();
System.out.println(product);
/**
* 输出:
* 在设置field1之前进行复杂的校验逻辑
* 在设置field2之前进行复杂的数据格式转化逻辑
* 在设置field3之前进行复杂的数据处理逻辑,跟其他对象的数据进行关联
* Product{field1='value1', field2='value2', field3='value3'}
*/
}
//==================构造器:Builder============
public interface Builder{
Builder field1(String field1);
Builder field2(String field2);
Builder field3(String field3);
Product create();
}
public static class ConcreteBuilder implements Builder{
private Product product = new Product();
public Builder field1(String field1) {
System.out.println("在设置field1之前进行复杂的校验逻辑");
product.setField1(field1);
return this;
}
public Builder field2(String field2) {
System.out.println("在设置field2之前进行复杂的数据格式转化逻辑");
product.setField2(field2);
return this;
}
public Builder field3(String field3) {
System.out.println("在设置field3之前进行复杂的数据处理逻辑,跟其他对象的数据进行关联");
product.setField3(field3);
return this;
}
public Product create() {
return product;
}
}
//==================商品类============
public static class Product{
private String field1;
private String field2;
private String field3;
public String getField1() {
return field1;
}
public void setField1(String field1) {
this.field1 = field1;
}
public String getField2() {
return field2;
}
public void setField2(String field2) {
this.field2 = field2;
}
public String getField3() {
return field3;
}
public void setField3(String field3) {
this.field3 = field3;
}
@Override
public String toString() {
return "Product{" +
"field1='" + field1 + '\'' +
", field2='" + field2 + '\'' +
", field3='" + field3 + '\'' +
'}';
}
}
}
3.开源项目使用构造器模式
1、Curator客户端创建可客户端实例:
CuratorFramework client = CuratorFrameworkFactory.newClient(
"localhost:2181",//zk地址,如果是集群,这里用逗号隔开的多个地址
5000,//客户端跟zk会进行心跳,如果心跳断开后超过这个时间,session就销毁了
3000,//客户端连接zk时候连接超时时间
retryPolicy);//重试策略
底层源码:基于工厂:CuratorFrameworkFactory的静态方法builder():
public static CuratorFramework newClient(String connectString, int sessionTimeoutMs, int connectionTimeoutMs, RetryPolicy retryPolicy) {
return builder().connectString(connectString).sessionTimeoutMs(sessionTimeoutMs).connectionTimeoutMs(connectionTimeoutMs).retryPolicy(retryPolicy).build();
}
构造静态内部类Builder;进而通过build()构建一个CuratorFramework。
public static class Builder {
private EnsembleProvider ensembleProvider;
private int sessionTimeoutMs;
private int connectionTimeoutMs;
private RetryPolicy retryPolicy;
private ThreadFactory threadFactory;
private String namespace;
private String authScheme;
private byte[] authValue;
private byte[] defaultData;
private CompressionProvider compressionProvi~~~~der;
private ZookeeperFactory zookeeperFactory;
private ACLProvider aclProvider;
private boolean canBeReadOnly;
public CuratorFramework build() {
return new CuratorFrameworkImpl(this);
}
...
public CuratorFrameworkFactory.Builder connectString(String connectString) {
this.ensembleProvider = new FixedEnsembleProvider(connectString);
return this;
}
public CuratorFrameworkFactory.Builder sessionTimeoutMs(int sessionTimeoutMs) {
this.sessionTimeoutMs = sessionTimeoutMs;
return this; }
public CuratorFrameworkFactory.Builder connectionTimeoutMs(int connectionTimeoutMs) {
this.connectionTimeoutMs = connectionTimeoutMs;
return this; }
public CuratorFrameworkFactory.Builder retryPolicy(RetryPolicy retryPolicy) {
this.retryPolicy = retryPolicy;
return this; }
private Builder() {
this.sessionTimeoutMs = CuratorFrameworkFactory.DEFAULT_SESSION_TIMEOUT_MS;
this.connectionTimeoutMs = CuratorFrameworkFactory.DEFAULT_CONNECTION_TIMEOUT_MS;
this.threadFactory = null;
this.authScheme = null;
this.authValue = null;
this.defaultData = CuratorFrameworkFactory.LOCAL_ADDRESS;
this.compressionProvider = CuratorFrameworkFactory.DEFAULT_COMPRESSION_PROVIDER;
this.zookeeperFactory = CuratorFrameworkFactory.DEFAULT_ZOOKEEPER_FACTORY;
this.aclProvider = CuratorFrameworkFactory.DEFAULT_ACL_PROVIDER;
this.canBeReadOnly = false;
}
}
2、curator里面的create方法一连串调用:
//3、创建数据节点:znode
client.create()
.creatingParentsIfNeeded()//设置如果节点父节点不存在 创建节点
.withMode(CreateMode.PERSISTENT) //创建节点类型:可以是持久化节点、临时节点、顺序节点
.forPath("/my/path","ZookeeperCrudDemo".getBytes());
4.总结
- 优化的构造器模式在开源软件代码里面大量使用:比如:SpringSecurity里面的:WebSecurityConfigurerAdapter的org.springframework.security.config.annotation.web.builders.HttpSecurity我们一般调用如下:
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private SecurityProperties securityProperties;
@Autowired
private MyAuthenticationSuccessHandler myAuthenticationSuccessHandler;
@Autowired
private MyAuthenticationFailureHandler myAuthenticationFailureHandler;
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
/**
* 定义web安全配置类:覆盖config方法
* 1.参数为HttpSecurity
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
/**
* 定义了任何请求都需要表单认证
*/
ValidateCodeFilter validateCodeFilter \= new ValidateCodeFilter();
validateCodeFilter.setAuthenticationFailureHandler(myAuthenticationFailureHandler);
http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)//自定义的额过滤器加到UsernamePasswordAuthenticationFilter前面去
.formLogin()//表单登录---指定了身份认证方式
// .loginPage("/login.html")
.loginPage("/authentication/require")
.loginProcessingUrl("/authentication/form")//配置UsernamePasswordAuthenticationFilter需要拦截的请求
.successHandler(myAuthenticationSuccessHandler)//表单登录成功之后用自带的处理器
.failureHandler(myAuthenticationFailureHandler)//表单登录失败之后用自带的处理器
// http.httpBasic()//http的basic登录
.and()
.authorizeRequests()//对请求进行授权
.antMatchers("/authentication/require",securityProperties.getBrowser().getLoginPage(),"/code/image").permitAll()//对匹配login.html的请求允许访问
.anyRequest()//任何请求
.authenticated()
.and()
.csrf().disable();//都需要认证
}
}
- 去肯德基,汉堡、可乐、薯条、炸鸡翅等是不变的,而其组合是经常变化的,生成出所谓的"套餐"。
- JAVA 中的 StringBuilder。
3.1 不使用设计模式对比
- 对于一些有几十个字段,甚至是上百个字段的复杂对象的构建
不使用设计模式上面那段代码会极度膨胀,非常复杂。
- 一个是说,大量代码堆积在一起,维护性非常差,可读性非常差,读不懂,没法改。
- 另外一个,就是说,这段逻辑,如果在多个地方都有使用的话,一旦这段逻辑出现了一些变化,那么可能就需要在多个地方修改这一大坨跟屎一样的代码;把不同的构造的步骤,抽取成某一个方法
3.2 使用一般builder模式
- 好处1:通过builder接口将复杂构建步骤拆分成了多个部分,代码逻辑清晰,维护性和扩展性都很好
- 好处2:将对象构建的过程,封装在了director里面,director来基于builder进行构建,构建逻辑修改,不需要修改很多地方
- 好处3:相对于工厂,有一个很好的抽象设计,director和builder
3.3 使用一般builder模式
- 这种模式下去除了Director,每次构建完一个属性之后会返回builder.基于Builder最后返回目标对象。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。