JAVA设计模式之建造者模式

简介: Java设计模式学习记录-建造者模式前言今天周末,有小雨,正好也不用出门了,那就在家学习吧,经过了两周的面试,拿到了几个offer,但是都不是自己很想去的那种,要么就是几个人的初创小公司,要么就是开发企业内部系统的这种传统开发,感觉这种传统开发已经不能给自己带来多大的提升了,因为工作了这几年这种系统经历了不少了,整天的就是增删改查。

Java设计模式学习记录-建造者模式

前言

今天周末,有小雨,正好也不用出门了,那就在家学习吧,经过了两周的面试,拿到了几个offer,但是都不是自己很想去的那种,要么就是几个人的初创小公司,要么就是开发企业内部系统的这种传统开发,感觉这种传统开发已经不能给自己带来多大的提升了,因为工作了这几年这种系统经历了不少了,整天的就是增删改查。创业小公司已经不想再去了,工作了这几年去的都是这种小公司,风险大,压力大,节奏快,没时间沉淀学习。上上家东家还欠我几个月工资呢,就是因为创业公司资金链断了,然后老板忽悠领导,领导再忽悠我们,后来实在发不出工资了,忽悠不住了,就大批大批的走人了。

所以现在很是纠结,大点公司又去不了小的公司还看不上,目前就是这么个高不成低不就的状态,所以还是抓紧时间学习,充实自己吧,哪怕现在进不去稍微大点的公司,那经过努力的学习后说不定还是有机会的,但是不努力是一点机会都没有的。

好了,言归正传,这次要介绍的是创建型设计模式的最后一个,建造者模式,这个模式其实我在平时开发中用的很多,只不过是用了这个模式的更深一种形式吧。后面我会介绍到这一部分内容的。

建造者模式

建造者模式能够将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。这句话理解起来可能有点抽象,简单来说就是调用相同的创建对象的方法(建造过程)可以创建出不同的对象。 

还是举例来说明吧,如果说我要创建一部手机,我需要先制造手机的几个核心部件,例如:屏幕、电池、听筒、话筒、机身等。

public class MobilePhone {

    //手机屏幕
    private String screen;
    //电池
    private String battery;
    //话筒
    private String microphone;
    //听筒
    private String phoneReceiver;
    //机身
    private String phoneBody;

    public String getScreen() {
        return screen;
    }

    public void setScreen(String screen) {
        this.screen = screen;
    }

    public String getBattery() {
        return battery;
    }

    public void setBattery(String battery) {
        this.battery = battery;
    }

    public String getMicrophone() {
        return microphone;
    }

    public void setMicrophone(String microphone) {
        this.microphone = microphone;
    }

    public String getPhoneReceiver() {
        return phoneReceiver;
    }

    public void setPhoneReceiver(String phoneReceiver) {
        this.phoneReceiver = phoneReceiver;
    }

    public String getPhoneBody() {
        return phoneBody;
    }

    public void setPhoneBody(String phoneBody) {
        this.phoneBody = phoneBody;
    }
}

每一部手机都是这个类的对象,在创建一部手机的时候都要保证这几个核心部件的创建。所以创建手机是需要一个标准规范的,因为这几个核心部件都可以是不同的型号,不同的型号的部件制造出来的手机也是不同的,这样就有了下面建造规范的接口。

public interface IBuildPhone {

    /**
     * 建造手机屏幕
     */
    void buildScreen();

    /**
     * 建造手机电池
     */
    void buildBattery();

    /**
     * 建造手机听筒
     */
    void buildMicrophone();

    /**
     * 建造手机话筒
     */
    void buildPhoneReceiver();

    /**
     * 建造手机机身
     */
    void buildPhoneBody();
}

有了规范了,就可以创建手机了,先创建一个iphoneX。

public class IPhoneX implements IBuildPhone {

    private MobilePhone mobilePhone;

    public IPhoneX(){
        mobilePhone =  new MobilePhone();
    }

    /**
     * 建造手机屏幕
     */
    @Override
    public void buildScreen() {
        mobilePhone.setScreen("OLED显示屏");
    }

    /**
     * 建造手机电池
     */
    @Override
    public void buildBattery() {
        mobilePhone.setBattery("2700mAh电池容量");
    }

    /**
     * 建造手机听筒
     */
    @Override
    public void buildMicrophone() {
        mobilePhone.setMicrophone("听筒");
    }

