java代码审计-设计模式-建造者模式

Java设计模式-建造者模式(Builder Pattern)

目录

  • 什么是建造者模式
  • 建造者模式的实现
  • JavaSE中建造者模式的使用
  • Struts2建造者模式的应用

工厂模式关注的是构建结果,一个工厂生产一类对象;而建造者模式关注的是构建过程,调用不同的方法生产不同的对象。

一、什么是建造者模式

建造者模式(Builder Pattern):将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。建造者模式是一种对象创建型模式。

建造者模式一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的具体构建细节。

UML表示图如下

建造者模式类图

建造者模式主要包含4个角色:

  1. 抽象建造者(Builder):要构建的目标对象所需要的“零部件”,大多是接口
  2. 具体建造者(ConcreteBuilder):Builder的实现类,提供创建方法以及产品实例
  3. 产品类(Product):要构建的对象,内部包含很多其他的“零部件对象”
  4. 指挥者类(Director):负责调用Builder的方法构建对象,返回实例,对调用者屏蔽了具体的构建过程,构建者一般只与指挥者类打交道

二、建造者模式的实现

还是举一个造车的例子,一辆汽车需要窗户、座椅、轮胎等,而每个零部件都会有单独的厂商,汽车生产厂家不过是使用零部件组装为一辆汽车。

该例子包含的4个角色:

  1. 抽象建造者(Builder):ICarBuilder
  2. 具体建造者(ConcreteBuilder):CarBuilder
  3. 产品类(Product):Car
  4. 指挥者类(Director):CarDirector

代码如下

package org.builderPattern.version1;

/**
 * 产品类
 */
public class Car {
    private String window;
    private String tyre;
    private String seat;
    private boolean ai;

    public boolean getAi() {
        return ai;
    }

    public void setAi(boolean ai) {
        this.ai = ai;
    }

    public String getWindow() {
        return window;
    }

    public void setWindow(String window) {
        this.window = window;
    }

    public String getTyre() {
        return tyre;
    }

    public void setTyre(String tyre) {
        this.tyre = tyre;
    }

    public String getSeat() {
        return seat;
    }

    public void setSeat(String seat) {
        this.seat = seat;
    }

    @Override
    public String toString() {
        String rs = "这款汽车有:" + this.getSeat() + "、" + this.getTyre()  + "、" + this.getWindow();
        if(this.getAi()){
            rs += "、自动驾驶";
        }
        return rs;
    }
}

package org.builderPattern.version1;

/**
 * 抽象建造者
 */
public interface ICarBuilder {
    public ICarBuilder setWindows(String window);
    public ICarBuilder setSeat(String seat);
    public ICarBuilder setTyre(String tyre);
    public ICarBuilder setAI(boolean ai);

    public Car createCar();
}

package org.builderPattern.version1;

/**
 * 具体建造者
 */
public class CarBuilder implements ICarBuilder{
    private Car car = new Car();
    @Override
    public ICarBuilder setWindows(String window) {
        this.car.setWindow(window);
        System.out.println("给汽车安装[" + window + "]");
        return this;
    }

    @Override
    public ICarBuilder setSeat(String seat) {
        this.car.setSeat(seat);
        System.out.println("给汽车安装[" + seat + "]");
        return this;
    }

    @Override
    public ICarBuilder setTyre(String tyre) {
        this.car.setTyre(tyre);
        System.out.println("给汽车安装[" + tyre + "]");
        return this;
    }

    @Override
    public ICarBuilder setAI(boolean ai) {
        this.car.setAi(ai);
        if (ai){
            System.out.println("给汽车安装[自动驾驶]");
        }
        return this;
    }

    @Override
    public Car createCar() {
        return this.car;
    }

}

package org.builderPattern.version1;

/**
 * 指挥者
 */
public class CarDirector {
    public Car createCar(ICarBuilder carBuilder){
        carBuilder.setSeat("加热座椅")
                .setTyre("耐磨轮胎")
                .setWindows("普通窗户");
        Car car = carBuilder.createCar();
        return car;
    }

    public Car createAICar(ICarBuilder carBuilder){
        carBuilder.setSeat("加热座椅")
                .setTyre("耐磨轮胎")
                .setWindows("普通窗户")
                .setAI(true);
        Car car = carBuilder.createCar();
        return car;
    }

}

package org.builderPattern.version1;

public class Client {
    public static void main(String[] args) {
        CarDirector director = new CarDirector();
        Car car = director.createCar(new CarBuilder());
        System.out.println(car.toString());

        System.out.println("---------------------------------");
        Car aiCar = director.createAICar(new CarBuilder());
        System.out.println(aiCar);
    }
}

