【Java设计模式】建造者模式 & 注解@Builder

这篇具有很好参考价值的文章主要介绍了【Java设计模式】建造者模式 & 注解@Builder。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

概念

  • 将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示。它使将一个复杂的对象分解成多个简单的对象,然后一步步构建而成。

  • 每一个具体建造者都相对独立,而与其它的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者,用户使用不同的具体建造者即可得到不同的产品对象。由于指挥者类针对抽象建造者编程,增加新的具体建造者无须修改原有类库的代码,系统扩展方便,符合“开闭原则”。

未用建造者模式

  • 以下举个最简单的例子:电脑配件(包括品牌、价格、描述)、组装电脑。

电脑接口

/**
 * 电脑接口
 */
public interface Computer {

    /**
     * 组件(主机Host、显示器Monitor、鼠标Mouse、键盘Keyboard)
     */
    String parts();

    /**
     * 品牌
     */
    String brand();

    /**
     * 价格
     */
    Double price();

    /**
     * 描述
     */
    String desc();
}

主机Host

/**
 * 惠普主机
 */
public class HPHost implements Computer {

    @Override
    public String parts() {
        return "惠普主机";
    }

    @Override
    public String brand() {
        return "惠普品牌";
    }

    @Override
    public Double price() {
        return 6999.00;
    }

    @Override
    public String desc() {
        return "HP Computer Welcome";
    }
}

/**
 * 联想主机
 */
public class LenovoHost implements Computer {

    @Override
    public String parts() {
        return "联想主机";
    }

    @Override
    public String brand() {
        return "联想品牌";
    }

    @Override
    public Double price() {
        return 6899.00;
    }

    @Override
    public String desc() {
        return "Lenovo Computer Welcome";
    }
}

显示器Monitor

/**
 * 小米显示器
 */
public class RedmiMonitor implements Computer {

    @Override
    public String parts() {
        return "小米显示器";
    }

    @Override
    public String brand() {
        return "小米品牌";
    }

    @Override
    public Double price() {
        return 1399.00;
    }

    @Override
    public String desc() {
        return "Redmi Monitor Welcome";
    }
}

/**
 * 华硕显示器
 */
public class ROGMonitor implements Computer {

    @Override
    public String parts() {
        return "华硕显示器";
    }

    @Override
    public String brand() {
        return "华硕品牌";
    }

    @Override
    public Double price() {
        return 1899.00;
    }

    @Override
    public String desc() {
        return "ROG Monitor Welcome";
    }
}

鼠标Monse

/**
 * 罗技鼠标
 */
public class GMouse implements Computer {

    @Override
    public String parts() {
        return "罗技鼠标";
    }

    @Override
    public String brand() {
        return "罗技品牌";
    }

    @Override
    public Double price() {
        return 139.00;
    }

    @Override
    public String desc() {
        return "G Mouse Welcome";
    }
}

/**
 * 联想鼠标
 */
public class LenovoMouse implements Computer {

    @Override
    public String parts() {
        return "联想鼠标";
    }

    @Override
    public String brand() {
        return "联想品牌";
    }

    @Override
    public Double price() {
        return 89.00;
    }

    @Override
    public String desc() {
        return "Lenovo Mouse Welcome";
    }
}

键盘Keyboard

/**
 * 罗技键盘
 */
public class GKeyboard implements Computer {

    @Override
    public String parts() {
        return "罗技键盘";
    }

    @Override
    public String brand() {
        return "罗技品牌";
    }

    @Override
    public Double price() {
        return 239.00;
    }

    @Override
    public String desc() {
        return "G Keyboard Welcome";
    }
}

/**
 * 惠普键盘
 */
public class HPKeyboard implements Computer {

    @Override
    public String parts() {
        return "惠普键盘";
    }

    @Override
    public String brand() {
        return "惠普品牌";
    }

    @Override
    public Double price() {
        return 89.00;
    }

    @Override
    public String desc() {
        return "HP Keyboard Welcome";
    }
}

组装电脑

**
 * 组装电脑
 * 不同的套装配不同的设备
 */
public class PackageComputer {

