设计模式——建造者模式

builder

阅读原文请访问我的博客BrightLoong's Blog

一. 概述

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

建造者模式可以将一个产品的内部表象与产品的生成过程分割开来,使用建造者模式,用户就只需指定需要建造的类型就可以得到它们,二具体建造的过程和细节就不需要知道了。

建造者模式相比于工厂模式更加关注组成部分的装配细节和顺序。

创建者模式属于创建型模式。

二. UML类图解析

建造者模式的UML类图如下:

builder
  • Builder:为创建一个Product对象的各个部件指定的抽象接口,buildPart()是对象组成部分构造的抽象方法,根据需求可以有多个组成部分的构造方法。
  • ConcreteBuilder:具体建造者,继承自Builder,构造和装配各个部件,getResult()返回构造后的实例。
  • Director:指挥者,持有一个Builder引用,调用具体的创建者的各个部件来创建对象,负责保证对象各部分完整创建或者按某种顺序创建 。
  • Product:产品,要创建的具体对象,一般来说是一个复杂的对象,包含多个组成部分。

三. 代码实现

这里把电脑作为一个产品,一般来说一个可以使用的台式电脑包括显示器、主机、键盘和鼠标,各个组成部分可以有不同的品牌、性能、型号等细节,下面就使用建造者模式来对电脑进行实现。

产品类——Computer

package io.github.brightloong.design.builder;

/**
 * 产品类Computer,为了方便实现,所有组成均用String来表示。
 * Created by BrightLoong on 2018/5/24.
 */
public class Computer {
    /**
     * 显示器
     */
    public String displayer;

    /**
     * 主机
     */
    public String host;

    /**
     * 键盘
     */
    public String keyboard;

    /**
     * 鼠标
     */
    public String mouse;

    public String getDisplayer() {
        return displayer;
    }

    public void setDisplayer(String displayer) {
        this.displayer = displayer;
    }

    public String getHost() {
        return host;
    }

    public void setHost(String host) {
        this.host = host;
    }

    public String getKeyboard() {
        return keyboard;
    }

    public void setKeyboard(String keyboard) {
        this.keyboard = keyboard;
    }

    public String getMouse() {
        return mouse;
    }

    public void setMouse(String mouse) {
        this.mouse = mouse;
    }

    /**
     * 覆盖toString方法.
     * @return
     */
    @Override
    public String toString() {
        return "Computer{" +
                "displayer='" + displayer + '\'' +
                ", host='" + host + '\'' +
                ", keyboard='" + keyboard + '\'' +
                ", mouse='" + mouse + '\'' +
                '}';
    }
}

构建抽象——ComputerBuilder

package io.github.brightloong.design.builder;

/**
 * Created by BrightLoong on 2018/5/24.
 */
public abstract class ComputerBuilder {
    abstract void buildDisplay();
    abstract void buildHost();
    abstract void buildKeyBoard();
    abstract void buildMouse();
    abstract Computer getComputer();
}

具体构建类——SuperComputerBuilder

package io.github.brightloong.design.builder;

/**
 * Created by BrightLoong on 2018/5/24.
 */
public class SuperComputerBuilder extends ComputerBuilder {
    Computer computer;

    public SuperComputerBuilder() {
        computer = new Computer();
    }

    void buildDisplay() {
        computer.setDisplayer("37.5英寸显示器IPS曲面屏微边框防眩光4K屏幕");
    }

    void buildHost() {
        computer.setHost("i7-6950X/TiTANX 高端硬管水冷电脑六核游戏主机");
    }

    void buildKeyBoard() {
        computer.setKeyboard("猛禽竞技机械键盘");
    }

    void buildMouse() {
        computer.setMouse("逆天悬浮鼠标");
    }

    Computer getComputer() {
        return computer;
    }
}

指挥者——ComputerDirector

package io.github.brightloong.design.builder;

/**
 * Created by BrightLoong on 2018/5/24.
 */
public class ComputerDirector {
    private ComputerBuilder computerBuilder;

    public ComputerDirector(ComputerBuilder computerBuilder) {
        this.computerBuilder = computerBuilder;
    }

    public Computer build() {
        computerBuilder.buildDisplay();
        computerBuilder.buildHost();
        computerBuilder.buildKeyBoard();
        computerBuilder.buildMouse();
        return computerBuilder.getComputer();
    }
}