// 运行结果
给汽车安装[加热座椅]
给汽车安装[耐磨轮胎]
给汽车安装[普通窗户]
这款汽车有:加热座椅、耐磨轮胎、普通窗户
---------------------------------
给汽车安装[加热座椅]
给汽车安装[耐磨轮胎]
给汽车安装[普通窗户]
给汽车安装[自动驾驶]
这款汽车有:加热座椅、耐磨轮胎、普通窗户、自动驾驶

使用IDEA的Diagrams功能生成的类图(在package上右键,选择Diagrams即可)

image-20230301224049467

案例中Client使用CarDirector指挥CarBuilder创造Car对象,Client并没有直接接触到CarBuilder类,这符合**迪米特法则:只和朋友交流,不和陌生人说话,实现解耦**。

在创建的两个Car中,一个有自动驾驶一个没有,CarBuilder通过链式调用精准的控制Car类创建的过程,将复杂的创建步骤,分解到不同的方法中,使创建过程更加清晰。总结下建造者模式的优缺点:

优点:

  1. 解耦,调用者无需知道构建细节,将产品本身与创建过程解耦,相同的创建过程可以创建不同的产品对象
  2. 灵活,指挥者只调用建造者建造产品,新增新的建造者无需修改原有类库代码,扩展方便,符合开闭原则,一般产品类和建造者是比较稳定的。
  3. 精细,复杂的创建过程拆解为不同方法,灵活调用,实现组装

缺点:

  1. 建造者模式所创建的产品一般具有较多的额共同点,如果产品间差别很大时不适用,可以考虑工厂模式
  2. 如果产品过于复杂,会导致定义过多的建造者类来实现这类变化,导致系统庞大

适用场景:

  1. 需要生成的产品对象有复杂的内部结构,这些产品对象通常包含多个成员属性。
  2. 需要生成的产品对象的属性相互依赖,需要指定其生成顺序。
  3. 对象的创建过程独立于创建该对象的类。在建造者模式中通过引入了指挥者类,将创建过程封装在指挥者类中,而不在建造者类和客户类中。
  4. 隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品。

三、JavaSE中建造者模式的使用

3.1、java.lang.StringBuilder

image-20230302094240775

分析下StringBuilder包含的4个角色:

  1. 抽象建造者(Builder):AbstractStringBuilder、Appendable
  2. 具体建造者(ConcreteBuilder):StringBuilder
  3. 产品类(Product):char数组,AbstractStringBuilder类的char[] value;
  4. 指挥者类(Director):无

Appendable接口定义了字符串组装的方法,由AbstractStringBuilder、StringBuilder进行实现具体的字符串组装方法,组装的产品就是字符串,其实本质上是char数组。

3.2、java.lang.StringBuffer

在上学的时候老师总是教给我们,能用StringBuffer就用StringBuffer,但为什么?老师没讲,接下来我们分析下

image-20230302102501988

可以发现StringBuffer同样是AbstractStringBuilder、Appendable的子类,看一下它的append方法

@Override
public synchronized StringBuffer append(String str) {
    toStringCache = null;
    super.append(str);
    return this;
}

发现了synchronized关键字,这是线程安全的,所以老师说能用StringBuffer就用StringBuffer

四、Struts2建造者模式的应用

在Struts2的ContainerBuiler类使用了建造者模式,类图如下

image-20230302103816511

分析下ContainerBuiler包含的4个角色:

  1. 抽象建造者(Builder):无
  2. 具体建造者(ConcreteBuilder):ContainerBuilder
  3. 产品类(Product):ContainerImpl(Container的子类)
  4. 指挥者类(Director):无

这个类比较特殊,需要先了解Struts2的容器加载机制,产品类ContainerImpl需要factories,该factories在ContainerBuiler进行创建。

class ContainerImpl implements Container {
    final Map<Key<?>, InternalFactory<?>> factories;
}

在ContainerBuiler中通过多个factory重载方法完成对factories列表的构造,factories中存放的实际是对象的创建工厂InternalFactory。

当ContainerBuiler调用create方法时,方法内部将factories封装到Container对象,从而返回一个Container对象。ContainerBuiler由此完成了对Container对象的构建。

public Container create(boolean loadSingletons) {
    ensureNotCreated();
    created = true;
    // 将factories封装到ContainerImpl中
    final ContainerImpl container = new ContainerImpl(
        new HashMap<Key<?>, InternalFactory<?>>(factories));
    if (loadSingletons) {
      container.callInContext(new ContainerImpl.ContextualCallable<Void>() {
        public Void call(InternalContext context) {
          for (InternalFactory<?> factory : singletonFactories) {
            factory.create(context);
          }
          return null;
        }
      });
    }
    container.injectStatics(staticInjections);
    return container;
}

关于Struts2的容器加载机制,可以浏览《Mark链接-Struts2依赖注入实现原理》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MarginSelf

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值