设计模式之构造器模式:封装复杂对象的构造逻辑

场景

场景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 优化后的构造器模式
  1. 省略Director,每次构建完对象的一个属性之后返回目标对象,直到属性构建完毕。
  2. 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.总结

  1. 优化的构造器模式在开源软件代码里面大量使用:比如: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();//都需要认证
 }
}
  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最后返回目标对象。
相关推荐
枫叶林FYL2 分钟前
【强化学习】2 大规模并行强化学习中的耦合策略优化:受控多样性驱动的样本高效探索
开发语言·php
chao1898444 分钟前
基于MATLAB的音频信号AM调制与解调实现
开发语言·matlab·音视频
雨落在了我的手上6 分钟前
初识java(八):数组的定义与使用
java·开发语言
贵州晓智信息科技6 分钟前
曼德勃罗集的 Three.js 实现
开发语言·javascript·ecmascript
xiaoshuaishuai87 分钟前
C# CUDA 到 OpenCL 迁移
开发语言·windows·c#
asdfg12589637 分钟前
一文理解“架构思维”
java·软件工程·软件开发·架构思维
AI科技星8 分钟前
基于平行素数对等腰梯形网格拓扑的完备性证明哥德巴赫猜想1+1
c语言·开发语言·网络·量子计算·agi
聆风吟º8 分钟前
【C标准库】深入理解C语言 isdigit函数详解:判断字符是否为数字
c语言·开发语言·库函数·isdigit
故事和你9110 分钟前
洛谷-【图论2-4】连通性问题1
开发语言·数据结构·c++·算法·动态规划·图论
RSCompany11 分钟前
Frida 17 以后 Python API 跑旧版 JS 报 Java is not defined ?一行 import 直接恢复 Frida 16 体验
开发语言·python·逆向·hook·frida·android逆向·frida17