侧边栏壁纸
博主头像
孔子说JAVA博主等级

成功只是一只沦落在鸡窝里的鹰,成功永远属于自信且有毅力的人!

  • 累计撰写 285 篇文章
  • 累计创建 125 个标签
  • 累计收到 4 条评论

目 录CONTENT

文章目录

JAVA设计模式之建造者模式

孔子说JAVA
2019-11-12 / 0 评论 / 4 点赞 / 198 阅读 / 4,449 字 / 正在检测是否收录...

1、建造者模式(Builder)的定义

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

Builder模式是一步一步创建一个复杂对象的创建型模式,它允许使用者在不知道内部建造细节的情况下,可以更精细的控制对象的构造流程。该模式是为了将构建复杂对象的过程和它的部件解耦,是的构建过程和不见得表示隔离开来。

  • 因为一个复杂的对象有很多大量组成部分,如电脑,有主机,显示器,操作系统,还有各种小零件等,如何将这些不见组建成一台电脑,这个装配过程很漫长,也很复杂,为了在构建过程中对外部隐藏实现细节,就可以使用Builder模式将部件和组装过程分离,使得构建过程和部件都可以自由扩展,两者之间的耦合也降到最低。

2、模式(Builder)优缺点

建造者模式是一种创建型设计模式。其主要优缺点如下:

优点:

  1. 产品的建造和表示分离,实现了解耦,可以使用相同的创建过程得到不同的产品。
  2. 将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰。(良好的封装性,不必知道内部组成的细节)
  3. 增加新的具体建造者无需修改原有类库的代码,易于拓展,符合“开闭原则“。

缺点:

  1. 产品必须有共同点,限制了使用范围。建造者模式创造出来的产品,其组成部分基本相同。如果产品之间的差异较大,则不适用这个模式。
  2. 产生多余的builder对象以及director对象,消耗内存。(如内部变化复杂,会有很多的建造类,难以维护)

3、模式(Builder)适用环境

  1. 相同的方法,不同的执行顺序,产生不同的事件结果。
  2. 对各部件或零件,都可以配到一个对象中,但是产生的运行结果又不相同时。
  3. 产品类非常复杂,或者产品类中的调用顺序不同产生了不同的结果,这个时候使用创建者模式非常合适。
  4. 当初始化一个对象特别复杂,如参数多,且很多参数都有默认值时(一个类有多个构造函数的时候,可以考虑使用建造者模式)
  5. 需要生成的产品对象有复杂的内部结构,这些产品对象具备共性;
  6. 隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品。
  7. 需要生成的对象内部属性本身相互依赖。

4、模式(Builder)的结构

image-1649380188722

建造者模式包含如下角色:

  • builder(抽象接口) : 为创建一个产品对象的各个部件指定抽象接口。
  • ConcreteBuilder(抽象接口的具体实现) : 实现Builder的接口以构造和装配该产品的各个部件,定义并明确它所创建的表示,并提供一个检索产品的接口。
  • Director(接口的构造者和使用者) : 构造一个使用Builder接口的对象。
  • Product(被构造的复杂对象) : ConcreteBuilder创建该产品的内部表示并定义它的装配过程,包含定义组成部件的类,包括将这些部件装配成最终产品的接口。

5、模式(Builder)的应用实例

建造者模式中有原型写法和链式写法。

5.1 原型写法:

实体User类(Product)

public class User {
    String id;
    String name;
    String password;
    String phone;
    //set/get方法
    @Override
    public String toString() {
        return "User{" +
                "id='" + id + '\'' +
                ", name='" + name + '\'' +
                ", password='" + password + '\'' +
                ", phone='" + phone + '\'' +
                '}';
    }
}

抽象建造者/建造者接口(Builder)

public interface Build {
    void makeId(String val);
    void makeName(String val);
    void makePassword(String val);
    void makePhone(String val);
    User makeUser();
}

具体建造者(BuilderImpl)

public class AdminBuilder implements Build{
    User user=new User();
    @Override
    public User makeUser(){
        return user;
    }
    @Override
    public void makeId(String val) {
        user.setId(val);
    }
    @Override
    public void makeName(String val) {
        user.setName(val);
    }
    @Override
    public void makePassword(String val) {
        user.setPassword(val);
    }
    @Override
    public void makePhone(String val) {
        user.setPhone(val);
    }
}

Admin指导建造者创建User(Director)

public class Admin {
    private AdminBuilder adminBuilder;

    public void setAdminBuilder(AdminBuilder adminBuilder) {
        this.adminBuilder = adminBuilder;
    }
    public User makeUser(String id,String name,String password,String phone){
        adminBuilder.makeId(id);
        adminBuilder.makeName(name);
        adminBuilder.makePassword(password);
        adminBuilder.makePhone(phone);
        return this.adminBuilder.makeUser();
    }
}

具体main类

public static void main(String[] args){
    // new具体建造者
    AdminBuilder adminBuilder=new AdminBuilder();
    Admin admin=new Admin();// new出Admin指导建造者
    admin.setAdminBuilder(adminBuilder);// 准备开始建造(不写会报空指针)
    // 根据Admin的建造方法创建User
    User user=admin.makeUser("1","张三","123456","88888888");
    System.out.println(user.toString());
}

5.2 链式写法(推荐):

链式写法是在原型写法的基础上做优化,有些时候Builder的创建部分有默认值,或者不需要的情况下,而产生不同的Product,通过以上方式,就需要修改Director类和Builder类,再或者根据不同的创建顺序,生成不同的结果,也需要修改Director类。Director似乎显得很不稳定和多余。可以通过Builder自身的调用逻辑来生成Product,即链式调用。

public class User {
    String id;
    String name;
    String password;
    String phone;

    private User(Builder builder) {
        id = builder.id;
        name = builder.name;
        password = builder.password;
        phone = builder.phone;
    }

    public String getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public String getPassword() {
        return password;
    }

    public String getPhone() {
        return phone;
    }

    @Override
    public String toString() {
        return "User{" +
                "id='" + id + '\'' +
                ", name='" + name + '\'' +
                ", password='" + password + '\'' +
                ", phone='" + phone + '\'' +
                '}';
    }

    public static final class Builder {
        private String id;
        private String name;
        private String password;
        private String phone;

        public Builder() {
        }

        public Builder id(String val) {
            id = val;
            return this;
        }

        public Builder name(String val) {
            name = val;
            return this;
        }

        public Builder password(String val) {
            password = val;
            return this;
        }

        public Builder phone(String val) {
            phone = val;
            return this;
        }

        public User build() {
            return new User(this);
        }
    }
}

测试类

public class Test {
    public static void main(String[] args) {
        User user=new User.Builder().id("1").name("张三").password("123456")
                                     .phone("15820638007").build();
        System.out.println(user.toString());
    }
}
  • 使用了内部类的方式,将原来自己的类方法变成私有的,而后提供一个静态的内部类来创建对象,通过返回this对象来链式调用。

重点(相比于普通JavaBean的好处):

  • 在建造者模式中,提供一个辅助的静态建造器Builder(静态内部类),可以在里面set实体类的属性,与JavaBean不同的是,建造者是先set,在通过build实例化实体类,这样既可以提高代码的阅读性,也可以防止对象没有实例化,就被调用;不会造成不一致性,同时解决了Javabean模式的线程安全问题。

6、总结

Director角色并非多余,能把复杂的Product创建过程对外隐藏,使Builder部件和创建过程分离,各方易于扩展,降低了耦合度。当需要对一个对象设置很多属性,此时就能方便的使用链式调用来提高编码速度和代码可读性。

4

评论区