设计模式-Builder建造者

什么是构建器

  • 分离复杂对象的构建和表示
  • 同样的构建过程可以创建不同的表示

建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

一个 Builder 类会一步一步构造最终的对象。该 Builder 类是独立于其他对象的

​ 创建者模式又叫建造者模式,是将一个复杂的对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。创建者模式隐藏了复杂对象的创建过程,它把复杂对象的创建过程加以抽象,通过子类继承或者重载的方式,动态的创建具有复合属性的对象。

在这里插入图片描述

角色

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

优缺点

优点

  • 客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。
  • 每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者,
    用户使用不同的具体建造者即可得到不同的产品对象 。
  • 可以更加精细地控制产品的创建过程 。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程。
  • 增加新的具体建造者无须修改原有类库的代码,指挥者类针对抽象建造者类编程,系统扩展方便,符合 “开闭原则”

缺点

  • 产品之间差异性很大的情况:建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。
  • 产品内部变化很复杂的情况: 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。

适用场景

  • 隔离复杂对象的创建和使用,相同的方法,不同执行顺序,产生不同事件结果
  • 多个部件都可以装配到一个对象中,但产生的运行结果不相同
  • 产品类非常复杂或者产品类因为调用顺序不同而产生不同作用
  • 初始化一个对象时,参数过多,或者很多参数具有默认值
  • Builder模式不适合创建差异性很大的产品类
  • 产品内部变化复杂,会导致需要定义很多具体建造者类实现变化,增加项目中类的数量,增加系统的理解难度和运行成本
    需要生成的产品对象有复杂的内部结构,这些产品对象具备共性;

注意事项

和适配器模式的区别:适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。
和装饰器模式的区别:装饰器模式为了增强功能,而代理模式是为了加以控制。

实现

产品类

需要进行构建的原始的产品类


/**
 * 需要进行构建的产品类
 */
public class Product {
    /**
     * 产品名称
     */
    private String name;
    /**
     * 单价
     */
    private float price;
    /**
     * 数量
     */
    private int num;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public float getPrice() {
        return price;
    }

    public void setPrice(float price) {
        this.price = price;
    }

    public int getNum() {
        return num;
    }

    public void setNum(int num) {
        this.num = num;
    }

    @Override
    public String toString() {
        return "Product{" +
                "name='" + name + '\'' +
                ", price=" + price +
                ", num=" + num +
                '}';
    }
}

Builder 接口

Builder 可以是接口也可以是抽象类,定义需要实现的构建模板

/**
 * 建造者接口
 */
public interface Builder {
    //构建产品名称
    public void buildName();

    //构建单价
    public void buildPrice();

    //构建数量
    public void buildNum();

    //获取构建的对象
    public Product build();
}

具体建造者(ConcreteBuilder)

构建苹果手机

/**
 * 构建苹果手机
 */
public class MobileBuilder implements Builder {
    private Product product = new Product();

    @Override
    public void buildName() {
        product.setName("iphone手机");
    }

    @Override
    public void buildPrice() {
        product.setPrice(8000);
    }

    @Override
    public void buildNum() {
        product.setNum(1000);
    }

    @Override
    public Product build() {
        return product;
    }
}

构建手表

/**
 * 构建手表
 */
public class WatchBuilder implements Builder {
    private Product product = new Product();

    @Override
    public void buildName() {
        product.setName("手表");
    }

    @Override
    public void buildPrice() {
        product.setPrice(10000);
    }

    @Override
    public void buildNum() {
        product.setNum(10);
    }

    @Override
    public Product build() {
        return product;
    }
}

指挥者(Director)

/**
 * 指挥者
 */
public class Director {
    //建造者接口
    private Builder builder = null;
    //构造方法
    public Director(Builder builder){
        this.builder = builder;
    }

    /**
     * 构建具体的对象
     * @return 构建出来的对象
     */
    public Product getProduct(){
        builder.buildName();
        builder.buildPrice();
        builder.buildNum();
        return builder.build();
    }
}

客户端使用

/**
 * 建造者测试
 */
public class BuildTest {

    public static void main(String[] args) {
        //打印手机
        printBuild(new MobileBuilder());
        //打印手表
        printBuild(new WatchBuilder());
    }

    /**
     * 打印建造后的对象数据
     * @param builder
     */
    public static void printBuild(Builder builder){
        Director director = new Director(builder);
        Product product = director.getProduct();
        System.out.println(product.toString());
    }
}

输出

Product{name='iphone手机', price=8000.0, num=1000}
Product{name='手表', price=10000.0, num=10}

通过链式调用优化(非常推荐)

链式写法是在原型写法的基础上做优化,有些时候Builder的创建部分有默认值,或者不需要的情况下,而产生不同的Product,通过以上方式,就需要修改Director类和Builder类,再或者根据不同的创建顺序,生成不同的结果,也需要修改Director类。Director似乎显得很不稳定和多余。可以通过Builder自身的调用逻辑来生成Product,即链式调用

