前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >04-04-设计模式 建造者模式

04-04-设计模式 建造者模式

作者头像
彼岸舞
发布2022-05-28 09:21:08
2140
发布2022-05-28 09:21:08
举报
文章被收录于专栏:java开发的那点事

需求

需要建造房子, 过程为: 打桩->砌墙->封顶

房子有各式各样的, 比如普通房, 高楼, 别墅, 过程虽然一样, 要是要求不要相同的

传统方式解决

新建抽象类

代码语言:javascript
复制
package com.dance.design.designmodel.buildmodel;

import lombok.Builder;
import lombok.Data;

/**
 * 抽象房子
 */
public abstract class AbsHouse {

    /**
     * 建造
     */
    public void build(){
        buildBasic();
        buildWalls();
        buildTop();
    }

    /**
     * 打桩
     * @param basic 地基
     */
    public abstract void buildBasic();

    /**
     * 砌墙
     * @param walls 墙
     */
    public abstract void buildWalls();

    /**
     * 封顶
     * @param top 顶部颜色
     */
    public abstract void buildTop();
}

新建实现类

代码语言:javascript
复制
package com.dance.design.designmodel.buildmodel;

public class CommonHouse extends AbsHouse{

    @Override
    public void buildBasic() {
        System.out.println("建造普通房子的地基");
    }

    @Override
    public void buildWalls() {
        System.out.println("建造普通房子的墙");
    }

    @Override
    public void buildTop() {
        System.out.println("建造普通房子的顶");
    }
}

新建客户端

代码语言:javascript
复制
package com.dance.design.designmodel.buildmodel;

public class Client {
    public static void main(String[] args) {
        AbsHouse absHouse = new CommonHouse();
        absHouse.build();
    }
}

传统方式的问题分析

  1. 优点是比较好理解, 简单易操作
  2. 设计的程序结构, 过于简单, 没有设计缓存层对象, 程序的扩展和维护不好, 也就是说这种设计方案把产品(房子)和创建的过程封装在一起,耦合性增强了
  3. 解决方案: 将产品和产品的建造过程解耦 => 建造者模式

建造者模式

基本介绍

  1. 建造者模式又叫生成器模式,是一种对象构建模式,他可以将复杂的对象的建造过程抽象出来(抽象类别), 使这个抽象过程的不同实现方法可以构造出不同表现(属性)的对象
  2. 建造者模式是一步一步创建一个复杂的对象, 它允许用户只通过指定复杂对象的类型和内容就可以构建他们,用户不需要知道内部的具体构建细节

建造者模式的四个角色

  1. Product(产品角色) : 一个具体的产品对象
  2. Builder(抽象建造者) : 创建一个Product对象的各个部件指定的 接口/抽象类
  3. ConcreateBuilder(具体建造者): 实现接口, 构建和装配各个部件
  4. Director(指挥者): 构建一个使用Builder接口的对象, 它主要是用于创建一个复杂的对象, 它主要有两个作用
    1. 隔离了客户与对象的生产过程
    2. 负责控制产品对象的生产过程

建造者模式原理类图

使用建造者模式改进需求

类图

  1. 抽象建造者组合了产品
  2. 具体建造者实现了抽象建造者的抽象方法
  3. 建造指挥者聚合了抽象建造者, 提供设置方法
  4. 客户端(调用方)依赖建造指挥者

实现

代码语言:javascript
复制
package com.dance.design.designmodel.buildmodel.build;

import lombok.Data;
import lombok.Setter;

public class Client2 {
    public static void main(String[] args) {
        // 建造普通的房子
        // 创建指挥者 传入建造的具体实现
        HouseDirector houseDirector = new HouseDirector(new CommonHouse());
        // 建造
        House house = houseDirector.constructHouse();

        // 设置建造高楼大厦
        houseDirector.setHouseBuilder(new HighHouse());
        // 建造
        house = houseDirector.constructHouse();
    }
}

/**
 * 产品
 */
@Data
class House{
    private String basic;
    private String wall;
    private String roofed;
}

/**
 * 抽象建造者
 */
abstract class HouseBuilder{

    // 组合产品
    protected House house = new House();

    public abstract void buildBasic();

    public abstract void buildWalls();

    public abstract void buildRoofed();

    /**
     * 建造
     * @return 房子
     */
    public House buildHouse(){
        return house;
    }

}

/**
 * 普通房子
 */
class CommonHouse extends HouseBuilder{
    @Override
    public void buildBasic() {
        System.out.println("打地基5m");
    }

    @Override
    public void buildWalls() {
        System.out.println("砌墙10m");
    }