    /**
     * 根据套餐数字对应返回整套电脑配置详情
     *
     * @param choose 套餐数字
     * @return 电脑配置
     */
    public String getComputer(Integer choose) {
        // 价格初始值
        double price;
        // 组装电脑配件
        List<Computer> parts = new ArrayList<>();

        StringBuilder stringBuilder = new StringBuilder();

        if(choose == 1) {
            HPHost hpHost = new HPHost();
            RedmiMonitor redmiMonitor = new RedmiMonitor();
            LenovoMouse lenovoMouse = new LenovoMouse();
            HPKeyboard hpKeyboard = new HPKeyboard();

            // 组装电脑
            parts.add(hpHost);
            parts.add(redmiMonitor);
            parts.add(lenovoMouse);
            parts.add(hpKeyboard);

            // 计算价格
            price = hpHost.price() + redmiMonitor.price() + lenovoMouse.price() + hpKeyboard.price();

            stringBuilder.append("套餐为:" + choose + "号套餐\r\n");
            stringBuilder.append("配件如下:\r\n");
            for(Computer c : parts) {
                stringBuilder.append(c.parts() + "、");
                stringBuilder.append(c.brand() + "、");
                stringBuilder.append(c.price() + "、");
                stringBuilder.append(c.desc() + "\r\n");
            }
            stringBuilder.append("总价格为:" + price + "RMB\r\n");
        } else if(choose == 2) {
            LenovoHost lenovoHost = new LenovoHost();
            ROGMonitor rogMonitor = new ROGMonitor();
            GMouse gMouse = new GMouse();
            GKeyboard gKeyboard = new GKeyboard();

            // 组装电脑
            parts.add(lenovoHost);
            parts.add(rogMonitor);
            parts.add(gMouse);
            parts.add(gKeyboard);

            // 计算价格
            price = lenovoHost.price() + rogMonitor.price() + gMouse.price() + gKeyboard.price();

            stringBuilder.append("套餐为:" + choose + "号套餐\r\n");
            stringBuilder.append("配件如下:\r\n");
            for(Computer c : parts) {
                stringBuilder.append(c.parts() + "、");
                stringBuilder.append(c.brand() + "、");
                stringBuilder.append(c.price() + "、");
                stringBuilder.append(c.desc() + "\r\n");
            }
            stringBuilder.append("总价格为:" + price + "RMB\r\n");
        } else if(choose == 3) {
            LenovoHost lenovoHost = new LenovoHost();
            RedmiMonitor redmiMonitor = new RedmiMonitor();
            GMouse gMouse = new GMouse();
            LenovoMouse lenovoMouse = new LenovoMouse();

            // 组装电脑
            parts.add(lenovoHost);
            parts.add(redmiMonitor);
            parts.add(gMouse);
            parts.add(lenovoMouse);

            // 计算价格
            price = lenovoHost.price() + redmiMonitor.price() + gMouse.price() + lenovoMouse.price();

            stringBuilder.append("套餐为:" + choose + "号套餐\r\n");
            stringBuilder.append("配件如下:\r\n");
            for(Computer c : parts) {
                stringBuilder.append(c.parts() + "、");
                stringBuilder.append(c.brand() + "、");
                stringBuilder.append(c.price() + "、");
                stringBuilder.append(c.desc() + "\r\n");
            }
            stringBuilder.append("总价格为:" + price + "RMB\r\n");
        }
        return stringBuilder.toString();
    }
}

测试

public class BuilderDesign {
    public static void main(String[] args) {
        PackageComputer computer = new PackageComputer();
        System.out.println(computer.getComputer(1));
        System.out.println("=======================================================");
        System.out.println(computer.getComputer(2));
        System.out.println("=======================================================");
        System.out.println(computer.getComputer(3));
    }
}

【Java设计模式】建造者模式 & 注解@Builder,设计模式,设计模式,建造者模式,java

使用建造者模式

  • 从上面可以看出来,电脑的每个配件都要去建对应的类。例子中我给了主机、显示器、鼠标、键盘四种部件,每个部件假设两种品牌,就写了 2 * 4 = 8个类。虽说不会是指数型增长,但是无论哪个增加都会是很明显的增长趋势。而且在组装电脑时,要根据每个不同要求的去返回对应的信息,每一个if语句都有二十行代码左右,看起来十分臃肿。

  • 接下来将会用到建造者模式去优化上面的代码量。