客户端调用和输出

package io.github.brightloong.design.builder;

/**
 * Created by BrightLoong on 2018/5/24.
 */
public class Client {
    public static void main(String[] args) {
        Computer computer = new ComputerDirector(new SuperComputerBuilder()).build();
        System.out.println(computer);
    }
}

输出:

Computer{displayer='37.5英寸显示器IPS曲面屏微边框防眩光4K屏幕', host='i7-6950X/TiTANX 高端硬管水冷电脑六核游戏主机', keyboard='猛禽竞技机械键盘', mouse='逆天悬浮鼠标'}

四. 总结

使用场景

  • 用于创建一些复杂的对象,这些对象内部构建的顺序通常是稳定的,但对象内部的构建通常面临着复杂的变化。
  • 当创建复杂的对象的算法应该独立于该对象的组成部分已经它们的装配方法时。

优点

  • 建造者模式使得建造代码与表示代码分离,隐藏了产品实现细节。
  • 如果要改变一个产品的内部表示,只要再定义一个具体的建造者就可以了,方便扩展。
  • 建造者模式通过指挥者可以控制组件建造顺序,能实现一定程度的细节把控,特别是那种生成的产品对象之间存在属性依赖的情况。

缺点

  • 如果产品多变,会生成大量的建造类,造成类膨胀。

五. 扩展——构建器

在《Effective Java》一书中,第2条提到:

遇到多个构造器参数时要考虑用构建器。

当有多个参数的时候,客户端代码不仅难编写,也很难阅读,再有,客户端可能不清楚每个参数到底代表什么。

这里也使用到了Builder模式,不直接生成想要的对象,二是让客户端利用所有必要的参数调用Builder构造器,得到一个builder对象。然后客户端在builder对象上调用类似于setter的方法,来设置每个相关的可选参数。最后,客户端调用无参的build方法来生成不可变(相比较JavaBeans模式,也就是调用set方法来进行设值,这种方式可以生成不可变对象)的对象。下面的是具体的代码,来自《Effective Java》书上的列子。

package io.github.brightloong.design.builder;

/**
 * Created by BrightLoong on 2018/5/24.
 */
public class NutritionFacts {
    private final int servingSize;
    private final int servings;
    private final int calories;
    private final int fat;
    private final int sodium;
    private final int carbohydrate;

    public static class Builder {
        //Required parameters
        private final int servingSize;
        private final int servings;

        //Optional parameters - initialized to default values
        private int calories = 0;
        private int fat = 0;
        private int sodium = 0;
        private int carbohydrate = 0;

        public Builder(int servingSize, int servings) {
            this.servingSize = servingSize;
            this.servings = servings;
        }

        public Builder calories(int val) {
            calories = val;
            return this;
        }

        public Builder fat(int val) {
            fat = val;
            return this;
        }

        public Builder sodium(int val) {
            sodium = val;
            return this;
        }

        public Builder carbohydrate(int val) {
            carbohydrate = val;
            return this;
        }

        public NutritionFacts build() {
            return new NutritionFacts(this);
        }

    }

    private NutritionFacts(Builder builder) {
        servingSize = builder.servingSize;
        servings = builder.servings;
        calories = builder.calories;
        fat = builder.fat;
        sodium = builder.sodium;
        carbohydrate = builder.carbohydrate;
    }

    @Override
    public String toString() {
        return "NutritionFacts{" +
                "servingSize=" + servingSize +
                ", servings=" + servings +
                ", calories=" + calories +
                ", fat=" + fat +
                ", sodium=" + sodium +
                ", carbohydrate=" + carbohydrate +
                '}';
    }
}

调用和输出:

package io.github.brightloong.design.builder;

/**
 * Created by BrightLoong on 2018/5/24.
 */
public class Client2 {
    public static void main(String[] args) {
        NutritionFacts cocaCola = new NutritionFacts.Builder(240,8)
                .calories(100).sodium(35)
                .carbohydrate(27)
                .build();
        System.out.println(cocaCola);
    }
}

输出:

NutritionFacts{servingSize=240, servings=8, calories=100, fat=0, sodium=35, carbohydrate=27}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,711评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,932评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,770评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,799评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,697评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,069评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,535评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,200评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,353评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,290评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,331评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,020评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,610评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,694评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,927评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,330评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,904评论 2 341

推荐阅读更多精彩内容