/**
 * 链式的建造者
 */
public class StreamProductBuilder {
    /**
     * 产品名称
     */
    private String name;
    /**
     * 单价
     */
    private float price;
    /**
     * 数量
     */
    private int num;

    public StreamProductBuilder() {
    }

    public StreamProductBuilder(String name, float price, int num) {
        this.name = name;
        this.price = price;
        this.num = num;
    }

    /**
     * 静态类 Builder
     */
    public static final  class Builder {
        /**
         * 产品名称
         */
        private String name;
        /**
         * 单价
         */
        private float price;
        /**
         * 数量
         */
        private int num;

        //空的构造方法
        public Builder(){}

        /**
         * 进行建造
         * @param name
         * @return
         */
        public Builder name(String name) {
            this.name = name;
            return this;
        }

        public Builder price(float prie) {
            this.price = price;
            return this;
        }

        public Builder num(int num) {
            this.num = num;
            return this;
        }

        /**
         * 返回建造的对象
         * @return
         */
        public StreamProductBuilder build() {
            return new StreamProductBuilder(name, price, num);
        }
    }


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public float getPrice() {
        return price;
    }

    public void setPrice(float price) {
        this.price = price;
    }

    public int getNum() {
        return num;
    }

    public void setNum(int num) {
        this.num = num;
    }
}

使用

public class BuildTest {

    public static void main(String[] args) {
        StreamProductBuilder builder = new StreamProductBuilder.Builder().name("iphone手机").price(8000).num(100).build();
        System.out.println(builder.getName());
    }
}

上面的示例代码只是传入三个参数,如果参数是十四个甚至更多,builder 模式的优势将会更加明显,传递参数更加灵活,代码具有更高的可读性,代码更加简洁。

相比于普通JavaBean的好处
​ 在建造者模式中,提供一个辅助的静态建造器Builder(静态内部类),可以在里面set实体类的属性,与JavaBean不同的是,建造者是先set,在通过build实例化实体类,这样既可以提高代码的阅读性,也可以防止对象没有实例化,就被调用;不会造成不一致性,同时解决了Javabean模式的线程安全问题

地形构建器

public interface TerrainBuilder {
    TerrainBuilder buildWall();
    TerrainBuilder buildFort();
    TerrainBuilder buildMine();
    Terrain build();
}
public class Terrain {
    Wall w;
    Fort f;
    Mine m;
}

class Wall {
    int x, y, w, h;

    public Wall(int x, int y, int w, int h) {
        this.x = x;
        this.y = y;
        this.w = w;
        this.h = h;
    }
}

class Fort {
    int x, y, w, h;

    public Fort(int x, int y, int w, int h) {
        this.x = x;
        this.y = y;
        this.w = w;
        this.h = h;
    }

}

class Mine {
    int x, y, w, h;

    public Mine(int x, int y, int w, int h) {
        this.x = x;
        this.y = y;
        this.w = w;
        this.h = h;
    }
}

实现类

public class ComplexTerrainBuilder implements TerrainBuilder {
    Terrain terrain = new Terrain();

    @Override
    public TerrainBuilder buildWall() {
        terrain.w = new Wall(10, 10, 50, 50);
        return this;
    }

    @Override
    public TerrainBuilder buildFort() {
        terrain.f = new Fort(10, 10, 50, 50);
        return this;
    }

    @Override
    public TerrainBuilder buildMine() {
        terrain.m = new Mine(10, 10, 50, 50);
        return this;
    }

    @Override
    public Terrain build() {
        return terrain;
    }
}

public class Main {
    public static void main(String[] args) {
        TerrainBuilder builder = new ComplexTerrainBuilder();
        Terrain t = builder.buildFort().buildMine().buildWall().build();
        //new Terrain(Wall w, Fort f, Mine m)
        //Effective Java
    }
}

建造者模式与抽象工厂模式的比较

与抽象工厂模式相比,建造者模式返回一个组装好的完整产品,而抽象工厂模式返回一系列相关的产品,这些产品位于不同的产品等级结构,构成了一个产品族 。
在抽象工厂模式中,客户端实例化工厂类,然后调用工厂方法获取所需产品对象,而在建造者模式中,客户端可以不直接调用建造者的相关方法,而是通过指挥者类来指导如何生成对象,包括对象的组装过程和建造步骤,它侧重于一步步构造一个复杂对象,返回一个完整的对象 。
如果将抽象工厂模式看成汽车配件生产工厂,生产一个产品族的产品,那么建造者模式就是一个汽车组装工厂,通过对部件的组装可以返回一辆完整的汽车

总结

​ Director角色并非多余,能把复杂的Product创建过程对外隐藏,使Builder部件和创建过程分离,各方易于扩展,降低了耦合度。当需要对一个对象设置很多属性,此时就能方便的使用链式调用来提高编码速度和代码可读性。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值