场景

场景1:
(1)构造一个复杂的对象,包含很多的属性,有些属性构造的时候需要做一些校验,格式转换等可能各个部分经常面临着剧烈的变化。
(2)比如:一个对象里面有很多属性:field1,field2,field3,... 我们需要构造这个大对象。
场景2:
(1) 电商项目中,商品中心的商品,采购中心的采购单,仓储中心的:采购入库单,销售出库单,退货入库单;订单中心的订单 等都比较复杂,可以使用构造器模式

内容

1. 不使用设计模式

1.1 类图

image.png

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 类图

image.png

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 优化后的构造器模式

  1. 省略Director,每次构建完对象的一个属性之后返回目标对象,直到属性构建完毕。
  2. Builder属性设置返回目标对象:Builder field3(String field3);
2.2.1 类图

image.png

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.总结

  1. 优化的构造器模式在开源软件代码里面大量使用:比如:SpringSecurity里面的:WebSecurityConfigurerAdapter的org.springframework.security.config.annotation.web.builders.HttpSecurity我们一般调用如下:

image.png

@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();//都需要认证
 }
}
  1. 去肯德基,汉堡、可乐、薯条、炸鸡翅等是不变的,而其组合是经常变化的,生成出所谓的"套餐"。
  2. JAVA 中的 StringBuilder。

3.1 不使用设计模式对比

  1. 对于一些有几十个字段,甚至是上百个字段的复杂对象的构建

不使用设计模式上面那段代码会极度膨胀,非常复杂。

  1. 一个是说,大量代码堆积在一起,维护性非常差,可读性非常差,读不懂,没法改。
  2. 另外一个,就是说,这段逻辑,如果在多个地方都有使用的话,一旦这段逻辑出现了一些变化,那么可能就需要在多个地方修改这一大坨跟屎一样的代码;把不同的构造的步骤,抽取成某一个方法

3.2 使用一般builder模式

  1. 好处1:通过builder接口将复杂构建步骤拆分成了多个部分,代码逻辑清晰,维护性和扩展性都很好
  2. 好处2:将对象构建的过程,封装在了director里面,director来基于builder进行构建,构建逻辑修改,不需要修改很多地方
  3. 好处3:相对于工厂,有一个很好的抽象设计,director和builder

3.3 使用一般builder模式

  1. 这种模式下去除了Director,每次构建完一个属性之后会返回builder.基于Builder最后返回目标对象。

startshineye
94 声望26 粉丝

我在规定的时间内,做到了我计划的事情;我自己也变得自信了,对于外界的人跟困难也更加从容了,我已经很强大了。可是如果我在规定时间内,我只有3分钟热度,哎,我不行,我就放弃了,那么这个就是我自己的问题,因为你自己...