设计模式(七):建造者模式(BuilderPattern)

star2017 1年前 ⋅ 140 阅读

软件系统中,通常会存在一些复杂的对象,由多个部件组成(拥有一系列成员属性,成员属性中有些是引用类型的对象),而部件的组装必须按照一定的顺序,否则不能组装成一个完成的复杂对象。

由于组合部件的过程很复杂,因此,这些部件的组合过程往往被 外部化 ,这里就可以引入 建造者模式,部件的组合过程由 建造者 负责,建造者 返回客户端一个建造完毕的完整对象,用户无须关系组装细节。

模式定义

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

建造者模式 是将一个复杂的对象分解成多个简单对象,然后按一定顺序构建而成。将变与不变相分离,即产品的组成部件是不变的,组建顺序是稳定的,但每一个部件是可以灵活选择的。用户只需指定复杂对象的类型和部件就可以完成构建,不需要知道内部的具体构建细节。

模式分析

模式结构

建造者模式包含如下角色(结构):

  • Builder:抽象建造者,定义了创建各个部件的抽象接口,通常还包含一个返回复杂产品的方法 getResult()。

  • ConcreteBuilder:具体建造者,实现 Builder 接口,由它来实现构建细节(各个部件的具体创建方法),构造和装配各个部件。

  • Director:指挥者,根据用户需求构建产品对象。调用具体建造者对象中的部件构造方法与装配方法完成复杂对象的创建。

    指挥者负责控制产品的创建过程,将客户端与产品生产过程隔离,客户端只需要知道具体建造者的类型,即可通过指挥者类调用建造者的相关方法,返回一个完整的复杂产品对象。

  • Product:具体产品,包含多个部件的复杂对象,由具体创建造来创建期各个部件。

模式优缺点

  1. 优点
    • 用户不需知道构建产品的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。
    • 各个具体的建造者相互独立,可以很方便地替换或增加新的具体建造者,有利于系统的扩展,符合 开闭原则
  2. 缺点
    • 产品的组成部件必须似,若产品之间存在较大的差异,则不适用创建者模式,这限制了其使用范围。
    • 如果产品的内部变化复杂,可能导致需要定义从个具体的建造者来实现,导致系统变得复杂。

模式应用

建造者模式 创建的是复杂的对象,其产品的各个部件可能发生变化,但构建在一起却相对稳定的。适用场景如给应用换皮肤功能。皮肤是个大的复杂对象,由多个部位组成,可使用建造者模式实现。

典型应用

  1. JDK 中的 StringBuilder 是建造者模式的典型应用。

    StringBuilder 是一个具体建造者类,继承了一个抽象的建造者类AbstractStringBuilder,并且使用 final 修饰不允许被继承,在此类中可以看到有很多和构建字符串的有关方法,其中以 AbstractStringBuilder 为返回类型的方法,返回的都是 this,就可以持续用于调用构建字符串的方法,可以方便地做链式调用。

    StringBuilder 维护了一个缓冲区,客户端可以传递不同类型的参数,就可构建各种字符串。

    abstract class AbstractStringBuilder implements Appendable, CharSequence {
        public AbstractStringBuilder append(String str) {
            //.........
            return this;
        }
    
        public AbstractStringBuilder append(StringBuffer sb) {
            //.........
            return this;
        }
    
        AbstractStringBuilder append(AbstractStringBuilder asb) {
            //.........
            return this;
        }
    
        @Override
        public AbstractStringBuilder append(CharSequence s) {
            //.........
            return this.append(s, 0, s.length());
        }
    
        private AbstractStringBuilder appendNull() {
            //.........
            return this;
        }
    
        public AbstractStringBuilder append(long l) {
            //.........
            return this;
        }
    }
    

    建造者模式 通常会做成链式调用,让建造过程的方法返回 this ,就可以链式调用建造过程,最后调一个build() 方法返回产品对象。

  2. 在 API 文档构建工具 Swagger 的 Java 配置类中,创建 DocketApiInfo 对象实例,使用的就是构建者模式,链式调用方法,根据用户需要传入参数或引用对象,最后调用 build() 方法返回产品。

    @Configuration
    @EnableSwagger2
    public class SwaggerConfig {
        //swagger2的配置文件,这里可以配置swagger2的一些基本的内容,比如扫描的包等等
        @Bean
        public Docket createRestApi() {
            return new Docket(DocumentationType.SWAGGER_2)
                    .apiInfo(apiInfo())
                    .select()
                    //为当前包路径
                    .apis(RequestHandlerSelectors.basePackage("com.xxx.controller"))
                    .paths(PathSelectors.any())
                    .build();
        }
        //构建 api文档的详细信息函数,注意这里的注解引用的是哪个
        private ApiInfo apiInfo() {
            return new ApiInfoBuilder()
                    //页面标题
                    .title("xxx project title")
                    //创建人
                    .contact(new Contact("your name", "url", "email"))
                    //版本号
                    .version("1.0")
                    //描述
                    .description("xxx project description")
                    .build();
        }
    }
    

