builder_builderexception - CSDN
精华内容
参与话题
  • lombok 下的@Builder注解用法

    万次阅读 2018-05-25 09:27:37
    pom依赖 <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version&.../

    pom依赖 

    <dependency>

                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>0.10.2</version>

       </dependency>

    java~lombok里的Builder注解

    lombok注解在java进行编译时进行代码的构建,对于java对象的创建工作它可以更优雅,不需要写多余的重复的代码,这对于JAVA开发人员是很重要的,在出现lombok之后,对象的创建工作更提供Builder方法,它提供在设计数据实体时,对外保持private setter,而对属性的赋值采用Builder的方式,这种方式最优雅,也更符合封装的原则,不对外公开属性的写操作!

    @Builder声明实体,表示可以进行Builder方式初始化,@Value注解,表示只公开getter,对所有属性的setter都封闭,即private修饰,所以它不能和@Builder现起用

    一般地,我们可以这样设计实体!

    复制代码
    @Builder(toBuilder = true)
    @Getter
    public class UserInfo {
      private String name;
      private String email;
      @MinMoney(message = "金额不能小于0.")
      @MaxMoney(value = 10, message = "金额不能大于10.")
      private Money price;
    
    }
    复制代码

    @Builder注解赋值新对象

    UserInfo userInfo = UserInfo.builder()
            .name("zzl")
            .email("bgood@sina.com")
            .build();

    @Builder注解修改原对象的属性值

    修改实体,要求实体上添加@Builder(toBuilder=true)

     userInfo = userInfo.toBuilder()
            .name("OK")
            .email("zgood@sina.com")
            .build();

    展开全文
  • 详解Lombok中的@Builder用法

    千次阅读 2020-03-04 14:58:35
    简述:Builder 使用创建者模式又叫建造者模式。简单来说,就是一步步创建一个对象,它对用户屏蔽了里面构建的细节,但却可以精细地控制对象的构造过程。 基础使用 @Builder注释为你的类生成相对略微复杂的构建器...

    https://www.jianshu.com/p/d08e255312f9

    简述:Builder 使用创建者模式又叫建造者模式。简单来说,就是一步步创建一个对象,它对用户屏蔽了里面构建的细节,但却可以精细地控制对象的构造过程。

    基础使用

    @Builder注释为你的类生成相对略微复杂的构建器API。@Builder可以让你以下面显示的那样调用你的代码,来初始化你的实例对象:

     

    Student.builder()
                   .sno( "001" )
                   .sname( "admin" )
                   .sage( 18 )
                   .sphone( "110" )
                   .build();
    

    @Builder可以放在类,构造函数或方法上。 虽然放在类上和放在构造函数上这两种模式是最常见的用例,但@Builder最容易用放在方法的用例来解释。

    那么@Builder内部帮我们做了什么?

    1. 创建一个名为ThisClassBuilder的内部静态类,并具有和实体类形同的属性(称为构建器)。
    2. 在构建器中:对于目标类中的所有的属性和未初始化的final字段,都会在构建器中创建对应属性。
    3. 在构建器中:创建一个无参的default构造函数。
    4. 在构建器中:对于实体类中的每个参数,都会对应创建类似于setter的方法,只不过方法名与该参数名相同。 并且返回值是构建器本身(便于链式调用),如上例所示。
    5. 在构建器中:一个build()方法,调用此方法,就会根据设置的值进行创建实体对象。
    6. 在构建器中:同时也会生成一个toString()方法。
    7. 在实体类中:会创建一个builder()方法,它的目的是用来创建构建器。

    说这么多,不如让我们通过下面这个例子来理解:

     

    @Builder
    public class User {
        private final Integer code = 200;
        private String username;
        private String password;
    }
    
    // 编译后:
    public class User {
        private String username;
        private String password;
        User(String username, String password) {
            this.username = username; this.password = password;
        }
        public static User.UserBuilder builder() {
            return new User.UserBuilder();
        }
    
        public static class UserBuilder {
            private String username;
            private String password;
            UserBuilder() {}
    
            public User.UserBuilder username(String username) {
                this.username = username;
                return this;
            }
            public User.UserBuilder password(String password) {
                this.password = password;
                return this;
            }
            public User build() {
                return new User(this.username, this.password);
            }
            public String toString() {
                return "User.UserBuilder(username=" + this.username + ", password=" + this.password + ")";
            }
        }
    }
    

    组合用法

    1. @Builder中使用 @Singular 注释集合

    @Builder也可以为集合类型的参数或字段生成一种特殊的方法。 它采用修改列表中一个元素而不是整个列表的方式,可以是增加一个元素,也可以是删除一个元素。

     

     Student.builder()
                    .sno( "001" )
                    .sname( "admin" )
                    .sage( 18 )
                    .sphone( "110" ).sphone( "112" )
                    .build();
    

    这样就可以轻松地将List <String>字段中包含2个字符串。 但是想要这样来操作集合,你需要使用@Singular来注释字段或参数。

    在使用@Singular注释注释一个集合字段(使用@Builder注释类),lombok会将该构建器节点视为一个集合,并生成两个adder方法而不是setter方法。

    • 一个向集合添加单个元素
    • 一个将另一个集合的所有元素添加到集合中

    将不生成仅设置集合(替换已添加的任何内容)的setter。 还生成了clear方法。 这些singular构建器相对而言是有些复杂的,主要是来保证以下特性:

    1. 在调用build()时,生成的集合将是不可变的。
    2. 在调用build()之后调用其中一个adder方法或clear方法不会修改任何已经生成的对象。如果对集合修改之后,再调用build(),则会创建一个基于上一个对象创建的对象实体。
    3. 生成的集合将被压缩到最小的可行格式,同时保持高效。

    @Singular只能应用于lombok已知的集合类型。目前,支持的类型有:

    • java.util:

      • Iterable, Collection, 和List (一般情况下,由压缩的不可修改的ArrayList支持).
      • Set, SortedSet, and NavigableSet (一般情况下,生成可变大小不可修改的HashSet或者TreeSet).
      • Map, SortedMap, and NavigableMap (一般情况下,生成可变大小不可修改的HashMap或者TreeMap).
    • Guava’s com.google.common.collect:

      • ImmutableCollection and ImmutableList
      • ImmutableSet and ImmutableSortedSet
      • ImmutableMap, ImmutableBiMap, and ImmutableSortedMap
      • ImmutableTable

    来看看使用了@Singular注解之后的编译情况:

     

    @Builder
    public class User {
        private final Integer id;
        private final String zipCode = "123456";
        private String username;
        private String password;
        @Singular
        private List<String> hobbies;
    }
    
    // 编译后:
    public class User {
        private final Integer id;
        private final String zipCode = "123456";
        private String username;
        private String password;
        private List<String> hobbies;
        User(Integer id, String username, String password, List<String> hobbies) {
            this.id = id; this.username = username;
            this.password = password; this.hobbies = hobbies;
        }
    
        public static User.UserBuilder builder() {return new User.UserBuilder();}
    
        public static class UserBuilder {
            private Integer id;
            private String username;
            private String password;
            private ArrayList<String> hobbies;
            UserBuilder() {}
            public User.UserBuilder id(Integer id) { this.id = id; return this; }
            public User.UserBuilder username(String username) { this.username = username; return this; }
            public User.UserBuilder password(String password) { this.password = password; return this; }
    
            public User.UserBuilder hobby(String hobby) {
                if (this.hobbies == null) {
                    this.hobbies = new ArrayList();
                }
                this.hobbies.add(hobby);
                return this;
            }
    
            public User.UserBuilder hobbies(Collection<? extends String> hobbies) {
                if (this.hobbies == null) {
                    this.hobbies = new ArrayList();
                }
                this.hobbies.addAll(hobbies);
                return this;
            }
    
            public User.UserBuilder clearHobbies() {
                if (this.hobbies != null) {
                    this.hobbies.clear();
                }
                return this;
            }
    
            public User build() {
                List hobbies;
                switch(this.hobbies == null ? 0 : this.hobbies.size()) {
                case 0:
                    hobbies = Collections.emptyList();
                    break;
                case 1:
                    hobbies = Collections.singletonList(this.hobbies.get(0));
                    break;
                default:
                    hobbies = Collections.unmodifiableList(new ArrayList(this.hobbies));
                }
                return new User(this.id, this.username, this.password, hobbies);
            }
            public String toString() {
                return "User.UserBuilder(id=" + this.id + ", username=" + this.username + ", password=" + this.password + ", hobbies=" + this.hobbies + ")";
            }
        }
    }
    

    其实,lombok的创作者还是很用心的,在进行build()来创建实例对象时,
    并没有直接使用Collections.unmodifiableList(Collection)此方法来床架实例,而是分为三种情况。

    • 第一种,当集合中没有元素时,创建一个空list
    • 第二种情况,当集合中存在一个元素时,创建一个不可变的单元素list
    • 第三种情况,根据当前集合的元素数量创建对应合适大小的list

    当然我们看编译生成的代码,创建了三个关于集合操作的方法:

    • hobby(String hobby):向集合中添加一个元素
    • hobbies(Collection<? extends String> hobbies):添加一个集合所有的元素
    • clearHobbies():清空当前集合数据

    2. @Singular 注解配置value属性

    我们先来看看 @Singular 注解的详情:

     

    @Target({FIELD, PARAMETER})
    @Retention(SOURCE)
    public @interface Singular {
        // 修改添加集合元素的方法名
        String value() default "";
    }
    
    • 测试如何使用注解属性value

     

    @Builder
    public class User {
        private final Integer id;
        private final String zipCode = "123456";
        private String username;
        private String password;
        @Singular(value = "testHobbies")
        private List<String> hobbies;
    }
    
    // 测试类
    public class BuilderTest {
        public static void main(String[] args) {
            User user = User.builder()
                    .testHobbies("reading")
                    .testHobbies("eat")
                    .id(1)
                    .password("admin")
                    .username("admin")
                    .build();
            System.out.println(user);
        }
    }
    

    说明,当我们使用了注解属性value之后,我们在使用添加集合元素时的方法名发生相应的改变。但是,同时生成的添加整个集合的方法名发生改变了吗?我们再来看看编译后的代码:

     

    / 编译后:
    public class User {
        // 省略部分代码,只看关键部分
        public static class UserBuilder {
            public User.UserBuilder testHobbies(String testHobbies) {
                if (this.hobbies == null) {
                    this.hobbies = new ArrayList();
                }
                this.hobbies.add(testHobbies);
                return this;
            }
    
            public User.UserBuilder hobbies(Collection<? extends String> hobbies) {
                if (this.hobbies == null) {
                    this.hobbies = new ArrayList();
                }
                this.hobbies.addAll(hobbies);
                return this;
            }
            
            public User.UserBuilder clearHobbies() {
                if (this.hobbies != null) {
                    this.hobbies.clear();
                }
                return this;
            }
        }
    }
    

    可以看到,只有添加一个元素的方法名发生了改变。

    3. @Builder.Default 的使用

    比如有这样一个实体类:

     

    @Builder
    @ToString
    public class User {
        @Builder.Default
        private final String id = UUID.randomUUID().toString();
        private String username;
        private String password;
        @Builder.Default
        private long insertTime = System.currentTimeMillis();
    }
    

    在类中我在idinsertTime上都添加注解@Builder.Default,当我在使用这个实体对象时,我就不需要在为这两个字段进行初始化值,如下面这样:

     

    public class BuilderTest {
        public static void main(String[] args) {
            User user = User.builder()
                    .password("admin")
                    .username("admin")
                    .build();
            System.out.println(user);
        }
    }
    
    // 输出内容:
    User(id=416219e1-bc64-43fd-b2c3-9f8dc109c2e8, username=admin, password=admin, insertTime=1546869309868)
    

    lombok在实例化对象时就为我们初始化了这两个字段值。

    当然,你如果再对这两个字段进行设值的话,那么默认定义的值将会被覆盖掉,如下面这样:

     

    public class BuilderTest {
        public static void main(String[] args) {
            User user = User.builder()
                    .id("admin")
                    .password("admin")
                    .username("admin")
                    .build();
            System.out.println(user);
        }
    }
    // 输出内容
    User(id=admin, username=admin, password=admin, insertTime=1546869642151)
    

    4. @Builder 详细配置

    下面我们再来详细看看@Builder这个注解类地详细实现:

     

    @Target({TYPE, METHOD, CONSTRUCTOR})
    @Retention(SOURCE)
    public @interface Builder {
        // 如果@Builder注解在类上,可以使用 @Builder.Default指定初始化表达式
        @Target(FIELD)
        @Retention(SOURCE)
        public @interface Default {}
        // 指定实体类中创建 Builder 的方法的名称,默认为: builder (个人觉得没必要修改)
        String builderMethodName() default "builder";
        // 指定 Builder 中用来构件实体类的方法的名称,默认为:build (个人觉得没必要修改)
        String buildMethodName() default "build";
        // 指定创建的建造者类的名称,默认为:实体类名+Builder
        String builderClassName() default "";
        // 使用toBuilder可以实现以一个实例为基础继续创建一个对象。(也就是重用原来对象的值)
        boolean toBuilder() default false;
        
        @Target({FIELD, PARAMETER})
        @Retention(SOURCE)
        public @interface ObtainVia {
            // 告诉lombok使用表达式获取值
            String field() default "";
            // 告诉lombok使用表达式获取值
            String method() default "";
    
            boolean isStatic() default false;
        }
    }
    

    以上注解属性,我只测试一个比较常用的toBuilder,因为我们在对实体对象进行操作时,往往会存在对某些实体对象的某个字段进行二次赋值,这个时候就会用到这一属性。但是,这会创建一个新的对象,而不是原来的对象,原来的对象属性是不可变的,除非你自己想要给这个实体类再添加上@Data或者@setter方法。下面就来测试一下:

     

    @Builder(toBuilder = true)
    @ToString
    public class User {
        private String username;
        private String password;
    }
    // 测试类
    public class BuilderTest {
        public static void main(String[] args) {
            User user1 = User.builder()
                    .password("admin")
                    .username("admin")
                    .build();
            System.out.println(user1);
    
            User user2 = user1.toBuilder().username("admin2").build();
            // 验证user2是否是基于user1的现有属性创建的
            System.out.println(user2);
            // 验证对象是否是同一对象
            System.out.println(user1 == user2);
        }
    }
    // 输出内容
    User(username=admin, password=admin)
    User(username=admin2, password=admin)
    false
    

    5. @Builder 全局配置

     

    # 是否禁止使用@Builder
    lombok.builder.flagUsage = [warning | error] (default: not set)
    # 是否使用Guaua
    lombok.singular.useGuava = [true | false] (default: false)
    # 是否自动使用singular,默认是使用
    lombok.singular.auto = [true | false] (default: true)
    
    • 总的来说@Builder还是很好用的。

    文章参考自:https://blog.csdn.net/weixin_41540822/article/details/86606562



    作者:虹猫日志
    链接:https://www.jianshu.com/p/d08e255312f9
    来源:简书
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    展开全文
  • 创建型设计模式之Builder模式

    千次阅读 2018-03-15 00:32:04
    1 Builder模式概念 1.1 介绍  Builder模式是一步一步创建一个复杂对象的创建型模式,它允许用户在不知道内部构建细节的情况下,可以更精细地控制对象的构造流程。该模式是为了将构建复杂对象的过程和它的部件...

    1 Builder模式概念

    1.1 介绍

      Builder模式是一步一步创建一个复杂对象的创建型模式,它允许用户在不知道内部构建细节的情况下,可以更精细地控制对象的构造流程。该模式是为了将构建复杂对象的过程和它的部件解耦,使得构建过程和部件的表示隔离开来。
      Builder模式,在于分工明确,一个抽象建造者,一个具体的建造者,一个指挥者,当然还需要具体的产品。那么我们以一个软件产品为例:技术主管就是抽象建造者,他和产品经理沟通,知道要做一个什么样的产品;而程序员就是具体的劳动者,技术主管说咋做你就咋做;而指挥者就是公司的产品经理,负责和用户沟通,了解客户的需求。

    1.2 定义

      将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

    1.3 使用场景

    • 相同的方法,不同的执行顺序,产生不同的事件结果时;
    • 多个部件或零件,都可以装配到一个对象中,但是产生的运行结果又不相同时;
    • 产品类非常复杂,或者产品类中的调用顺序不同产生了不同的效能,这个时候使用建造者模式非常合适;
    • 当初始化一个对象特别复杂,如参数多,且很多参数都具有默认值时。

    2 Builder模式UML类图通用

    角色介绍:

    • Product——产品类 : 产品的抽象类。
    • Builder——抽象类, 规范产品的组建,一般是由子类实现具体的组件过程。
    • ConcreteBuilder——具体的构建者。
    • Director——指挥者,统一组装过程(可省略)。

    这里写图片描述

    3 通用模式代码

    (1)产品类

    /**
     * 产品类
     */
    public class Product {
        public void doSomething() {
            // 独立业务处理
        }
    }

    (2)抽象建造者类

    /**
     * 抽象建造者
     * 如果有多个产品类就有几个具体的建造者,而且这多个产品类具有相同接口或抽象类
     */
    public abstract class Builder { 
        // setPart方法是零件的配置,设置产品的不同零件,或者不同的装配顺序,以产生不同的产品
        public abstract void setPart(); 
    
        // 建造产品
        public abstract Product buildProduct();
    }

    (3)具体建造者类

    /**
     * 具体建造者
     */
    public class ConcreteProduct extends Builder {
        private Product product = new Product(); 
    
        // 设置产品零件
        public void setPart() {
            // 产品类内的逻辑处理    
        }
    
        // 组建一个产品
        public Product buildProduct() {
            return product;
        }
    }

    (4)指挥者(导演)类

    /**
     * 指挥者类
     * 指挥者类起到封装的作用,避免高层模块深入到建造者内部的实现类
     */
    public class Director {
        // 构建不同的产品
        private Builder builder = new ConcreteProduct(); 
    
        public Product getAProduct() {
            builder.setPart();
    
            // 设置不同的零件,产生不同的产品
            return builder.buildProduct();
        }
    
    }

    4 Builder模式变种(常用)

    4.1 普通

    public class Person {
        private String name;
        private int age;
        private double height;
        private double weight;
    
        public Person(String name) {
            this.name = name;
        }
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
        public Person(String name, int age, double height) {
            this.name = name;
            this.age = age;
            this.height = height;
        }
        public Person(String name, int age, double height, double weight) {
            this.name = name;
            this.age = age;
            this.height = height;
            this.weight = weight;
        }
    
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public int getAge() {
            return age;
        }
        public void setAge(int age) {
            this.age = age;
        }
        public double getHeight() {
            return height;
        }
        public void setHeight(double height) {
            this.height = height;
        }
        public double getWeight() {
            return weight;
        }
        public void setWeight(double weight) {
            this.weight = weight;
        }
    }

      创建各个需要的对象:

    Person p1=new Person();
    Person p2=new Person("张三");
    Person p3=new Person("李四",18);
    Person p4=new Person("王五",21,180);
    Person p5=new Person("赵六",17,170,65.4);

      可以想象一下这样创建的坏处,最直观的就是四个参数的构造函数的最后面的两个参数到底是什么意思,可读性不怎么好,如果不点击看源码,不知道哪个是weight哪个是height。还有一个问题就是当有很多参数时,编写这个构造函数就会显得异常麻烦,这时候如果换一个角度,试试Builder模式,你会发现代码的可读性一下子就上去了。

    4.2 使用Builder模式

    public class Person {
        private String name;
        private int age;
        private double height;
        private double weight;
    
        private Person(Builder builder){
            this.name=builder.name;
            this.age=builder.age;
            this.height=builder.height;
            this.weight=builder.weight;
        }
    
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public int getAge() {
            return age;
        }
        public void setAge(int age) {
            this.age = age;
        }
        public double getHeight() {
            return height;
        }
        public void setHeight(double height) {
            this.height = height;
        }
        public double getWeight() {
            return weight;
        }
        public void setWeight(double weight) {
            this.weight = weight;
        }
    
        static class Builder{
            private String name;
            private int age;
            private double height;
            private double weight;
    
            public Builder setName(String name) {
                this.name=name;
                return this;
            }
            public Builder setAge(int age) {
                this.age=age;
                return this;
            }
            public Builder setHeight(double height) {
                this.height=height;
                return this;
            }
            public Builder setWeight(double weight) {
                this.weight=weight;
                return this;
            }
            public Person build(){
                return new Person(this);// build()返回Person对象
            }
        }
    }

      在Builder类里定义了一份与Person类一模一样的变量,通过一系列的成员函数进行设置属性值,但是返回值都是this,也就是都是Builder对象,最后提供了一个build函数用于创建Person对象,返回的是Person对象,对应的构造函数在Person类中进行定义,也就是构造函数的入参是Builder对象,然后依次对自己的成员变量进行赋值,对应的值都是Builder对象中的值。此外Builder类中的成员函数返回Builder对象自身的另一个作用就是让它支持链式调用,使代码可读性大大增强。于是可以这样创建Person类。

    Person person = new Person().Builder()
          .setName("张三")
          .setAge(18)
          .setHeight(178.5)
          .setWeight(67.4)
          .build();

    4.3 总结一下

    (1)定义一个静态内部类Builder,内部的成员变量和外部类一样
    Builder类通过一系列的方法用于成员变量的赋值,并返回当前对象本身(this)。
    (2)Builder类提供一个build方法或者create方法用于创建对应的外部类,该方法内部调用了外部类的一个私有构造函数,该构造函数的参数就是内部类Builder。
    (3)外部类提供一个私有构造函数供内部类调用,在该构造函数中完成成员变量的赋值,取值为Builder对象中对应的值。

    5 Builder模式实战

    (1)产品类——探探App

    /**
     * 产品类——探探App
     */
    public class Product {
        public static final String ANDROID = "android";
        public static final String IOS = "ios";
    
        private String appName;
        private String appFuction;
        private String appSystem;
    
        public String getAppName() {
            return appName;
        }
    
        public void setAppName(String appName) {
            this.appName = appName;
        }
    
        public String getAppFuction() {
            return appFuction;
        }
    
        public void setAppFuction(String appFuction) {
            this.appFuction = appFuction;
        }
    
        public String getAppSystem() {
            return appSystem;
        }
    
        public void setAppSystem(String appSystem) {
            this.appSystem = appSystem;
        }
    
        @Override
        public String toString() {
            return "Product [appName=" + appName + ", appFuction=" + appFuction
                    + ", appSystem=" + appSystem + "]";
        }
    }

    (2)抽象建造者类——技术主管(可省略)

    /**
     * 抽象建造者类——技术主管 
     */
    public abstract class Build {
        public abstract Build setAppName(String appName);
    
        public abstract Build setAppFuction(String appFuction);
    
        public abstract Build setAppSystem(String appSystem);
    
        public abstract Product build();
    }

    (3)具体建造者类——全栈程序员

    /**
     * 具体建造者类——全栈程序员
     */
    public class WorkBuilder extends Build {
    
        private Product product;// 产品类
        private InnerProduct innerProduct = new InnerProduct();// 产品缓冲类
    
        @Override
        public Build setAppName(String appName) {
            innerProduct.setAppName(appName);
            return this;
        }
    
        @Override
        public Build setAppFuction(String appFuction) {
            innerProduct.setAppFuction(appFuction);
            return this;
        }
    
        @Override
        public Build setAppSystem(String appSystem) {
            innerProduct.setAppSystem(appSystem);
            return this;
        }
    
        @Override
        public Product build() {
            product = new Product();
            product.setAppName(innerProduct.getAppName());
            product.setAppFuction(innerProduct.getAppFuction());
            product.setAppSystem(innerProduct.getAppSystem());
            return product;
        }
    
        /**
         * 产品缓冲类
         */
        private class InnerProduct {
            private String appName;
            private String appFuction;
            private String appSystem;
    
            public String getAppName() {
                return appName;
            }
    
            public void setAppName(String appName) {
                this.appName = appName;
            }
    
            public String getAppFuction() {
                return appFuction;
            }
    
            public void setAppFuction(String appFuction) {
                this.appFuction = appFuction;
            }
    
            public String getAppSystem() {
                return appSystem;
            }
    
            public void setAppSystem(String appSystem) {
                this.appSystem = appSystem;
            }
        }
    }

    (4)指挥者类——产品经理(可省略)

    /**
     * 指挥者类——产品经理 
     */
    public class Director {
        public static Product create(String system) {
            return new WorkBuilder().setAppSystem(system).setAppName("探探").setAppFuction("划一划找妹子").build();
        }
    }

    (5)客户端——客户

    /**
     * 客户端——客户
     */
    public class Client {  
        public static void main(String[] args){  
            // 客户:我需要一个可以摇一摇找妹子的软件  
            // 产品经理:分析得出就做一个探探  
            // 技术主管:appName:探探,系统:ios,android功能:摇一摇找妹子  
            Product android = Director.create(Product.ANDROID);  
            Product ios = Director.create(Product.IOS);  
            System.out.println(android);
            System.out.println(ios);
    
            /**
             * 变异来的建造者可以只需要具体建造者,抽象的不要了,指挥者也可以不要了
             */
            // 程序员觉得太累了,工资又少,干的最多,最后决定自己出去单干。  
            WorkBuilder niubiProgremer = new WorkBuilder();  
            Product androidBest = niubiProgremer.setAppName("探探").setAppSystem(Product.ANDROID).setAppFuction("摇一摇,找妹子").build();  
            Product iosBest = niubiProgremer.setAppName("探探").setAppSystem(Product.IOS).setAppFuction("摇一摇,找妹子").build();
            System.out.println("-------------------------------------------------------------------------------------");
            System.out.println(androidBest);
            System.out.println(iosBest);
        }  
    }  

    (6)结果

    Product [appName=探探, appFuction=划一划找妹子, appSystem=android]
    Product [appName=探探, appFuction=划一划找妹子, appSystem=ios]
    -------------------------------------------------------------------------------------
    Product [appName=探探, appFuction=摇一摇,找妹子, appSystem=android]
    Product [appName=探探, appFuction=摇一摇,找妹子, appSystem=ios]
    

    6 Android源码中的Builder模式

      在Android源码中,我们最常用到的Builder模式就是AlertDialog.Builder, 使用该Builder来构建复杂的AlertDialog对象。

    6.1 AlertDialog时序图分析

    这里写图片描述
    这里写图片描述

    6.2 AlertDialog源码分析

      在Android源码中,我们最常用到的Builder模式就是AlertDialog.Builder, 使用该Builder来构建复杂的AlertDialog对象。简单示例如下 :

    (1)showDialog——显示基本的AlertDialog

    // 显示基本的AlertDialog  
        private void showDialog(Context context) {  
            AlertDialog.Builder builder = new AlertDialog.Builder(context);  
            builder.setIcon(R.drawable.icon);  
            builder.setTitle("Title");  
            builder.setMessage("Message");  
            builder.setPositiveButton("Button1",  
                    new DialogInterface.OnClickListener() {  
                        public void onClick(DialogInterface dialog, int whichButton) {  
                            setTitle("点击了对话框上的Button1");  
                        }  
                    });  
            builder.setNeutralButton("Button2",  
                    new DialogInterface.OnClickListener() {  
                        public void onClick(DialogInterface dialog, int whichButton) {  
                            setTitle("点击了对话框上的Button2");  
                        }  
                    });  
            builder.setNegativeButton("Button3",  
                    new DialogInterface.OnClickListener() {  
                        public void onClick(DialogInterface dialog, int whichButton) {  
                            setTitle("点击了对话框上的Button3");  
                        }  
                    });  
            builder.create().show();  // 构建AlertDialog, 并且显示
        } 

      从类名可以看出这是一个Builder模式,通过Builder对象来组装Dialog的各个部分,如:Title\Button\Message等,将Dialog的构造和表示进行分离。

      结果如图所示 :

    这里写图片描述

    (2)AlertDialog

    // AlertDialog  
    public class AlertDialog extends AppCompatDialog implements DialogInterface {  
        // Controller, 接受Builder成员变量P中的各个参数  
        private AlertController mAlert;  
    
        // 构造函数  
        protected AlertDialog(Context context, int theme) {  
            this(context, theme, true);  
        }  
    
        // 构造AlertDialog  
        AlertDialog(Context context, int theme, boolean createContextWrapper) {  
            super(context, resolveDialogTheme(context, theme), createContextWrapper);  
            mWindow.alwaysReadCloseOnTouchAttr();  
            mAlert = new AlertController(getContext(), this, getWindow());  
        }  
    
        // 实际上调用的是mAlert的setTitle方法  
        @Override  
        public void setTitle(CharSequence title) {  
            super.setTitle(title);  
            mAlert.setTitle(title);  
        }  
    
        // 实际上调用的是mAlert的setCustomTitle方法  
        public void setCustomTitle(View customTitleView) {  
            mAlert.setCustomTitle(customTitleView);  
        }  
    
        public void setMessage(CharSequence message) {  
            mAlert.setMessage(message);  
        }  
    
        // AlertDialog其他的代码省略  
    
        // ************  Builder为AlertDialog的内部类   *******************  
        public static class Builder {  
            // 1:存储AlertDialog的各个参数, 例如title, message, icon等.  
            private final AlertController.AlertParams P;  
            // 属性省略  
    
            public Builder(Context context) {  
                this(context, resolveDialogTheme(context, 0));  
            }  
    
            public Builder(Context context, int theme) {  
                P = new AlertController.AlertParams(new ContextThemeWrapper(  
                        context, resolveDialogTheme(context, theme)));  
                mTheme = theme;  
            }  
    
            // Builder的其他代码省略 ......  
    
            // 2:设置各种参数  
            public Builder setTitle(CharSequence title) {  
                P.mTitle = title;  
                return this;  
            }  
    
            public Builder setMessage(CharSequence message) {  
                P.mMessage = message;  
                return this;  
            }  
    
            public Builder setIcon(int iconId) {  
                P.mIconId = iconId;  
                return this;  
            }  
    
            public Builder setPositiveButton(CharSequence text, final OnClickListener listener) {  
                P.mPositiveButtonText = text;  
                P.mPositiveButtonListener = listener;  
                return this;  
            }  
    
            public Builder setView(View view) {  
                P.mView = view;  
                P.mViewSpacingSpecified = false;  
                return this;  
            }  
    
            // 3:构建AlertDialog, 传递参数  
            public AlertDialog create() {  
                // 4:调用new AlertDialog构造对象, 并且将参数传递个体AlertDialog   
                final AlertDialog dialog = new AlertDialog(P.mContext, mTheme, false);  
                // 5:将P中的参数应用的dialog中的mAlert对象中  
                P.apply(dialog.mAlert); // 重要 
                dialog.setCancelable(P.mCancelable);  
                if (P.mCancelable) {  
                    dialog.setCanceledOnTouchOutside(true);  
                }  
                dialog.setOnCancelListener(P.mOnCancelListener);  
                if (P.mOnKeyListener != null) {  
                    dialog.setOnKeyListener(P.mOnKeyListener);  
                }  
                return dialog;  
            }  
        }  
    }

      通过Builder来设置AlertDialog中的title, message, button等参数, 这些参数都存储在类型为AlertController.AlertParams的成员变量P中,AlertController.AlertParams中包含了与之对应的成员变量。在调用Builder类的create函数时才创建AlertDialog, 并且将Builder成员变量P中保存的参数应用到AlertDialog的mAlert对象中,即P.apply(dialog.mAlert)代码段。我们看看AlertController的实现 :

    (3)AlertController

    
    class AlertController {
        private final Context mContext;
        private final AppCompatDialog mDialog;
        private final Window mWindow;
    
        private CharSequence mTitle;
        private CharSequence mMessage;
        private ListView mListView;
        private View mView;
    
        private int mViewLayoutResId;
    
        private int mViewSpacingLeft;
        private int mViewSpacingTop;
        private int mViewSpacingRight;
        private int mViewSpacingBottom;
        private boolean mViewSpacingSpecified = false;
    
        private Button mButtonPositive;
        private CharSequence mButtonPositiveText;
        private Message mButtonPositiveMessage;
    
        private Button mButtonNegative;
        private CharSequence mButtonNegativeText;
        private Message mButtonNegativeMessage;
    
        private Button mButtonNeutral;
        private CharSequence mButtonNeutralText;
        private Message mButtonNeutralMessage;
    
        private NestedScrollView mScrollView;
    
        private int mIconId = 0;
        private Drawable mIcon;
    
        private ImageView mIconView;
        private TextView mTitleView;
        private TextView mMessageView;
        private View mCustomTitleView;
    
        private ListAdapter mAdapter;
    
        private int mCheckedItem = -1;
    
        private int mAlertDialogLayout;
        private int mButtonPanelSideLayout;
        private int mListLayout;
        private int mMultiChoiceItemLayout;
        private int mSingleChoiceItemLayout;
        private int mListItemLayout;
    
        private int mButtonPanelLayoutHint = AlertDialog.LAYOUT_HINT_NONE;
    
        private Handler mHandler;
    
        /**
         * installContent(),极为重要,它调用了Window对象的setContentView,这个setContentView与Activity中一样,实际上Activity最终也是调用Window对象的setContentView函数。
         */
        public void installContent() {
            // 设置窗口
            final int contentView = selectContentView();
            // 设置窗口的内容视图布局
            mDialog.setContentView(contentView);
            // 初始化AlertDialog其他子视图的内容
            setupView();// setupView
        }
    
      private int selectContentView() {
            if (mButtonPanelSideLayout == 0) {
                return mAlertDialogLayout;
            }
            if (mButtonPanelLayoutHint == AlertDialog.LAYOUT_HINT_SIDE) {
                return mButtonPanelSideLayout;
            }
            return mAlertDialogLayout;// AlertDialog的布局id
        }
    
        /**
         * setupView()
         */ 
        private void setupView() {
            // 获取并初始化内容区域
            final View parentPanel = mWindow.findViewById(R.id.parentPanel);
            final View defaultTopPanel = parentPanel.findViewById(R.id.topPanel);
            final View defaultContentPanel = parentPanel.findViewById(R.id.contentPanel);
            final View defaultButtonPanel = parentPanel.findViewById(R.id.buttonPanel);
            final ViewGroup customPanel = (ViewGroup) parentPanel.findViewById(R.id.customPanel);
            setupCustomContent(customPanel);
    
            // 自定义内容视图区域
            final View customTopPanel = customPanel.findViewById(R.id.topPanel);
            final View customContentPanel = customPanel.findViewById(R.id.contentPanel);
            final View customButtonPanel = customPanel.findViewById(R.id.buttonPanel);
            final ViewGroup topPanel = resolvePanel(customTopPanel, defaultTopPanel);
            final ViewGroup contentPanel = resolvePanel(customContentPanel, defaultContentPanel);
            final ViewGroup buttonPanel = resolvePanel(customButtonPanel, defaultButtonPanel);
    
            setupContent(contentPanel);
            setupButtons(buttonPanel);
            setupTitle(topPanel);
    
            final boolean hasCustomPanel = customPanel != null
                    && customPanel.getVisibility() != View.GONE;
            final boolean hasTopPanel = topPanel != null
                    && topPanel.getVisibility() != View.GONE;
            final boolean hasButtonPanel = buttonPanel != null
                    && buttonPanel.getVisibility() != View.GONE;
    
            // Only display the text spacer if we don't have buttons.
            if (!hasButtonPanel) {
                if (contentPanel != null) {
                    final View spacer = contentPanel.findViewById(R.id.textSpacerNoButtons);
                    if (spacer != null) {
                        spacer.setVisibility(View.VISIBLE);
                    }
                }
            }
    
            if (hasTopPanel) {
                if (mScrollView != null) {
                    mScrollView.setClipToPadding(true);
                }
            }
    
            // Update scroll indicators as needed.
            if (!hasCustomPanel) {
                final View content = mListView != null ? mListView : mScrollView;
                if (content != null) {
                    final int indicators = (hasTopPanel ? ViewCompat.SCROLL_INDICATOR_TOP : 0)
                            | (hasButtonPanel ? ViewCompat.SCROLL_INDICATOR_BOTTOM : 0);
                    setScrollIndicators(contentPanel, content, indicators,
                            ViewCompat.SCROLL_INDICATOR_TOP | ViewCompat.SCROLL_INDICATOR_BOTTOM);
                }
            }
    
            final ListView listView = mListView;
            if (listView != null && mAdapter != null) {
                listView.setAdapter(mAdapter);
                final int checkedItem = mCheckedItem;
                if (checkedItem > -1) {
                    listView.setItemChecked(checkedItem, true);
                    listView.setSelection(checkedItem);
                }
            }
        }
    
        /**
         * AlertController.AlertParams 持有AlertController的所有属性,在调用builder里的设置属性方法时,就是给AlertController.AlertParams做一个缓存。
         */
        public static class AlertParams {
            public final Context mContext;
            public final LayoutInflater mInflater;
    
            public int mIconId = 0;
            public Drawable mIcon;
            public int mIconAttrId = 0;
            public CharSequence mTitle;
            public View mCustomTitleView;
            public CharSequence mMessage;
            public CharSequence mPositiveButtonText;
            public DialogInterface.OnClickListener mPositiveButtonListener;
            public CharSequence mNegativeButtonText;
            public DialogInterface.OnClickListener mNegativeButtonListener;
            public CharSequence mNeutralButtonText;
            public DialogInterface.OnClickListener mNeutralButtonListener;
            public boolean mCancelable;
            public DialogInterface.OnCancelListener mOnCancelListener;
            public DialogInterface.OnDismissListener mOnDismissListener;
            public DialogInterface.OnKeyListener mOnKeyListener;
            public CharSequence[] mItems;
            public ListAdapter mAdapter;
            public DialogInterface.OnClickListener mOnClickListener;
            public int mViewLayoutResId;
            public View mView;
            public int mViewSpacingLeft;
            public int mViewSpacingTop;
            public int mViewSpacingRight;
            public int mViewSpacingBottom;
            public boolean mViewSpacingSpecified = false;
            public boolean[] mCheckedItems;
            public boolean mIsMultiChoice;
            public boolean mIsSingleChoice;
            public int mCheckedItem = -1;
            public DialogInterface.OnMultiChoiceClickListener mOnCheckboxClickListener;
            public Cursor mCursor;
            public String mLabelColumn;
            public String mIsCheckedColumn;
            public boolean mForceInverseBackground;
            public AdapterView.OnItemSelectedListener mOnItemSelectedListener;
            public OnPrepareListViewListener mOnPrepareListViewListener;
            public boolean mRecycleOnMeasure = true;
    
            public AlertParams(Context context) {
                mContext = context;
                mCancelable = true;
                mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            }
    
            /**
             * 最主要的方法是apply(),实际上就是把P中的参数挨个的设置到AlertController中, 也就是AlertDialog中的mAlert对象。从AlertDialog的各个setter方法中我们也可以看到,实际上也都是调用了mAlert对应的setter方法。
             */
            public void apply(AlertController dialog) {
                if (mCustomTitleView != null) {
                    dialog.setCustomTitle(mCustomTitleView);
                } else {
                    if (mTitle != null) {
                        dialog.setTitle(mTitle);
                    }
                    if (mIcon != null) {
                        dialog.setIcon(mIcon);
                    }
                    if (mIconId != 0) {
                        dialog.setIcon(mIconId);
                    }
                    if (mIconAttrId != 0) {
                        dialog.setIcon(dialog.getIconAttributeResId(mIconAttrId));
                    }
                }
                if (mMessage != null) {
                    dialog.setMessage(mMessage);
                }
                if (mPositiveButtonText != null) {
                    dialog.setButton(DialogInterface.BUTTON_POSITIVE, mPositiveButtonText,
                            mPositiveButtonListener, null);
                }
                if (mNegativeButtonText != null) {
                    dialog.setButton(DialogInterface.BUTTON_NEGATIVE, mNegativeButtonText,
                            mNegativeButtonListener, null);
                }
                if (mNeutralButtonText != null) {
                    dialog.setButton(DialogInterface.BUTTON_NEUTRAL, mNeutralButtonText,
                            mNeutralButtonListener, null);
                }
                // For a list, the client can either supply an array of items or an
                // adapter or a cursor
                if ((mItems != null) || (mCursor != null) || (mAdapter != null)) {
                    createListView(dialog);
                }
                if (mView != null) {
                    if (mViewSpacingSpecified) {
                        dialog.setView(mView, mViewSpacingLeft, mViewSpacingTop, mViewSpacingRight,
                                mViewSpacingBottom);
                    } else {
                        dialog.setView(mView);
                    }
                } else if (mViewLayoutResId != 0) {
                    dialog.setView(mViewLayoutResId);
                }
    
                /*
                dialog.setCancelable(mCancelable);
                dialog.setOnCancelListener(mOnCancelListener);
                if (mOnKeyListener != null) {
                    dialog.setOnKeyListener(mOnKeyListener);
                }
                */
            }
        }
    }

    (5)调用create()和show()方法(该函数在Dialog中)

    new AlertDialog.Builder(this).setTitle("标题").setIcon(R.mipmap.ic_launcher).setMessage("测试").create().show(); 
    
    public class Dialog implements DialogInterface.... {
    
        public void create() {
            if (!mCreated) {
                dispatchOnCreate(null);
            }
        }
    
        /**
         * show()主要作用:
         * (1)通过dispatchOnCreate函数来调用AlertDialog的onCreate函数;
         * (2)然后调用AlertDialog的onStart函数
         * (3)最后将Dialog的DecorView添加到WindowManaget中。
         */
        public void show() {
            if (mShowing) {
                if (mDecor != null) {
                    if (mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {
                        mWindow.invalidatePanelMenu(Window.FEATURE_ACTION_BAR);
                    }
                    mDecor.setVisibility(View.VISIBLE);
                }
                return;
            }
    
            mCanceled = false;
    
            // 1.onCreate调用
            if (!mCreated) {
                dispatchOnCreate(null);// dispatchOnCreate
            }
    
            // 2.onStart
            onStart();
            // 3.获取DecorView
            mDecor = mWindow.getDecorView();
               // 代码省略
            // 4.获取布局参数
            WindowManager.LayoutParams l = mWindow.getAttributes();
            if ((l.softInputMode
                    & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) == 0) {
                WindowManager.LayoutParams nl = new WindowManager.LayoutParams();
                nl.copyFrom(l);
                nl.softInputMode |=
                        WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
                l = nl;
            }
    
            try {
                // 5.将mDecor添加到WindowManager中
                mWindowManager.addView(mDecor, l);
                mShowing = true;
                // 发送一个显示Dialog的消息
                sendShowMessage();
            } finally {
            }
        }
    
        // dispatchOnCreate()
        void dispatchOnCreate(Bundle savedInstanceState) {
                if (!mCreated) {
                    onCreate(savedInstanceState);// 接着看AlertDialog的onCreate函数
                    mCreated = true;
                }
            }
        }
    

    (6)AlertDialog的onCreate方法

    public class AlertDialog extends AppCompatDialog implements DialogInterface {
    
        private final AlertController mAlert;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);// Dialog的onCreate空实现
            mAlert.installContent();// 调用了AlertController的installContent()方法
        }
    }

    (7)AlertController的installContent()和setupView()方法

    class AlertController {
    
        private int mAlertDialogLayout;
    
        // 构造函数
        public AlertController(Context context, AppCompatDialog di, Window window) {
            mContext = context;
            mDialog = di;
            mWindow = window;
            mHandler = new ButtonHandler(di);
    
            final TypedArray a = context.obtainStyledAttributes(null, R.styleable.AlertDialog,R.attr.alertDialogStyle, 0);
    
            // AlertDialog的布局id,也就是AlertDialog_android_layout.xml的布局
            mAlertDialogLayout = a.getResourceId(R.styleable.AlertDialog_android_layout, 0);
            mButtonPanelSideLayout = a.getResourceId(R.styleable.AlertDialog_buttonPanelSideLayout, 0);
    
            mListLayout = a.getResourceId(R.styleable.AlertDialog_listLayout, 0);
            mMultiChoiceItemLayout = a.getResourceId(R.styleable.AlertDialog_multiChoiceItemLayout, 0);
            mSingleChoiceItemLayout = a
                   .getResourceId(R.styleable.AlertDialog_singleChoiceItemLayout, 0);
            mListItemLayout = a.getResourceId(R.styleable.AlertDialog_listItemLayout, 0);
    
            a.recycle();
    
            /* We use a custom title so never request a window title */
            di.supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
        }
    
        /**
         * installContent(),极为重要,它调用了Window对象的setContentView,这个setContentView与Activity中一样,实际上Activity最终也是调用Window对象的setContentView函数。
         */
        public void installContent() {
            // 设置窗口
            final int contentView = selectContentView();
            // 设置窗口的内容视图布局
            mDialog.setContentView(contentView);
            // 初始化AlertDialog其他子视图的内容
            setupView();// setupView
        }
    
      private int selectContentView() {
            if (mButtonPanelSideLayout == 0) {
                return mAlertDialogLayout;
            }
            if (mButtonPanelLayoutHint == AlertDialog.LAYOUT_HINT_SIDE) {
                return mButtonPanelSideLayout;
            }
            return mAlertDialogLayout;// AlertDialog的布局id
        }
    
        /**
         * setupView()
         */ 
        private void setupView() {
            // 获取并初始化内容区域
            final View parentPanel = mWindow.findViewById(R.id.parentPanel);
            final View defaultTopPanel = parentPanel.findViewById(R.id.topPanel);
            final View defaultContentPanel = parentPanel.findViewById(R.id.contentPanel);
            final View defaultButtonPanel = parentPanel.findViewById(R.id.buttonPanel);
            final ViewGroup customPanel = (ViewGroup) parentPanel.findViewById(R.id.customPanel);
            setupCustomContent(customPanel);
    
            // 自定义内容视图区域
            final View customTopPanel = customPanel.findViewById(R.id.topPanel);
            final View customContentPanel = customPanel.findViewById(R.id.contentPanel);
            final View customButtonPanel = customPanel.findViewById(R.id.buttonPanel);
            final ViewGroup topPanel = resolvePanel(customTopPanel, defaultTopPanel);
            final ViewGroup contentPanel = resolvePanel(customContentPanel, defaultContentPanel);
            final ViewGroup buttonPanel = resolvePanel(customButtonPanel, defaultButtonPanel);
    
            setupContent(contentPanel);
            setupButtons(buttonPanel);
            setupTitle(topPanel);
    
            final boolean hasCustomPanel = customPanel != null
                    && customPanel.getVisibility() != View.GONE;
            final boolean hasTopPanel = topPanel != null
                    && topPanel.getVisibility() != View.GONE;
            final boolean hasButtonPanel = buttonPanel != null
                    && buttonPanel.getVisibility() != View.GONE;
    
            // Only display the text spacer if we don't have buttons.
            if (!hasButtonPanel) {
                if (contentPanel != null) {
                    final View spacer = contentPanel.findViewById(R.id.textSpacerNoButtons);
                    if (spacer != null) {
                        spacer.setVisibility(View.VISIBLE);
                    }
                }
            }
    
            if (hasTopPanel) {
                if (mScrollView != null) {
                    mScrollView.setClipToPadding(true);
                }
            }
    
            // Update scroll indicators as needed.
            if (!hasCustomPanel) {
                final View content = mListView != null ? mListView : mScrollView;
                if (content != null) {
                    final int indicators = (hasTopPanel ? ViewCompat.SCROLL_INDICATOR_TOP : 0)
                            | (hasButtonPanel ? ViewCompat.SCROLL_INDICATOR_BOTTOM : 0);
                    setScrollIndicators(contentPanel, content, indicators,
                            ViewCompat.SCROLL_INDICATOR_TOP | ViewCompat.SCROLL_INDICATOR_BOTTOM);
                }
            }
    
            final ListView listView = mListView;
            if (listView != null && mAdapter != null) {
                listView.setAdapter(mAdapter);
                final int checkedItem = mCheckedItem;
                if (checkedItem > -1) {
                    listView.setItemChecked(checkedItem, true);
                    listView.setSelection(checkedItem);
                }
            }
        }
    }

      在该函数调用之后整个Dialog的视图内容全部设置完毕。

    (8)小总结
      在AlertDialog的Builder模式中并没有看到Director角色出现,并没按照标准的模式使用,而是做了一些修改。AlertDialog.Builder同时扮演了上下文提到的builder、ConcreteBuilder、Director的角色,简化了Builder模式的设计。

    7 总结

      Builder模式在Android开发中也较为常用,通常作为配置类的构建器将配置的构建和表示分离开来,同时也是将配置从目标类中隔离出来,避免过多的setter方法。Builder模式比较常见的实现形式是通过调用链实现,这样使得代码更简洁、易懂,例如图片请求框架ImageLoader就是通过ImageLoaderConfig进行配置,这样避免了目标类中被过多的接口“污染”。

    7.1 优点

    (1)良好的封装性, 使用建造者模式可以使客户端不必知道产品内部组成的细节;
    (2)建造者独立,容易扩展;
    (3)在对象创建过程中会使用到系统中的一些其它对象,这些对象在产品对象的创建过程中不易得到。

    7.2 缺点

    (1)会产生多余的Builder对象以及Director对象,消耗内存;
    (2)对象的构建过程暴露。

    8 参考文章与链接

    《Android源码设计模式解析与实战》

    《设计模式之禅》

    Android源码分析之Builder模式

    Android进阶系列之源码分析AlertDialog建造者模式

    Android设计模式——Builder模式

    展开全文
  • Java设计模式——构建器模式(Builder)(一)

    千次阅读 多人点赞 2019-07-22 09:01:25
    Java设计模式——变种的构建器模式

    一、引言

    在日常的开发中,我们可能经常能看到如下的代码:

    PrivateKeyDecryptParam param = new PrivateKeyDecryptParam.Builder()
                                                  .uAppId(uAppId)
                                                  .containerId(containerId)
                                                  .cipher(cipher)
                                                  .signFlag(signFlag)
                                                  .build();
    

    在Android中,也会看到下面创建AlertDialog代码:

    new AlertDialog.Builder(this)
               .setTitle("标题")
               .setMessage("内容")
               .setCancelable(true)
               .setOnCancelListener(new DialogInterface.OnCancelListener() {
                   @Override
                   public void onCancel(DialogInterface dialog) {
                    
                   }
               })
               .show();
    

    观察上面这两段代码,都有一个共同的特点,就是都可以进行链式操作,这就是我们要学习的Builder模式,下面就来详细学习一下。

    二、Builer模式的使用场景

    在《Effective Java第2版》书中有提到,当遇到多个构造器参数时,要考虑使用构建器(Builder模式)。

    举个例子,比如在项目中,我们需要新建一个Person类,假设该类有7个属性(现实中远不止这几个参数),其中有2个是必要的参数需要初始化,分别是id和name。

    1、使用JavaBean的setter方法来设置对象属性

    最常见的写法应该是写成JavaBean。代码如下:

    public class Person {
        //必要参数
        private int id;
        private String name;
        //可选参数
        private int age;
        private String sex;
        private String phone;
        private String address;
        private String desc;
    
      // set/get方法忽略
    }
    

    在使用该Person类的时候,会写出如下的代码:

    	Person person = new Person();
    	person.setId(1);
    	person.setName("张小毛");
    	person.setAge(22);
    	person.setSex("男");
    	person.setPhone("19999999999");
    	person.setAddress("beijing");
    	person.setDesc("JavaBeans模式");
    

    这种JavaBean的方式也是常见的构造对象并赋值的方式,这种方式的好处是:

    (1)、易于阅读,并且可以只对有用的成员变量赋值;

    但它的缺点是:

    (1)、成员变量不可以是 final 类型,失去了不可变对象的很多好处;

    (2)、对象状态不连续,你必须调用7次setter方法才能得到一个具备7个属性值得变量,在这期间用户可能拿到不完整状态的对象。如果有N个属性,岂不是要person.setXXX调用N次?此种方式不优雅。

    最重要的缺点是第二条:对象状态不连续。 什么意思呢?

    解释一下:这种方式是 先创建对象、后赋值,用户不知道什么时候拿到的对象是完整的,构建完成的。很有可能你只setter了一两个属性就返回了,一些必要的属性没有被赋值。

    2、使用重叠构造器

    在这种模式下,需要提供一个只有必要参数的构造器,第二个构造器有一个可选参数,第三个有两个可选参数,依此类推,最后一个构造器包含所有的可选参数。代码如下:

    	public class Person {
    	    //必要参数
    	    private final int id;
    	    private final String name;
    	    //可选参数
    	    private int age;
    	    private String sex;
    	    private String phone;
    	    private String address;
    	    private String desc;
    	
    	    public Person(int id, String name) {
    	        this(id, name, 0);
    	    }
    	
    	    public Person(int id, String name, int age) {
    	        this(id, name, age, "");
    	    }
    	
    	    public Person(int id, String name, int age, String sex) {
    	        this(id, name, age, sex, "");
    	    }
    	
    	    public Person(int id, String name, int age, String sex, String phone) {
    	        this(id, name, age, sex, phone, "");
    	    }
    	
    	    public Person(int id, String name, int age, String sex, String phone,
    	    			 String address) {
    	        this(id, name, age, sex, phone, address, "");
    	    }
    	
    	    public Person(int id, String name, int age, String sex, String phone, 
    	    			String address, String desc) {
    	        this.id = id;
    	        this.name = name;
    	        this.age = age;
    	        this.sex = sex;
    	        this.phone = phone;
    	        this.address = address;
    	        this.desc = desc;
    	    }
    	}
    

    从上面的代码可以看出,当你想要创建实例的时候,就利用参数列表最短的构造器,但该列表中包含了要设置的所有参数,其余都为默认值:

    Person person = new Persion(1, "张小毛");
    

    这个构造器调用,通常需要许多你本不想设置的参数,但还是不得不为它们传递值。

    这种方式的优点就是:简单!!!!(这是对开发者而言),但使用者在使用时,可得仔细了解你每个构造函数,否则一不小心填错顺序也不知道。而且如果有十几个属性,就歇菜了……(我也没见过有十几个参数的构造函数)

    所以缺点是:

    只适用于成员变量少的情况,太多了不容易理解、维护。

    简而言之:重叠构造器可行,但是当有许多参数的时候,创建使用代码会很难写,并且较难以阅读。

    三、变种Builder模式

    对于上述分析的两种方法,都存在优点和缺点,为了解决上述两种构建方式的不足,伟大的程序员们创造出了变种 Builder模式。直接看代码:

    public class Person {
        //必要参数
        private final int id;
        private final String name;
        //可选参数
        private int age;
        private String sex;
        private String phone;
        private String address;
        private String desc;
    
        private Person(Builder builder) {
            this.id = builder.id;
            this.name = builder.name;
            this.age = builder.age;
            this.sex = builder.sex;
            this.phone = builder.phone;
            this.address = builder.address;
            this.desc = builder.desc;
        }
    
        public static class Builder {
            //必要参数
            private final int id;
            private final String name;
            //可选参数
            private int age;
            private String sex;
            private String phone;
            private String address;
            private String desc;
    
            public Builder(int id, String name) {
                this.id = id;
                this.name = name;
            }
    
            public Builder age(int val) {
                this.age = val;
                return this;
            }
    
            public Builder sex(String val) {
                this.sex = val;
                return this;
            }
    
            public Builder phone(String val) {
                this.phone = val;
                return this;
            }
    
            public Builder address(String val) {
                this.address = val;
                return this;
            }
    
            public Builder desc(String val) {
                this.desc = val;
                return this;
            }
    
            public Person build() {
                return new Person(this);
            }
        }
    }
    

    观察上述代码,可以看到变种Builder模式包括以下内容:

    (1)、在要构建类的内部,创建一个静态内部类Builder;

    (2)、静态内部类的属性要与构建类的属性一致;

    (3)、构建类的构造参数是静态内部类,使用静态内部类的变量为构建类逐一赋值;

    (4)、静态内部类提供参数的setter方法,并且返回值是当前Builder对象;

    (5)、最终提供一个build方法new出来一个构建类的对象,参数是当前Builder对象;

    调用代码如下:

    	public class Test {
    	
    	    public static void main(String[] args) {
    	        Person person = new Person.Builder(1, "张小毛")
    	                .age(22).sex("男").desc("使用builder模式").build();
    	        System.out.println(person.toString());
    	    }
    	}
    

    对变种Builer模式的总结:

    (1)、变种Builder模式目的在于:减少对象创建过程中引入的多个构造函数、可选参数以及多个setter过度使用导致的不必要的复杂性。

    (2)、优点就是:看起来很整齐;先赋值,后创建对象,最终调用build()方法才创建了构建类的对象,保证了状态的完整性。

    (3)、缺点嘛,就是需要额外写的代码多了点。

    四、小结:

    变种Builer模式相比于重叠构造器模式和JavaBeans模式,Builder模式实现的对象更利于使用。

    对Builer模式使用方法的总结:

    (1)、外部类的构造函数私有,且参数为静态内部类;
    (2)、静态内部类拥有外部类相同的属;
    (3)、为每一个属性,写一个方法,返回的是Builer;
    (4)、最后一个方法是build方法,用于构建一个外部类;

    展开全文
  • Builder模式简单理解

    千次阅读 2018-07-25 07:54:58
    Builder 模式 Builder模式 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。 那么Builder模式的用场景呢? (1) 相同的方法,不同的执行顺序,产生不同的事件结果时。 (2) 多个部件...
  • @Builder

    千次阅读 2019-06-10 14:38:51
    https://www.projectlombok.org/features/Builder...@Builder ... and Bob's your uncle: No-hassle fancy-pants APIs for object creation! @Builderwas introduced as experimental feature in lombok v0.12.0. ...
  • Builder模式

    2019-05-09 22:16:03
    Builder模式作为创建型模式之一,几乎随处可见,尤其是创建复杂对象时;每当构造方法不能再愉快地工作时,可能就需要它出马拯救即将”崩坏““丑化”的代码。 模式概况 实际需求 想创建的目标对象所需参数较多,且...
  • Java中Builder模式的实现详解

    万次阅读 2018-06-04 17:58:13
    下面这篇文章主要给大家介绍了在Java各个版本中Builder模式实现的相关资料,文中介绍的非常详细,需要的朋友可以参考学习。 前言本文主要给大家介绍了关于如何实现Builder模式,大家在构建大对象时,对象的...
  • Java Lombok @Data @Builder

    万次阅读 多人点赞 2016-12-25 20:06:26
    常用的 lombok 注解@EqualsAndHashCode:实现equals()方法和hashCode()方法 @ToString:实现toString()方法 @Data :注解在类上;提供类所有属性的 getting 和 setting 方法,此外还提供了equals、canEqual、...
  • 承接上一篇博客观察者模式,这一期我们一起来了解一下Builder模式。我们最近几期博客打算主要讲一下单例模式、观察者模式、Build模式,目的是为了方便后期为大家带来RXJava+Retrofit2.0+Okhttp3结合使用的网络请求...
  • 网上下载开源项目,build的时候提示:Errors running builder 'Integrated External Tool Builder' on project 'PocketSphinxDemo'. 解决方法是: 右键项目选择“Properties”,再选择“Builders”,...
  • Errors running builder 'Maven Project Builder'

    万次阅读 2017-09-13 14:11:44
    由于第一次玩maven的时候,很多东西都还是懵懵懂懂,不是很清楚,不知道怎么把Myeclipse中的maven配置弄坏了,从外部导入maven...Errors running builder 'Maven Project Builder' on project '(Hid Project Name)'.
  • 解决方法为 在Windows->Preferences->Java->Build Path->ClassPath Variables中增加某变量,通常是用户导入项目的lib库所在目录。
  • 在eclipse构建项目的时候,一直报如下错误: ...Errors running builder "Integrated External Tool Builder" on project project. The builder launch configuration could not be found. Errors running buil
  • 此问题一般发生在Myeclipse 保存文件并自动部署...Errors running builder 'DeploymentBuilder' on project '项目名'. java.lang.NullPointerException 有一种产生此错误的原因是因为此项目不不是由myec
  • Errors occurred during the build... Errors running builder 'Integrated External Tool Builder' on project 'xinghe-base'.The builder launch configuration could not be found.The builder launch configuratio
  • AlertDialog.Builder的基本用法

    千次阅读 2018-01-14 20:09:01
    AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this); builder.setTitle("是否取消"); builder.setNegativeButton("取消", new DialogInterface.OnClickListener() { @Override public voi
  • eclipse编译工程里,报如下错误: ... Errors running builder 'Integrated External ToolBuilder' on project 'dataprocessor'.  The file does not exist for the external toolnamed jspack. -----
  • 摘要:目的:开发中喜欢builder模式去构造一个实例,而当一个对象的属性过多的时候,手动的去完成一个类的builder是很繁琐的;所以如果能够自动生成相应的代码在开发中是很有必要的原理:builder模式代码的自动生成借鉴于...
  • 最近使用eclipse时,在编译项目总是出现问题。...Errors running builder 'JavaScript Validator' on project 'XX'.  java.lang.NullPointerException  要解决这个问题,要就把JavaScript Validator去掉。
1 2 3 4 5 ... 20
收藏数 390,667
精华内容 156,266
关键字:

builder