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插件倡导的那个原则:“写的更少,做的更多”

相关文章
|
17天前
|
设计模式 Java 开发者
设计模式揭秘:Java世界的七大奇迹
【4月更文挑战第7天】探索Java设计模式:单例、工厂方法、抽象工厂、建造者、原型、适配器和观察者,助你构建健壮、灵活的软件系统。了解这些模式如何提升代码复用、可维护性,以及在特定场景下的应用,如资源管理、接口兼容和事件监听。掌握设计模式,但也需根据实际情况权衡,打造高效、优雅的软件解决方案。
|
18天前
|
设计模式 存储 Java
23种设计模式,享元模式的概念优缺点以及JAVA代码举例
【4月更文挑战第6天】享元模式(Flyweight Pattern)是一种结构型设计模式,旨在通过共享技术有效地支持大量细粒度对象的重用。这个模式在处理大量对象时非常有用,特别是当这些对象中的许多实例实际上可以共享相同的状态时,从而可以减少内存占用,提高程序效率
33 4
|
18天前
|
设计模式 Java 中间件
23种设计模式,适配器模式的概念优缺点以及JAVA代码举例
【4月更文挑战第6天】适配器模式(Adapter Pattern)是一种结构型设计模式,它的主要目标是让原本由于接口不匹配而不能一起工作的类可以一起工作。适配器模式主要有两种形式:类适配器和对象适配器。类适配器模式通过继承来实现适配,而对象适配器模式则通过组合来实现
30 4
|
22天前
|
设计模式 Java 数据库
Java设计模式精讲:让代码更优雅、更可维护
【4月更文挑战第2天】**设计模式是解决软件设计问题的成熟方案,分为创建型、结构型和行为型。Java中的单例模式确保类仅有一个实例,工厂方法模式让子类决定实例化哪个类。适配器模式则协调不兼容接口间的合作。观察者模式实现了一对多依赖,状态变化时自动通知相关对象。学习和适当应用设计模式能提升代码质量和可维护性,但需避免过度使用。设计模式的掌握源于实践与不断学习。**
Java设计模式精讲:让代码更优雅、更可维护
|
25天前
|
设计模式 安全 Java
在Java中即指单例设计模式
在Java中即指单例设计模式
18 0
|
17天前
|
设计模式 监控 Java
设计模式 - 观察者模式(Observer):Java中的战术与策略
【4月更文挑战第7天】观察者模式是构建可维护、可扩展系统的关键,它在Java中通过`Observable`和`Observer`实现对象间一对多的依赖关系,常用于事件处理、数据绑定和同步。该模式支持事件驱动架构、数据同步和实时系统,但需注意避免循环依赖、控制通知粒度,并关注性能和内存泄漏问题。通过明确角色、使用抽象和管理观察者注册,可最大化其效果。
|
1月前
|
设计模式 存储 安全
Java设计模式---结构型模式
Java设计模式---结构型模式
|
6天前
|
设计模式 算法 Java
Java中的设计模式及其应用
【4月更文挑战第18天】本文介绍了Java设计模式的重要性及分类,包括创建型、结构型和行为型模式。创建型模式如单例、工厂方法用于对象创建;结构型模式如适配器、组合关注对象组合;行为型模式如策略、观察者关注对象交互。文中还举例说明了单例模式在配置管理器中的应用,工厂方法在图形编辑器中的使用,以及策略模式在电商折扣计算中的实践。设计模式能提升代码可读性、可维护性和可扩展性,是Java开发者的必备知识。
|
9天前
|
设计模式 算法 Java
小谈设计模式(30)—Java设计模式总结
小谈设计模式(30)—Java设计模式总结
|
10天前
|
设计模式 存储 Java
Java设计模式:解释一下单例模式(Singleton Pattern)。
`Singleton Pattern`是Java中的创建型设计模式,确保类只有一个实例并提供全局访问点。它通过私有化构造函数,用静态方法返回唯一的实例。类内静态变量存储此实例,对外仅通过静态方法访问。
15 1