设计模式之建造者模式(Java实现)

简介: 设计模式之建造者模式(Java实现)

一、认识建造者模式


建造者模式:是创建型模式的一种,与工厂模式比较,工厂模式主要是负责创建某个零件,建造者模式则是负责具体的操作,也可以说是负责组装。通常建造者模式与工厂模式一起搭配使用!


主要角色:


产品角色(Product):其包含多个组成部件的复杂对象,由具体建造者来创建其各个零部件。

抽象建造者(Builder):为其包含多个创建产品各个子部件的抽象方法接口,并包含一个返回产品的接口。

具体建造者(Concrete Builder):实现Builder抽象类,并且实现完成各个负责操作的接口。

指挥者(Director):主要是用来指挥建造者对象,来完成复杂产品对象的创建,在指挥者中不会涉及产品信息,只负责指挥建造者。

目的:其实也是为了创建对象,只不过在建造者模式中你是通过具体建造者来去创建复杂对象,你可以自己去指挥建造者做事也可以创建出一个指挥者来替你做事(例如你想要个产品,你去找指挥者,指挥者指挥建造者去创建产品,并将建造者创建的产品提交给你,注意了指挥者并不会涉及产品操作仅作指挥)。


优缺点:


优点:封装性好,构建和表示分离;扩展性好,各个具体的建造者相互独立,利于系统的解耦;隐藏内部组成细节,建造者可以对创建过程进行逐步细化。

缺点:若是产品内部发生改变,建造者也要进行同步修改,维护成本大,违反了开闭原则。

建造者模式UML图:




二、实现建造者模式


2.1、完整实现建造者模式(含指挥者)


实现过程


例子见builder包中的demo1:



通过指挥者来指挥建造者要做的事情并且将建造者创建的产品返回给顾客。

具体产品:Product


public class Product {
    private String builderA;
    private String builderB;
    private String builderC;
    private String builderD;
    public String getBuilderA() {
        return builderA;
    }
    public void setBuilderA(String builderA) {
        this.builderA = builderA;
    }
    public String getBuilderB() {
        return builderB;
    }
    public void setBuilderB(String builderB) {
        this.builderB = builderB;
    }
    public String getBuilderC() {
        return builderC;
    }
    public void setBuilderC(String builderC) {
        this.builderC = builderC;
    }
    public String getBuilderD() {
        return builderD;
    }
    public void setBuilderD(String builderD) {
        this.builderD = builderD;
    }
    @Override
    public String toString() {
        return "Product{" +
                "builderA='" + builderA + '\'' +
                ", builderB='" + builderB + '\'' +
                ", builderC='" + builderC + '\'' +
                ", builderD='" + builderD + '\'' +
                '}';
    }
}


抽象建造者:Builder


//抽象工人类
public abstract class Builder {
    //各个具体的抽象步骤
    abstract void builderA();
    abstract void builderB();
    abstract void builderC();
    abstract void builderD();
    //产生产品
    abstract Product getProduct();
}


具体建造者:Worker


//具体建造者:负责实现各个步骤以及产品提供
public class Worker extends Builder{
    //需要获取一个产品
    private Product product;
    public Worker(){
        this.product = new Product();
    }
    @Override
    void builderA() {
        product.setBuilderA("打地基");
        System.out.println("打地基");
    }
    @Override
    void builderB() {
        product.setBuilderB("拉线");
        System.out.println("拉线");
    }
    @Override
    void builderC() {
        product.setBuilderC("搭房子");
        System.out.println("搭房子");
    }
    @Override
    void builderD() {
        product.setBuilderB("砌墙");
        System.out.println("砌墙");
    }
    @Override
    Product getProduct() {
        return product;
    }
}


指挥者:Director


//指挥者:主要来指挥具体建造者做事
public class Director {
    //通过传入参数的建造者,可以指挥建造者做事的顺序,并返回建造者创建的产品
    public static Product build(Builder builder){
        builder.builderA();
        builder.builderB();
        builder.builderC();
        builder.builderD();
        return builder.getProduct();
    }
}