组装电脑接口

public interface IComputer {

    /**
     * 主机
     */
    IComputer appendHost(Computer computer);

    /**
     * 显示器
     */
    IComputer appendMonitor(Computer computer);

    /**
     * 鼠标
     */
    IComputer appendMouse(Computer computer);

    /**
     * 键盘
     */
    IComputer appendKeyboard(Computer computer);

    /**
     * @return 电脑清单
     */
    String computerDetail();
}

建造者组装电脑

/**
 * 建造者组装电脑
 */
public class BuilderComputer implements IComputer{

    List<Computer> parts = new ArrayList<>();
    private double price = 0.00;
    private Integer choose;

    public BuilderComputer(){}

    public BuilderComputer(Integer choose) {
        this.choose = choose;
    }

    @Override
    public IComputer appendHost(Computer computer) {
        parts.add(computer);
        price = price + computer.price();
        return this;
    }

    @Override
    public IComputer appendMonitor(Computer computer) {
        parts.add(computer);
        price = price + computer.price();
        return this;
    }

    @Override
    public IComputer appendMouse(Computer computer) {
        parts.add(computer);
        price = price + computer.price();
        return this;
    }

    @Override
    public IComputer appendKeyboard(Computer computer) {
        parts.add(computer);
        price = price + computer.price();
        return this;
    }

    @Override
    public String computerDetail() {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("套餐为:" + choose + "号套餐\r\n");
        stringBuilder.append("配件如下:\r\n");
        for(Computer c : parts) {
            stringBuilder.append(c.parts() + "、");
            stringBuilder.append(c.brand() + "、");
            stringBuilder.append(c.price() + "、");
            stringBuilder.append(c.desc() + "\r\n");
        }
        stringBuilder.append("总价格为:" + price + "RMB\r\n");
        return stringBuilder.toString();
    }
}

建造者

        去掉了繁琐的if else,符合单一职责原则、开闭原则,代码可读性、复用性、拓展性强。这里面就完美的展示了什么叫做将一个复杂对象的构造与它的表示分离。并且链式编程的语法比不断的set()要美观得多,这会在后续Lambok中的@Builder中进行说明。

/**
 * 建造者
 */
public class Builder {

    /**
     * @return 一号套餐
     */
    public IComputer chooseOne() {
        return new BuilderComputer(1)
                .appendHost(new HPHost())
                .appendMonitor(new RedmiMonitor())
                .appendMouse(new LenovoMouse())
                .appendKeyboard(new HPKeyboard());
    }

    /**
     * @return 二号套餐
     */
    public IComputer chooseTwo() {
        return new BuilderComputer(2)
                .appendHost(new LenovoHost())
                .appendMonitor(new ROGMonitor())
                .appendMouse(new GMouse())
                .appendKeyboard(new GKeyboard());
    }

    /**
     * @return 三号套餐
     */
    public IComputer chooseThree() {
        return new BuilderComputer(3)
                .appendHost(new LenovoHost())
                .appendMonitor(new RedmiMonitor())
                .appendMouse(new GMouse())
                .appendKeyboard(new LenovoMouse());
    }
}

测试

public class BuilderDesign {
    public static void main(String[] args) {
        Builder builder = new Builder();
        System.out.println(builder.chooseOne().computerDetail());
        System.out.println("=======================================================");
        System.out.println(builder.chooseTwo().computerDetail());
        System.out.println("=======================================================");
        System.out.println(builder.chooseThree().computerDetail());
    }
}

@Builder

        此注解是Lombok依赖下的,而Lombok基本是各个公司都会使用到的工具包。可以用来简化开发。上面的建造者组装电脑的示例代码就是链式编程的关键之处:每个方法除了会传参还会返回this自身。我创建了一个用户User类,其带有六个属性。

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class User {

    private String username;

    private String sex;

    private Integer age;

    private String address;

    private String qq;

    private String email;

}

底层

        为了验证此注解背后的样子,最简单的实践方法就是加上此注解然后查看编译后的class文件中的代码。等编译后我发现多了以下内容。会发现多了一个静态内部类UserBuilder以及返回User.UserBuilder的build()方法