    /**
     * 建造手机话筒
     */
    @Override
    public void buildPhoneReceiver() {
        mobilePhone.setPhoneReceiver("话筒");
    }

    /**
     * 建造手机机身
     */
    @Override
    public void buildPhoneBody() {
        mobilePhone.setPhoneBody("iphoneX机身");
    }

    /**
     * 创建手机
     * @return
     */
    public MobilePhone build(){
        return mobilePhone;
    }
}

创建手机的工具写好了,下面就可以使用了。

public class Director {

    /**
     * 建造一部手机
     * @param buildPhone
     * @return
     */
    public MobilePhone createMobilePhone(IBuildPhone buildPhone){

        buildPhone.buildBattery();
        buildPhone.buildMicrophone();
        buildPhone.buildScreen();
        buildPhone.buildPhoneReceiver();
        buildPhone.buildPhoneBody();

        return buildPhone.createMobilePhone();
    }

    @Test
    public void thatTest(){
        System.out.println(JSON.toJSONString(createMobilePhone(new IPhoneX())));
    }
}

关键的方法在createMobilePhone()方法,这个方法接收一个IBuildPhone接口的对象,所以只要符合这个创建手机规范的对象都可以创建一部手机。createMobilePhone()方法可以接收new IPhoneX()这样一个对象,也可以接收new IPhone8()、new FindX()等等。

具体使用方法在thatTest()方法中。这个方法的运行结果是:

{"battery":"2700mAh电池容量","microphone":"听筒","phoneBody":"iphoneX机身","phoneReceiver":"话筒","screen":"OLED显示屏"}

上面这个例子的实现过程就使用了我们今天要说的建造者模式,我们来分析一下建造者模式的结构。

如下图:

在建造者模式的结构图中包含如下4个角色。

Builder(抽象建造者):它(IBuildPhone)为创建一个产品的各个部件指定了标准,规定了要创建复杂对象需要创建哪些部分,并不直接创建对象的具体部分。

ConcreteBuilder(具体建造者):它实现了Builder接口(IPhoneX),实现各个部分的具体构造和装配方法,定义并明确它所创建的复杂对象,也可以提供一个方法返回创建好的复杂产品对象。

Product(产品角色):它(MobilePhone)是被建造的复杂对象,包含多个组成部分,具体建造者创建该产品的内部表示并定义它的装配过程。

Director(指挥者):指挥者(Director),它复杂安排复杂对象的建造次序,指挥者与抽象建造者之间存在关联关系,可以在Director的方法中调用建造者对象的部件构造与装配方法,完成建造复杂对象的任务。客户端一般只需与Director进行交互。

建造者模式灵活使用

好了,建造者模式到这里就算是介绍完了,然后说一说我们平时在项目中是怎么使用建造者模式的。先说一下场景,我们一般在开发的过程中都是需要分层的,MVC这个不一般人都不陌生吧,Model-View-Controller。(我这里只是举例子不一定真的项目中就这样用)那我们的数据在每一层的传输过程中如果需要增加或删除些额外的功能怎么实现呢?

还是举例子吧,如下面一个实体类:

public class Person {
    
    private Long id;
    
    private String name;
    
    private int age;
    
    private String address;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    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 String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}

如果说这个类是一个orm框架需要的实体类,它最好的场景是只被后端的数据操作使用,但是controller中有一个add方法,这个方法是新增一个人员,add方法接收的参数是一个人员对象,但是这个对象和上面这个Person得属性有些差别,例如这个对象里面有请求ip,以及这个对象中没有id这个字段(id在数据库中自增,所以前端不允许传过来id )。这个时候就不能使用Person类的对象作为add的方法了,需要再创建一个类专门来给Controller使用。

如下代码:

/**
 * Controller使用的参数类
 */
public class PersonVO {

    private String name;

    private int age;

    private String address;

    //ip地址
    private String requestIP;

    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 String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getRequestIP() {
        return requestIP;
    }

    public void setRequestIP(String requestIP) {
        this.requestIP = requestIP;
    }
}

参数对象可以创建了, 但是PersonVO的对象是需要转成Person的对象的,这样才能插入到数据库中(数据库的insert方法的参数是Person对象)。这种转换操作其实也简单如下代码:

public Person convert2Person(PersonVO personVO){

        Person person = new Person();

        person.setName(personVO.getName());
        person.setAge(personVO.getAge());
        person.setAddress(personVO.getAddress());
        
        return person;
    }

但是我们通常是不这么做的,因为如果要转换的这个对象的字段很多那需要写很多次对象调setter方法来进行赋值。一种方式是直接写一个将所有属性当做参数的构造方法,直接一个一个把属性值传入就可以了,这种方式最简单暴力。还有一种方式就是需要包装一下这种方式,把Person改造一下。

如下代码:

public class Person {


    private Long id;

    private String name;

    private int age;

    private String address;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    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 String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

     Person(){}
     
     Person(String name,int age,String address){
        this.name = name;
        this.age = age;
        this.address = address;
    }

    public static Person builder(){
        return new Person();
    }

    public Person name(String name){
        this.name = name;
        return this;
    }

    public Person age(int age) {
        this.age = age;
        return this;
    }

    public Person address(String address) {
        this.address = address;
        return this;
    }

    public Person build(){
        return new Person(name,age,address);
    }
    
}

后面新增了两个构造函数,以及一个builder()方法和一个build()方法,还有几个赋值方法,需要注意的是赋值方法和setter方法的区别,这样的赋值方法是在赋值后将当前对象返回,用来实现链式调用。

这样在对象转换的时候就可以这样用了:

public Person convert2Person(PersonVO personVO){

        return Person.builder()
                .name(personVO.getName())
                .age(personVO.getAge())
          .address(personVO.getAddress())
                .build();
    }

这种方式其实也是一种建造者模式的应用,这种方式在构建对象的过程实现起来更灵活,例如如果这个对象就只有前两个参数有值,address是没有内容的,那可以直接这样写:

public Person convert2Person(PersonVO personVO){

        return Person.builder()
                .name(personVO.getName())
                .age(personVO.getAge())
                .build();
    }

在填充了两个属性后就直接调用build()方法区创建对象。

其实为了实现这种创建对象的方式,每次除了写getter/setter方法后还需要写这么多其他的代码,这样是有点麻烦的,所以在日常的开发过程中,我们是没必要写额外的代码来实现这种方式,可以用工具来实现。推荐一个工具包Lombok,我们的开发工具是使用IDEA,IDEA在使用Lombok时是需要下载一个lombok的插件,然后在项目中依赖lombok的工具包,就可以使用了。使用了lombok后的代码变的非常简洁,连getter/setter方法都不用写了。

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


    private Long id;

    private String name;

    private int age;

    private String address;

}

@Data 这个注解代表实现了所有非final属性的getter/setter方法,以及重写了toString方法和hashCode方法。

@AllArgsConstructor 这个注解代表实现了一个包含全部属性为参数的构造方法(Person(Long id,String name,int age, String address))。

@NoArgsConstructor 这个注解代表实现了一个没有任何参数的构造方法(Person())。

@Builder 这个注解代表实现了上面介绍的那种灵活的创建对象的建造者模式(使用这个注解时需要依赖上面3个注解,原因看这种方式的实现过程就能明白了)。

在创建对象时,使用方式没有变化也是链式调用方法赋值,这里就不再写创建对象的过程了。

其实lombok还有一些其他的注解也很强大,使用这个工具包的好处是,不但使代码变得简洁,也提高了开发效率。

在这里想到了jQuery插件倡导的那个原则:“写的更少,做的更多”