顾客类(即测试类):Customer


//顾客类(测试)
public class Customer {
    public static void main(String[] args) {
        //创建一个指挥者
        Director director = new Director();
        //调用建造方法,传入参数为指定的建造者
        Product product = director.build(new Worker());
        //打印一下产品信息
        System.out.println(product);
    }
}



说明:具体产品包含set/get方法包含属性,具体建造者实现抽象建造者的抽象方法来完成产品的各个复杂步骤,对于指挥者则是去指挥建造者完成产品的步骤顺序,其本身是不会接触到产品,并负责将建造者创建出的产品交接给用户!



2.2、不含指挥者的建造者模式


该demo不通过指挥者来控制建造过程,而是交给客户端来进行指挥建造者的产品创建!


见builder包中的demo2:



该demo并没有指挥者存在,我们可根据自己的需求来让建造者创建出产品出来。

具体产品:Product


//具体产品:一种搭配
public class Product {
    private String fruit = "葡萄";
    private String drink = "牛奶";
    private String food = "意面";
    private String tool = "手套";
    public String getFruit() {
        return fruit;
    }
    public void setFruit(String fruit) {
        this.fruit = fruit;
    }
    public String getDrink() {
        return drink;
    }
    public void setDrink(String drink) {
        this.drink = drink;
    }
    public String getFood() {
        return food;
    }
    public void setFood(String food) {
        this.food = food;
    }
    public String getTool() {
        return tool;
    }
    public void setTool(String tool) {
        this.tool = tool;
    }
    @Override
    public String toString() {
        return "Product{" +
                "fruit='" + fruit + '\'' +
                ", drink='" + drink + '\'' +
                ", food='" + food + '\'' +
                ", tool='" + tool + '\'' +
                '}';
    }
}



本部分产品本身就有一组搭配,若我们想要替换可指挥具体建造者来进行更换操作。

抽象建造者:Builder


//具体工人
public abstract class Builder {
    abstract Builder buildFruit(String msg);
    abstract Builder buildDrink(String msg);
    abstract Builder buildFood(String msg);
    abstract Builder buildTool(String msg);
    abstract Product getProduct();
}


注意在这里其可以进行传参,并且返回本身对象用于链式操作!

具体建造者:Worker


public class Worker extends Builder {
    private Product product;
    public Worker() {
        this.product = new Product();
    }
    @Override
    Builder buildFruit(String msg) {
        product.setFruit(msg);
        return this;
    }
    @Override
    Builder buildDrink(String msg) {
        product.setDrink(msg);
        return this;
    }
    @Override
    Builder buildFood(String msg) {
        product.setFood(msg);
        return this;
    }
    @Override
    Builder buildTool(String msg) {
        product.setTool(msg);
        return this;
    }
    @Override
    Product getProduct() {
        return product;
    }
}



顾客类(即测试类):Customer


public class Customer {
    public static void main(String[] args) {
        Worker worker = new Worker();
        Product product = worker.buildDrink("可乐")
                .getProduct();
        System.out.println(product);
    }
}




三、简易版建造者(简化set方法)


3.1、手写静态内部类(建造者)


给一个对象的属性赋初值常用方法:


我们以前给对象进行赋值通常都是通过set方法,若是赋值多个属性就要使用多个set方法,一旦属性过多那么set也就会越来越多造成代码量冗余;

通过有参构造,只不过这种方式你需要重载多个构造器,但这种形式虽说会在调用创建时很简短,但是阅读起来很不友好,你需要去看每个位置后的参数是什么,并且很容易出错。

学习了建造者模式之后,我们可以通过使用建造者链来简化set方法,起初我看到时也不禁直呼秒呀!


代码见builder包中的demo3:



public class Product {
    private int id;
    private String name;
    private int age;
    public Product(Builder builder){
        this.id = builder.id;
        this.name = builder.name;
        this.age = builder.age;
    }
    //静态内部类:具体建造者
    public static class Builder{
        private int id;
        private String name;
        private int age;
        //有参构造限制写入id
        public Builder(int id){
            this.id = id;
        }
        //set方法返回Builder实例,为形成链式操作
        public Builder setName(int id){
            this.id = id;
            return this;
        }
        public Builder setId(String name){
            this.name = name;
            return this;
        }
        public Builder setAge(int age){
            this.age = age;
            return this;
        }
        //builder()方法相当于创建一个Product实例,其中包含赋值操作
        public Product builder(){
            return new Product(this);
        }
    }
    @Override
    public String toString() {
        return "Product{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}



我们测试一下,看一下使用了建造者链起到了什么样的效果:


//测试类
class Test{
    public static void main(String[] args) {
        //通过链式set方法来进行赋值,最终使用builder()方法来创建出产品
        Product builder = new Product.Builder(15).setAge(100)
                .builder();
        System.out.println(builder);
    }
}



说明:通过这种方式来构造对象是不是感觉一下子舒服了,以后一定会使用到实际项目中去。其实我们可以直接使用Lombok插件,只需要在类上添加一个@Builder就可以实现建造者模式的domain了!



3.2、使用lombok来实现简易建造者


引入loback的jar包,使用@Builder


需要引入lombok的jar包:


<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.16</version>
</dependency>


实现过程


编写Domain类:Person


@Builder
public class Person {
    private String name;
    private int age;
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}


看下IDEA的目录(这需要你在IDEA中安装lombok插件才能看见)



测试一下:


方式一:在类内部或者同一个包中使用方法(测试中使用)


class Test{
    public static void main(String[] args) {
        //允许手动new其静态类
        Person build = new Person.PersonBuilder()
              .age(18)
              .name("changlu")
                .build();
        System.out.println(build);
    }
}



方式二:在其他包中使用方法(实际项目中使用)


class Main2{
    public static void main(String[] args) {
      //通过使用Person类的静态方法builder()获取一个内部静态实例
        Person person = Person.builder()
                .age(18)
                .name("changlu")
                .build();
        System.out.println(person);
    }
}




看下反编译之后的代码


public class Person {
    private String name;
    private int age;
    public String toString() {
        return "Person{name='" + this.name + '\'' + ", age=" + this.age + '}';
    }
    Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public static Person.PersonBuilder builder() {
        return new Person.PersonBuilder();
    }
    public static class PersonBuilder {
        private String name;
        private int age;
        //default方法,只允许在类内部,同一个包类进行new方法构造
        PersonBuilder() {
        }
        public Person.PersonBuilder name(String name) {
            this.name = name;
            return this;
        }
        public Person.PersonBuilder age(int age) {
            this.age = age;
            return this;
        }
        public Person build() {
            return new Person(this.name, this.age);
        }
        public String toString() {
            return "Person.PersonBuilder(name=" + this.name + ", age=" + this.age + ")";
        }
    }
}



说明:获取内部静态类PersonBuilder实例有两种方式:①new Person.PersonBuilder();②Person.builder()。获取好实例之后进行赋值操作,最终调用静态类的build()方法获取Person实例。


注意:静态内部类PersonBuilder的无参构造器是default方法,若是在不同包中建议使用第二种获取方式获取,否则会出现编译错误,权限不足无法构造。



总结


1、建造者模式也属于创建型模式,相对于工厂模式,工厂模式主要是负责创建某个零件,建造者模式则是负责具体的操作,将构建一个负责的对象的各个步骤交由一个建造者来实现,具体构建过程是交由一个指挥者指挥。


2、建造者模式的最终目的实际上也是产生出一个产品,交由指挥者去指挥建造者(可任意调整建造顺序)来创建产品给使用者或者说是用户。


3、建造者优点是封装性好,客户端不需要知道产品内部组成过程,其中建造者可以将创建过程细化,而不对其他模块产生任何影响;缺点是组成部分必须相同,若是产品内部变化建造者也要同步修改,后期维护成本较大,也违反了开闭原则。

相关文章
|
20天前
|
设计模式 消息中间件 搜索推荐
Java 设计模式——观察者模式:从优衣库不使用新疆棉事件看系统的动态响应
【11月更文挑战第17天】观察者模式是一种行为设计模式,定义了一对多的依赖关系,使多个观察者对象能直接监听并响应某一主题对象的状态变化。本文介绍了观察者模式的基本概念、商业系统中的应用实例,如优衣库事件中各相关方的动态响应,以及模式的优势和实际系统设计中的应用建议,包括事件驱动架构和消息队列的使用。
|
1月前
|
设计模式 Java 数据库连接
Java编程中的设计模式:单例模式的深度剖析
【10月更文挑战第41天】本文深入探讨了Java中广泛使用的单例设计模式,旨在通过简明扼要的语言和实际示例,帮助读者理解其核心原理和应用。文章将介绍单例模式的重要性、实现方式以及在实际应用中如何优雅地处理多线程问题。
36 4
|
2月前
|
设计模式 Java 程序员
[Java]23种设计模式
本文介绍了设计模式的概念及其七大原则,强调了设计模式在提高代码重用性、可读性、可扩展性和可靠性方面的作用。文章还简要概述了23种设计模式,并提供了进一步学习的资源链接。
52 0
[Java]23种设计模式
|
1月前
|
设计模式 JavaScript Java
Java设计模式:建造者模式详解
建造者模式是一种创建型设计模式,通过将复杂对象的构建过程与表示分离,使得相同的构建过程可以创建不同的表示。本文详细介绍了建造者模式的原理、背景、应用场景及实际Demo,帮助读者更好地理解和应用这一模式。
|
2月前
|
设计模式 Java
Java设计模式
Java设计模式
33 0
时间轮-Java实现篇
在前面的文章《[时间轮-理论篇](https://developer.aliyun.com/article/910513)》讲了时间轮的一些理论知识,然后根据理论知识。我们自己来实现一个简单的时间轮。
|
6天前
|
安全 Java API
java如何请求接口然后终止某个线程
通过本文的介绍,您应该能够理解如何在Java中请求接口并根据返回结果终止某个线程。合理使用标志位或 `interrupt`方法可以确保线程的安全终止,而处理好网络请求中的各种异常情况,可以提高程序的稳定性和可靠性。
36 6
|
21天前
|
设计模式 Java 开发者
Java多线程编程的陷阱与解决方案####
本文深入探讨了Java多线程编程中常见的问题及其解决策略。通过分析竞态条件、死锁、活锁等典型场景,并结合代码示例和实用技巧,帮助开发者有效避免这些陷阱,提升并发程序的稳定性和性能。 ####
|
19天前
|
存储 监控 小程序
Java中的线程池优化实践####
本文深入探讨了Java中线程池的工作原理,分析了常见的线程池类型及其适用场景,并通过实际案例展示了如何根据应用需求进行线程池的优化配置。文章首先介绍了线程池的基本概念和核心参数,随后详细阐述了几种常见的线程池实现(如FixedThreadPool、CachedThreadPool、ScheduledThreadPool等)的特点及使用场景。接着,通过一个电商系统订单处理的实际案例,分析了线程池参数设置不当导致的性能问题,并提出了相应的优化策略。最终,总结了线程池优化的最佳实践,旨在帮助开发者更好地利用Java线程池提升应用性能和稳定性。 ####
|
21天前
|
缓存 Java 开发者
Java多线程编程的陷阱与最佳实践####
本文深入探讨了Java多线程编程中常见的陷阱,如竞态条件、死锁和内存一致性错误,并提供了实用的避免策略。通过分析典型错误案例,本文旨在帮助开发者更好地理解和掌握多线程环境下的编程技巧,从而提升并发程序的稳定性和性能。 ####
下一篇
DataWorks