【Java设计模式】建造者模式 & 注解@Builder,设计模式,设计模式,建造者模式,java

【Java设计模式】建造者模式 & 注解@Builder,设计模式,设计模式,建造者模式,java

        其实User中的builder()方法以及User类的静态内部类UserBuilder的build()方法。这两个方法名在@Builder注解中已经是默认的值了。并且或者注解可以用于类、普通方法和构造方法上。关于其底层是如何在User类中生成静态内部类并且具体的方法代码块就不深究Lombok中的源码了。这里我需要强调的是使用建造者赋值的时候就是赋值给其内部类属性的

【Java设计模式】建造者模式 & 注解@Builder,设计模式,设计模式,建造者模式,java

优势

可读性好

        其实当使用过@Builder这个注解的时候就已经可以感受到它的好处之一了:美观且可读性高。这里我使用了三种创建对象的方式来作比较出优劣处。

        第一个User对象使用有参构造的真是长的让人反胃,甚至如果在真实的复杂业务场景中,还不知道其中一个参数是什么含义,还需要点进去看注释。并且自己使用这种有参构造的话,如果没有背下来每个位置要放什么参数那就更麻烦了。所以说有参构造的劣势就是:可读性差、参数过多可能导致传递错误。

        第二个User对象就是一直Setter。相比于第三种而言没有那么好的可读性。所以说使用建造者模式的链式编程可读性好。但是要记住建造者模式的赋值是给其内部类属性的

public class BuilderDesign {
    public static void main(String[] args) {
        User u1 = new User("张三x", "男", 18, "福建省厦门市xxx镇xxxx小区x楼xxx号", "465795464", "465795464@qq.com");

        User u2 = new User();
        u2.setUsername("李四");
        u2.setSex("女");
        u2.setAge(20);
        u2.setAddress("福建省泉州市xxx镇xxxx小区x楼xxx号");
        u2.setQq("504899214");
        u2.setEmail("504899214@qq.com");

        User u3 = User.builder()
                .username("王五")
                .sex("男")
                .age(22)
                .address("福建省福州市xxx镇xxxx小区x楼xxx号")
                .qq("684354768")
                .email("684354768@qq.com")
                .build();
    }
}

JavaBean创建

        我曾在某个地方看到一个大佬说过使用set()方法注入属性和静态内部类Builder注入属性值的区别,但具体怎么说的已经忘记了,

        这里由衷希望看到这里的读者可以在评论里说一下关于JavaBean赋值可能涉及到的线程安全问题或者其它问题。谢谢。

避坑

        在上面有说过一个问题就是:使用builder()方法赋值是赋值给其静态内部类建造者类的。那么这句话是什么意思呢?这句话的意思就是当我们在实体类上已经附带初始值了,但是使用建造者模式去构建实体类打印toString()方法出来的时候是看到为类加载的初始值的(比如0/false/null等)。具体看以下代码以及控制台输出。

public class BuilderDesign {
    public static void main(String[] args) {
        User u = User.builder()
                .username("王五")
                .sex("男")
                .address("福建省福州市xxx镇xxxx小区x楼xxx号")
                .qq("684354768")
                .email("684354768@qq.com")
                .build();
        System.out.println(u);
    }
}

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class User {

    private String username;
    private String sex;
    private Integer age = 30;
    private String address;
    private String qq;
    private String email;

}

【Java设计模式】建造者模式 & 注解@Builder,设计模式,设计模式,建造者模式,java

        可以看到age = null。因为age是包装类型Integer,所以类加载时的初始值为null,而不是0。这里的原因就是User的age属性初始值为30,但是其内部的UserBuilder类的age属性并没有,所以导致获取到的User对象的age属性为初始值null。为了避免这个情况发生,@Builder注解中有一个内部注解来解决这个问题,就是@Builder.Default。只需要在设置初始值的属性上使用此注解即可。编译生成的User对象会多生成个静态的$default$age()方法。

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class User {

    private String username;
    private String sex;

    @Builder.Default
    private Integer age = 30;

    private String address;
    private String qq;
    private String email;

}