    @Override
    public void buildRoofed() {
        System.out.println("封顶10m2");
    }
}
/**
 * 高楼大厦
 */
class HighHouse extends HouseBuilder{
    @Override
    public void buildBasic() {
        System.out.println("打地基20m");
    }

    @Override
    public void buildWalls() {
        System.out.println("砌墙200m");
    }

    @Override
    public void buildRoofed() {
        System.out.println("封顶200m2");
    }
}

/**
 * 指挥者
 */
class HouseDirector{

    @Setter
    private HouseBuilder houseBuilder = null;

    /**
     * 传入具体的 要建造的房子
     * @param houseBuilder
     */
    public HouseDirector(HouseBuilder houseBuilder) {
        this.houseBuilder = houseBuilder;
    }

    // 建造流程交给指挥者
    public House constructHouse(){
        houseBuilder.buildBasic();
        houseBuilder.buildWalls();
        houseBuilder.buildRoofed();
        return houseBuilder.buildHouse();
    }

}

源码剖析

JDK中的建造者模式

StringBuilder 从名字就能看出来,这时一个建造者

  1. CharSequence : 字符序列 属于产品 因为要创建的为字符串
  2. Appendable : 功能接口, 支持追加功能append接口
  3. AbstractStringBuilder : 抽象建造者
    1. 实现了CharSequence(接口)
    2. 实现了Appendable(添加功能接口)
  4. 具体建造者
    1. 具体实现有StringBuffer 和 StringBuilder
  5. StringBuilder : 具体建造者, 兼职指挥者 继承了 抽象建造者
  6. 使用的时候直接使用具体建造者

建造者模式的注意事项和细节

  1. 客户端(使用程序)不必知道产品内部组成的细节, 将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象
  2. 每一个具体建造者都相对独立, 而与其他的具体建造者无关,因此可以很方便的替换具体建造者或增加新的具体建造者, 用户使用不同的具体建造者即可得到不同的产品对象
  3. 可以更加精细的控制产品的创建过程, 将复杂产品的创建步骤分解在不同的方法中, 使得创建过程更加清晰,也更方便使用程序来控制创建过程
  4. 增加新的具体建造者无需修改原有类库的代码, 指挥者针对抽象建造者编程, 系统扩展方便, 符合开闭原则
  5. 建造者模式所创建的产品一般具有较多的共同点, 其组成部分相似,如果产品之间差异性很大,则不适合使用建造者模式,因为此使用范围受到一定的限制
  6. 如果产品内部的变化复杂, 可能会导致需要定义很多具体建造者类来实现这种变化, 导致系统变得很庞大, 因此这种请款下, 要考虑是否使用建造者模式
  7. 抽象工厂模式 VS 建造者模式
    1. 抽象工厂模式实现对产品家族的创建, 一个产品家族是这样的一系列产品, 具有不同分类维度的产品组合, 采用抽象工厂模式不关系建造过程,只关心什么产品由什么工厂产生即可, 而建造者模式则是要求按照指定的蓝图建造产品,他的主要目的是通过组装零配件而生产一个新产品
    2. 其实就是关注粒度不同, 工厂模式只关注由那个工厂生产, 建造者需要关心建造过程

扩展: 为什么StringBuilder不是线程安全的?

一般我们面试的时候会问?

面试官: StringBuilder和StringBuffer有什么区别?

我: StringBuilder线程不安全, StringBuffer是线程安全的

面试官: StringBuilder 哪里不安全? StringBuffer为啥安全?

我 : ........

没得聊了

接下来说一下

我们先分析Stringbuilder的append方法

从源码可以看出调用的是父类的append方法

他的父类就是抽象建造者 AbstractStringBuilder ,从代码可以看出在进行count(统计char数组的长度)累加的时候 是一个+=的操作符,这个操作就不是一个原子操作, 所以会导致多线程操作不安全

那为什么StringBuffer是线程安全的呢?

从源码可以看出 StringBuffer的append方法添加了synchronized修饰, 这个直接是对象锁, 直接将方法加了锁,导致了哪怕是多线程操作一个对象的append方法也要乖乖的等着另一个线程执行完成....

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-05-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 需求
  • 传统方式解决
    • 新建抽象类
      • 新建实现类
        • 新建客户端
        • 传统方式的问题分析
        • 建造者模式
          • 基本介绍
            • 建造者模式的四个角色
              • 建造者模式原理类图
              • 使用建造者模式改进需求
                • 类图
                  • 实现
                  • 源码剖析
                    • JDK中的建造者模式
                    • 建造者模式的注意事项和细节
                    • 扩展: 为什么StringBuilder不是线程安全的?
                    领券
                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档