建造者模式就是这么简单

上一篇文章中学习了三种工厂模式,这篇文章来学习一下另一种应用比较多的创建型模式,它适合用来创建拥有非常多的属性的对象。同样地,也是先从一个例子开始。

建造者模式

相信小伙伴们在假期出去游玩时,都会制定一套度假计划。一般来说,需要包括如下几个部分,VacationPlanner 类可以如下定义:

public class VacationPlanner {

    private int day; // 天数
    private String city; // 旅游城市
    private int ticket; // 车票
    private String hotel; // 住宿旅馆
    private String tourist; // 旅游景点
    private int personNum; // 人数
    private String other; // 其他
    
    // setter/getter/toString   
}
复制代码

如此,我们创建计划的方法 createPlanner 可以按照如下写:

public class Vacation {

    public VacationPlanner createPlanner(int day, String city, String hotel, int personNum, 
                              int ticket, String tourist, String other) {
        VacationPlanner planner = new VacationPlanner();
        planner.setDay(day);
        planner.setCity(city);
        planner.setHotel(hotel);
        planner.setPersonNum(personNum);
        planner.setTicket(ticket);
        planner.setTourist(tourist);
        planner.setOther(other);

        return planner;
    }
}
复制代码

很简单吧。但是如果很多人都想要制作一个度假计划,而每个人的计划又不太一样,所以,创建的度假计划及其创建步骤也就不同。例如,有的人就是本地人,不需要旅馆;有的人需要其他活动。

另外,其实我们并不需要知道度假计划的内部实现。那我们该如何做一个有弹性的设计,去适应各种变化呢?其实,我们可以创建一个单独的创建者,将度假计划的表示和创建计划的步骤分开来。

但在这之前,我们应该定义一个抽象建造者,也就是要符合之前我们所说的针对接口编程,而不是具体的类:

public abstract class AbstractVacationPlannerBuilder {

    public abstract void buildDay();
    public abstract void buildTicket();
    public abstract void buildHotel();
    public abstract void buildTourist();
    public abstract void buildPersonNum();
    public abstract void buildOther();
    public abstract VacationPlanner getVacationPlanner();
}
复制代码

然后,再来创建一个具体的建造者:

public class VacationPlannerBuilder extends AbstractVacationPlannerBuilder {

    private VacationPlanner vacationPlanner = new VacationPlanner();

    @Override
    public void buildDay(int day) {
        vacationPlanner.setDay(day);
    }
    @Override
    public void buildTicket(int ticket) {
        vacationPlanner.setTicket(ticket);
    }
    @Override
    public void buildHotel(String hotel) {
        vacationPlanner.setHotel(hotel);
    }
    @Override
    public void buildTourist(String tourist) {
        vacationPlanner.setTourist(tourist);
    }
    @Override
    public void buildPersonNum(int personNum) {
        vacationPlanner.setPersonNum(personNum);
    }   
    @Override
    public void buildOther(String other) {
        vacationPlanner.setOther(other);
    }

    @Override
    public VacationPlanner getVacationPlanner() {
        return vacationPlanner;
    }
}
复制代码

现在 Vacation 就可以按照如下的方式写了:

public class Vacation {

    private AbstractVacationPlannerBuilder builder;
    
    public void setVacationPlannerBuilder(AbstractVacationPlannerBuilder builder) {
        this.builder = builder;
    }

    public AbstractVacationPlannerBuilder createPlanner(int day, String city, String hotel, int personNum,
                                         int ticket, String tourist, String meal, String other) {
        builder.buildDay(day);
        builder.buildCity(city);
        builder.buildHotel(hotel);
        builder.buildPersonNum(personNum);
        builder.buildTicket(ticket);
        builder.buildTourist(tourist);
        builder.buildOther(other);
        
        return builder;
    }
}
复制代码

上述模式就是建造者模式,它是将一个复杂对象的表示和创建分离开来,允许通过多个步骤来创建对象,并且可以改变其过程。

它的 UML 图如下:

下面来总结一下建造者模式的优点:

  • 建造者模式将一个复杂对象的创建过程封装起来;
  • 向客户端隐藏了产品内部的实现,将产品本身与产品的创建过程分离开来;
  • 可以更加精确地控制产品的创建过程,将复杂产品的创建步骤分解在不同的方法中;

缺点:

  • 在建造者模式中,如果产品之间的差异性太大,则不适合使用建造者模式。
  • 如果产品的内部变化复杂,可能需要定义很多建造者类来实现这些变化,导致系统变得很庞大。

静态内部类方式

另外,建造者模式也可以使用静态内部类的形式,它是将实体类对应的 Builder 放置到实体类的内部,看一下它的代码:

public class VacationPlanner {

    private int day; // 天数
    private String city; // 旅游城市
    private int ticket; // 车票
    private String hotel; // 住宿旅馆
    private String tourist; // 旅游景点
    private int personNum; // 人数
    private String other; // 其他
    
    // 全属性的构造方法
    public VacationPlanner(VacationPlannerBuilder builder) {
        this.day = builder.day;
        this.city = builder.city;
        this.ticket = builder.ticket;
        this.hotel = builder.hotel;
        this.tourist = builder.tourist;
        this.personNum = builder.personNum;
        this.other = builder.other;
    }

    public static class VacationPlannerBuilder {

        private int day; // 天数
        private String city; // 旅游城市
        private int ticket; // 车票
        private String hotel; // 住宿旅馆
        private String tourist; // 旅游景点
        private int personNum; // 人数
        private String other; // 其他