【Java设计模式】建造者模式 & 注解@Builder,设计模式,设计模式,建造者模式,java文章来源地址https://www.toymoban.com/news/detail-634442.html

到了这里,关于【Java设计模式】建造者模式 & 注解@Builder的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 设计模式——建造者模式(Builder Pattern)

    概述        建造者模式是较为复杂的创建型模式,它将客户端与包含多个组成部分(或部件)的复杂对象的创建过程分离,客户端无须知道复杂对象的内部组成部分与装配方式,只需要知道所需建造者的类型即可。它关注如何一步一步创建一个的复杂对象,不同的具体建

    2024年01月20日
    浏览(37)
  • 设计模式|建造者模式(Builder Pattern)

    建造者模式(Builder Pattern)是一种创建型设计模式,用于将一个复杂对象的构建过程与其表示分离,以便可以使用相同的构建过程创建不同的表示。 Builder(建造者)接口或抽象类 : 定义了构建对象的各个步骤的方法。 ConcreteBuilder(具体建造者)类 : 实现了 Builder 接口或继

    2024年04月15日
    浏览(45)
  • 设计模式五:建造者模式(Builder Pattern)

    建造者模式(Builder Pattern)是一种创建型设计模式,用于通过一系列步骤来构建复杂对象。它将对象的构建过程与其表示分离,从而允许相同的构建过程可以创建不同的表示。 建造者模式中的几个角色: 产品(Product):表示被构建的复杂对象。 抽象建造者(Builder):定义了构建复

    2024年02月15日
    浏览(27)
  • (一)创建型设计模式:3、建造者模式(Builder Pattern)

    目录 1、建造者模式含义 2、建造者模式的讲解 3、使用C++实现建造者模式的实例 4、建造者模式的优缺点 5、建造者模式VS工厂模式 1、建造者模式含义 The intent of the Builder design pattern is to separate the construction of a complex object from its representation. By doing so the same construction process ca

    2024年02月13日
    浏览(26)
  • Java设计模式-建造者模式

    建造者模式是一种创建型设计模式,用于将复杂对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示。建造者模式通过将复杂对象的构建过程分解为多个简单的步骤来实现。 与其他创建型模式不同,建造者模式强调的是将构建过程与表示分离,而不是将对

    2024年02月02日
    浏览(28)
  • 03-JAVA设计模式-建造者模式

    建造者模式(Builder Pattern)是一种对象构建的设计模式,它允许你通过一步一步地构建一个复杂对象,来隐藏复杂对象的创建细节。 这种模式将一个复杂对象的构建过程与其表示过程分离,使得同样的构建过程可以创建不同的表示。命名建议以Builder结尾,以达到见名之意。

    2024年04月15日
    浏览(49)
  • Java设计模式之一:建造者模式

    目录 一、什么是建造者模式 二、建造者模式如何使用 三、建造者模式的优势和应用场景 Java建造者模式是一种创建对象的设计模式,它通过将对象的构造过程分离出来,使得同样的构建过程可以创建不同的表示。建造者模式适用于创建复杂对象,它将对象的构建过程分解成

    2024年02月13日
    浏览(35)
  • Java 设计模式之建造者模式

            在软件开发中,设计模式是一种被广泛使用的解决问题的方法。设计模式帮助我们更好地组织和管理代码,提供了一种可靠和可复用的设计方案。在本篇文章中,我们将重点介绍一种常用的设计模式——建造者模式,并通过一个实际的案例来演示其应用。     

    2024年02月13日
    浏览(31)
  • java设计模式之 - 建造者模式

    建造者模式(Builder Pattern)是一种创建型设计模式,它通过将对象的构建过程分离出来,使得同样的构建过程可以创建不同的表示形式。 简单来说,建造者模式允许你按步骤创建复杂的对象,同时将对象的构建与其表示分离。 主要解决的问题是当创建一个复杂对象时,如果

    2024年02月16日
    浏览(46)
  • Java建造者设计模式

    建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。一个 Builder 类会一步一步构造最终的对象。该 Builder 类是独立于其他对象的。 Builder(抽象建造者) :它为创建一个产

    2024年02月15日
    浏览(21)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包