相关文章
|
28天前
|
设计模式 算法 Java
Java中的设计模式:提升代码质量的秘诀
【8月更文挑战第23天】在Java开发中,设计模式是提高代码可读性、可维护性和扩展性的强有力工具。本文通过浅显易懂的语言和实际案例,探讨几种常见的设计模式及其在Java中的应用,旨在帮助开发者更好地理解并运用这些模式来优化自己的代码结构。
39 2
|
6天前
|
设计模式 Java 关系型数据库
【Java笔记+踩坑汇总】Java基础+JavaWeb+SSM+SpringBoot+SpringCloud+瑞吉外卖/谷粒商城/学成在线+设计模式+面试题汇总+性能调优/架构设计+源码解析
本文是“Java学习路线”专栏的导航文章,目标是为Java初学者和初中高级工程师提供一套完整的Java学习路线。
|
2天前
|
设计模式 Java
Java设计模式:组合模式的介绍及代码演示
组合模式是一种结构型设计模式,用于将多个对象组织成树形结构,并统一处理所有对象。例如,统计公司总人数时,可先统计各部门人数再求和。该模式包括一个通用接口、表示节点的类及其实现类。通过树形结构和节点的通用方法,组合模式使程序更易扩展和维护。
Java设计模式:组合模式的介绍及代码演示
|
6天前
|
设计模式 安全 算法
【Java面试题汇总】设计模式篇(2023版)
谈谈你对设计模式的理解、七大原则、单例模式、工厂模式、代理模式、模板模式、观察者模式、JDK中用到的设计模式、Spring中用到的设计模式
【Java面试题汇总】设计模式篇(2023版)
|
6天前
|
设计模式 Java 关系型数据库
【Java笔记+踩坑】设计模式——原型模式
对比原型模式和传统方式的实现思路、代码方案、优缺点,阐述原型模式的使用场景,以及深拷贝、浅拷贝等相关概念,并扩展原型模式在Spring源码中的应用。
【Java笔记+踩坑】设计模式——原型模式
|
1月前
|
设计模式 存储 Java
【十】设计模式~~~结构型模式~~~享元模式(Java)
文章详细介绍了享元模式(Flyweight Pattern),这是一种对象结构型模式,通过共享技术实现大量细粒度对象的重用,区分内部状态和外部状态来减少内存中对象的数量,提高系统性能。通过围棋棋子的设计案例,展示了享元模式的动机、定义、结构、优点、缺点以及适用场景,并探讨了单纯享元模式和复合享元模式以及与其他模式的联用。
【十】设计模式~~~结构型模式~~~享元模式(Java)
|
1月前
|
设计模式 存储 Java
【九】设计模式~~~结构型模式~~~外观模式(Java)
文章详细介绍了外观模式(Facade Pattern),这是一种对象结构型模式,通过引入一个外观类来简化客户端与多个子系统之间的交互,降低系统的耦合度,并提供一个统一的高层接口来使用子系统。通过文件加密模块的实例,展示了外观模式的动机、定义、结构、优点、缺点以及适用场景,并讨论了如何通过引入抽象外观类来提高系统的可扩展性。
【九】设计模式~~~结构型模式~~~外观模式(Java)
|
21天前
|
设计模式 缓存 算法
揭秘策略模式:如何用Java设计模式轻松切换算法?
【8月更文挑战第30天】设计模式是解决软件开发中特定问题的可重用方案。其中,策略模式是一种常用的行为型模式,允许在运行时选择算法行为。它通过定义一系列可互换的算法来封装具体的实现,使算法的变化与客户端分离。例如,在电商系统中,可以通过定义 `DiscountStrategy` 接口和多种折扣策略类(如 `FidelityDiscount`、`BulkDiscount` 和 `NoDiscount`),在运行时动态切换不同的折扣逻辑。这样,`ShoppingCart` 类无需关心具体折扣计算细节,只需设置不同的策略即可实现灵活的价格计算,符合开闭原则并提高代码的可维护性和扩展性。
37 2
|
21天前
|
设计模式 Java
Java 设计模式之谜:工厂模式与抽象工厂模式究竟隐藏着怎样的神奇力量?
【8月更文挑战第30天】在Java编程中,设计模式为常见问题提供了高效解决方案。工厂模式与抽象工厂模式是常用的对象创建型设计模式,能显著提升代码的灵活性、可维护性和可扩展性。工厂模式通过定义创建对象的接口让子类决定实例化哪个类;而抽象工厂模式则进一步提供了一个创建一系列相关或相互依赖对象的接口,无需指定具体类。这种方式使得系统更易于扩展和维护。
30 1
|
21天前
|
设计模式 Java
重构你的代码:探索Java中的混合、装饰器与组合设计模式
【8月更文挑战第30天】在软件开发中,设计模式为特定问题提供了结构化的解决方案,使代码更易理解、维护及扩展。本文将介绍三种常用的 Java 设计模式:混合模式、装饰器模式与组合模式,并附有示例代码展示实际应用。混合模式允许通过继承多个接口或抽象类实现多重继承;装饰器模式可在不改变对象结构的情况下动态添加新功能;组合模式则通过树形结构表示部分-整体层次,确保客户端处理单个对象与组合对象时具有一致性。
15 1