        // 与之前不同,这里返回 VacationPlannerBuilder
        public VacationPlannerBuilder buildDay(int day) {
            this.day = day;
            return this;
        }
        public VacationPlannerBuilder buildCity(String city) {
            this.city = city;
            return this;
        }
        public VacationPlannerBuilder buildTicket(int ticket) {
            this.ticket = ticket;
            return this;
        }
        public VacationPlannerBuilder buildHotel(String hotel) {
            this.hotel = hotel;
            return this;
        }
        public VacationPlannerBuilder buildTourist(String tourist) {
            this.tourist = tourist;
            return this;
        }
        public VacationPlannerBuilder buildPersonNum(int personNum) {
            this.personNum = personNum;
            return this;
        }
        public VacationPlannerBuilder buildOther(String other) {
            this.other = other;
            return this;
        }
        
        public VacationPlanner build() {
            return new VacationPlanner(this);
        }
    }
}
复制代码

下面来写个测试类进行测试:

public class Test {

    public static void main(String[] args) {
        VacationPlanner planner = new VacationPlanner.VacationPlannerBuilder()
                .buildDay(3)
                .buildCity("北京")
                .buildHotel("7天")
                .buildPersonNum(2)
                .buildTicket(999)
                .buildTourist("故宫")
                .buildOther("其他")
                .build();
        System.out.println(planner);
    }
}
复制代码

建造者模式的具体实践

JDK#StringBuilder

AbstractStringBuilder

可以将 StringBuilder 看成 String 的建造者,而 AbstractStringBuilder 是建造者的抽象类。它的部分源码如下:

abstract class AbstractStringBuilder implements Appendable, CharSequence {
    // 存储 char 字符的数组
    char[] value;
    int count;
    
    public AbstractStringBuilder append(String str) {
        if (str == null)
            return appendNull();
        int len = str.length();
        ensureCapacityInternal(count + len); // 确保容量
        str.getChars(0, len, value, count);
        count += len;
        return this;
    }
    public AbstractStringBuilder append(int i) {
        if (i == Integer.MIN_VALUE) {
            append("-2147483648");
            return this;
        }
        int appendedLength = (i < 0) ? Integer.stringSize(-i) + 1
                                     : Integer.stringSize(i);
        int spaceNeeded = count + appendedLength;
        ensureCapacityInternal(spaceNeeded);
        Integer.getChars(i, spaceNeeded, value);
        count = spaceNeeded;
        return this;
    }
    
    @Override
    public abstract String toString();
    
}
复制代码

StringBuilder

StringBuilder 的部分源码如下,这里使用的建造者模式是我们之前看到的第一种模式:

public final class StringBuilder
        extends AbstractStringBuilder
        implements java.io.Serializable, CharSequence {
        
    @Override
    public StringBuilder append(String str) {
        super.append(str);
        return this;
    }
    @Override
    public StringBuilder append(int i) {
        super.append(i);
        return this;
    }
    
    @Override
    public String toString() {
        // 创建了一个新的 String
        return new String(value, 0, count);
    }
}
复制代码

Guava#ImmutableSet

ImmutableSet 的建造者使用的是静态内部类方式:

public abstract class ImmutableSet<E> extends ImmutableCollection<E> implements Set<E> {

    public static <E> Builder<E> builder() {
        return new Builder<E>();
    }

    public static class Builder<E> extends ImmutableCollection.ArrayBasedBuilder<E> {
        
        @Override
        public Builder<E> add(E element) {
          super.add(element);
          return this;
        }
        @Override
        public Builder<E> addAll(Iterable<? extends E> elements) {
          super.addAll(elements);
          return this;
        }
        @Override
        public ImmutableSet<E> build() {
            ImmutableSet<E> result = construct(size, contents);
            size = result.size();
            return result;
        }
    }   
    
    private static <E> ImmutableSet<E> construct(int n, Object... elements) {
        // 返回不同的 ImmutableSet 实现
    }
}
复制代码

一般它有两种创建方法:

ImmutableSet<Object> set1 = new ImmutableSet.Builder<>().build();

ImmutableSet<Object> set2 = ImmutableSet.builder().build();
复制代码

参考资料

  • 《Head First 设计模式》

转载于:https://juejin.im/post/5cd808ab6fb9a0324936d1a0

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
内容简介: 设计模式是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性。 本课程内容定位学习设计原则,学习设计模式的基础。在实际开发过程中,并不是一定要求所有代码都遵循设计原则,我们要考虑人力、时间、成本、质量,不是刻意追求完美,要在适当的场景遵循设计原则,体现的是一种平衡取舍,帮助我们设计出更加优雅的代码结构。本章将详细介绍开闭原则(OCP)、依赖倒置原则(DIP)、单一职责原则(SRP)、接口隔离原则(ISP)、迪米特法则(LoD)、里氏替换原则(LSP)、合成复用原则(CARP)的具体内容。 为什么需要学习这门课程? 你在日常的开发中,会不会也遇到过同样的问题。系统出现问题,不知道问题究竟出在什么位置;当遇到产品需求,总是对代码缝缝补补,不能很快的去解决。而且平时工作中,总喜欢把代码堆在一起,出现问题时,不知道如何下手,工作效率很低,而且自己的能力也得不到提升。而这些都源于一个问题,那就是软件设计没做好。这门课能帮助你很好的认识设计模式,让你的能力得到提升。课程大纲: 为了让大家快速系统了解设计模式知识全貌,我为您总结了思维导图,帮您梳理学习重点,建议收藏!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值