代码示例

  1. 复杂产品:麦当劳全家桶

    /**
     * 复杂产品
     */
    public class FamilyBarrel {
    
        //鸡腿
        private Drumstick drumstick;
        //可乐
        private CocaCola cocaCola;
        //汉堡
        private Hamburger hamburger;
        //薯条
        private FrenchFries frenchFries;
        //派
        private Pie pie;
    
        public SelectorBuilder select() {
            return new SelectorBuilder(this);
        }
    
        public Pie getPie() {
            return pie;
        }
    
        public FamilyBarrel setPie(Pie pie) {
            this.pie = pie;
            return this;
        }
    
        public Drumstick getDrumstick() {
            return drumstick;
        }
    
        public FamilyBarrel setDrumstick(Drumstick drumstick) {
            this.drumstick = drumstick;
            return this;
        }
    
        public CocaCola getCocaCola() {
            return cocaCola;
        }
    
        public FamilyBarrel setCocaCola(CocaCola cocaCola) {
            this.cocaCola = cocaCola;
            return this;
        }
    
        public Hamburger getHamburger() {
            return hamburger;
        }
    
        public FamilyBarrel setHamburger(Hamburger hamburger) {
            this.hamburger = hamburger;
            return this;
        }
    
        public FrenchFries getFrenchFries() {
            return frenchFries;
        }
    
        public FamilyBarrel setFrenchFries(FrenchFries frenchFries) {
            this.frenchFries = frenchFries;
            return this;
        }
    
        public void show() {
            String barrel = "麦当劳全家桶:[" + drumstick.getFlavor() + ", " + cocaCola.getVolume() + ", " +
                    hamburger.getFlavor() + ", " + frenchFries.getCapacity() + ", " + pie.getFlavor() + "]";
            System.out.println(barrel);
        }
    }
    
  2. 简单产品

    /**
     * 产品部件:可乐
     */
    public class CocaCola {
    
        //容量:大杯,中杯,小杯
        private String volume;
    
        public CocaCola(String volume) {
            this.volume = volume;
        }
    
        public String getVolume() {
            return volume;
        }
    
        public CocaCola setVolume(String volume) {
            this.volume = volume;
            return this;
        }
    }
    
    /**
     * 产品部件:鸡腿
     */
    public class Drumstick {
    
        //口味:盐焗,麦辣,
        private String flavor;
    
        public Drumstick(String flavor) {
            this.flavor = flavor;
        }
    
        public String getFlavor() {
            return flavor;
        }
    
        public Drumstick setFlavor(String flavor) {
            this.flavor = flavor;
            return this;
        }
    }
    
    /**
     * 产品部件:署条
     */
    public class FrenchFries {
    
        //容量
        private String capacity;
    
        public FrenchFries(String capacity) {
            this.capacity = capacity;
        }
    
        public String getCapacity() {
            return capacity;
        }
    
        public FrenchFries setCapacity(String capacity) {
            this.capacity = capacity;
            return this;
        }
    }
    
    /**
     * 产品部件:汉堡
     */
    public class Hamburger {
    
        //类型:鸡肉,牛肉,鳕鱼
        private String flavor;
    
        public Hamburger(String flavor) {
            this.flavor = flavor;
        }
    
        public String getFlavor() {
            return flavor;
        }
    
        public Hamburger setFlavor(String flavor) {
            this.flavor = flavor;
            return this;
        }
    }
    
    /**
     * 产品部件:派
     */
    public class Pie {
    
        //口味:菠萝,香芋,蜜桃
        private String flavor;
    
        public Pie(String flavor) {
            this.flavor = flavor;
        }
    
        public String getFlavor() {
            return flavor;
        }
    
        public Pie setFlavor(String flavor) {
            this.flavor = flavor;
            return this;
        }
    }
    
  3. 定义建造者抽象接口

    /**
     * 抽象建造者
     */
    public interface Builder {
    
        //可乐
        void buildCocaCola();
    
        //鸡腿
        void buildDrumstick();
    
        //薯条
        void buildFrenchFires();
    
        //汉堡
        void buildHamburger();
    
        //派
        void buildPie();
    
        //返回结果
        FamilyBarrel getResult();
    }
    
  4. 具体建造者

    /**
     * 具体建造者A
     */
    public class ConcreteBuilderA implements Builder {
    
        FamilyBarrel familyBarrel = new FamilyBarrel();
    
        @Override
        public void buildCocaCola() {
            familyBarrel.setCocaCola(new CocaCola("中杯可乐"));
        }
    
        @Override
        public void buildDrumstick() {
            familyBarrel.setDrumstick(new Drumstick("麦辣鸡腿"));
        }
    
        @Override
        public void buildFrenchFires() {
            familyBarrel.setFrenchFries(new FrenchFries("小份薯条"));
        }
    
        @Override
        public void buildHamburger() {
            familyBarrel.setHamburger(new Hamburger("牛肉汉堡"));
        }
    
        @Override
        public void buildPie() {
            familyBarrel.setPie(new Pie("菠萝味"));
        }
    
        @Override
        public FamilyBarrel getResult() {
            return familyBarrel;
        }
    }
    
    /**
     * 具体建造者B
     */
    public class ConcreteBuilderB implements Builder {
    
        FamilyBarrel familyBarrel = new FamilyBarrel();
    
        @Override
        public void buildCocaCola() {
            familyBarrel.setCocaCola(new CocaCola("大杯可乐"));
        }
    
        @Override
        public void buildDrumstick() {
            familyBarrel.setDrumstick(new Drumstick("盐焗鸡腿"));
        }
    
        @Override
        public void buildFrenchFires() {
            familyBarrel.setFrenchFries(new FrenchFries("大份薯条"));
        }
    
        @Override
        public void buildPie() {
            familyBarrel.setPie(new Pie("香芋派"));
        }
    
        @Override
        public void buildHamburger() {
            familyBarrel.setHamburger(new Hamburger("鳕鱼汉堡"));
        }
    
        @Override
        public FamilyBarrel getResult() {
            return familyBarrel;
        }
    }
    
  5. 指挥者

    public class Director {
        public void construct(Builder builder) {
            builder.buildCocaCola();
            builder.buildDrumstick();
            builder.buildFrenchFires();
            builder.buildHamburger();
            builder.buildPie();
        }
    }
    
  6. 创建全家桶

    public class MainTest {
        public static void main(String[] args) {
            Director director = new Director();
    
            Builder builderA = new ConcreteBuilderA();
            Builder builderB = new ConcreteBuilderB();
    
            director.construct(builderA);
            FamilyBarrel barrelA = builderA.getResult();
            barrelA.show();
    
            director.construct(builderB);
            FamilyBarrel barrelB = builderB.getResult();
            barrelB.show();
        }
    }
    
  7. 输出结果

    麦当劳全家桶:[麦辣鸡腿, 中杯可乐, 牛肉汉堡, 小份薯条, 菠萝味]
    麦当劳全家桶:[盐焗鸡腿, 大杯可乐, 鳕鱼汉堡, 大份薯条, 香芋派]
    

相关参考

  1. 建造者模式
  2. 建造者模式(Bulider模式)详解
更多内容请访问:IT源点

相关文章推荐
  • 该目录下还没有内容!

全部评论: 0

    我有话说: