Builder模式:御凤仙姑在北美开了家中式快餐店

家乡的口味,西式的标准

自从和四大炼丹术士在爪娲山一别之后,御凤独自一人来到了亚美利加。由于吃腻了左宗棠鸡,李鸿章杂碎,她觉得自己应该做点什么,自己的那十几口炼丹炉平时闲着也没别的用,不如做饭吧。说干就干,你有肯德基,我有玛萨基。御凤用炼丹挣来的闲钱,在家门口开了一家中式快餐店,名字就叫玛萨基。<br />
玛萨基用餐流程简明,效率高超。玛萨基的厨房有主食、汤、自选菜、水果、饮料五大区域,根据用户的选择,通过自动传送带,构建出成品套餐以供用户享用。主食有米饭、白粥、烂糊面可选,汤有冬瓜排骨汤、罗宋汤、滋补牛鞭汤等选择,其余不再赘述。玛萨基提供两种套餐:标准套餐和商务套餐,其中标准套餐由主食、汤、自选菜组成,不含水果饮料,商务套餐五种都包含。<br />

标准套餐 商务套餐
主食
自选菜
水果 ×
饮品 ×

御凤见招拆招,用了Builder模式来对付她的玛萨基套餐这个复杂的Product,第一版代码如下

Client.class

public class Client {
    public static void main(String[] args) {
        玛萨基套餐Builder builder = new 标准套餐Builder();
        Director director1 = new Director(builder);
        菜[] dishes1 = {new 海蜇皮(), new 白斩鸡()};
        菜[] dishes2 = {new 红烧羊肉(), new 白斩鸡()};
        director1.construct标准套餐(new 烂糊面(), dishes1, new 罗宋汤());
        System.out.println(builder.create().toString());


        玛萨基套餐Builder 商务套餐Builder = new 商务套餐Builder();
        Director director2 = new Director(商务套餐Builder);
        director2.construct商务套餐(new 米饭(), dishes2, new 牛鞭汤(), new 绍兴黄酒(), new 香蕉());
        商务套餐Builder businessBuilder = (商务套餐Builder) 商务套餐Builder;
        System.out.println(businessBuilder.create().toString());
    }
}

Director.class

public class Director {
    玛萨基套餐Builder builder = null;

    public Director(玛萨基套餐Builder builder) {
        this.builder = builder;
    }

    public void construct标准套餐(主食 stapleFood, 菜[] dishes, 汤 soup) {
        builder.build主食(stapleFood);
        builder.build自选菜(dishes);
        builder.build汤(soup);
    }

    public void construct商务套餐(主食 stapleFood, 菜[] dishes, 汤 soup, 饮料 beverage, 水果 fruit) {
        builder.build主食(stapleFood);
        builder.build自选菜(dishes);
        builder.build汤(soup);
        商务套餐Builder businessBuilder = (商务套餐Builder) builder;
        businessBuilder.build饮料(beverage);
        businessBuilder.build水果(fruit);
    }

}

玛萨基套餐.class

public abstract class 玛萨基套餐 {
    protected 主食 stapleFood;
    protected 菜[] dishes;
    protected 汤 soup;


    public 玛萨基套餐() {
    }

    public void setStapleFood(主食 stapleFood) {
        this.stapleFood = stapleFood;
    }

    public void setDishes(菜[] dishes) {
        this.dishes = dishes;
    }


    public void setSoup(汤 soup) {
        this.soup = soup;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder()
                .append("主食是:").append(stapleFood.getClass().getSimpleName())
                .append(", 菜有丰盛的");
        for (菜 dish : dishes) {
            sb.append(dish.getClass().getSimpleName() + ",");
        }
        sb.append(" 汤是上好的").append(soup.getClass().getSimpleName()).toString();
        return sb.toString();
    }
}

商务套餐.class

public class 商务套餐 extends 玛萨基套餐 {
    protected 水果 fruit;
    protected 饮料 beverage;

    public void setFruit(水果 fruit) {
        this.fruit = fruit;
    }

    public void setBeverage(饮料 beverage) {
        this.beverage = beverage;
    }

    @Override
    public String toString() {
        return super.toString() +
                ", 水果是" + fruit.getClass().getSimpleName() +
                ", 饮料是" + beverage.getClass().getSimpleName();
    }
}

玛萨基套餐Builder.class

public abstract class 玛萨基套餐Builder {
    public abstract void build汤(汤 soup);

    public abstract void build主食(主食 stapleFood);

    public abstract void build自选菜(菜[] dishes);

    public abstract 玛萨基套餐 create();

}

标准套餐Builder.class

public class 标准套餐Builder extends 玛萨基套餐Builder {
    private 玛萨基套餐 massage = new 标准套餐();


    @Override
    public void build汤(汤 soup) {
        massage.setSoup(soup);
    }

    @Override
    public void build主食(主食 stapleFood) {
        massage.setStapleFood(stapleFood);
    }

    @Override
    public void build自选菜(菜[] dishes) {
        massage.setDishes(dishes);
    }

    @Override
    public 玛萨基套餐 create() {
        return massage;
    }
}

商务套餐Builder.class

public class 商务套餐Builder extends 玛萨基套餐Builder {
    private 商务套餐 massage = new 商务套餐();

    @Override
    public void build汤(汤 soup) {
        massage.setSoup(soup);
    }

    @Override
    public void build主食(主食 stapleFood) {
        massage.setStapleFood(stapleFood);
    }

    @Override
    public void build自选菜(菜[] dishes) {
        massage.setDishes(dishes);
    }

    public void build饮料(饮料 beverage) {
        massage.setBeverage(beverage);
    }

    public void build水果(水果 fruit) {
        massage.setFruit(fruit);
    }

    @Override
    public 玛萨基套餐 create() {
        return massage;
    }
}

运行结果

Builder模式和Abstract Factory的区别

Builder Abstract Factory
一步步构建一个复杂的product对象 着重于一组product对象的创建
最后一步返回product 立即返回product

简单Builder模式在Java中的运用

结构图

  • Client类
  • 创建Builder和Director对象
  • 将Builder的引用传递给Director
    1. 通过Director的构造函数传入Director director = new Director(xBuilder);
    • 通过Director的方法传入director.setBuilder(xBuilder);
  • 通过Director的construct方法调用Builder的各个buildPart方法,完成Product的构建
  • Product类
  • 如果Product种类比较多
    • Product作为抽象类,具体的产品继承该父类
    • new Product()放在具体产品的Builder中private Product product = new ConcreteProductA();
    • 抽象Builder类提供一个getProduct()方法,在具体的Builder返回具体的Product引用
    • Director的唯一功能就是调用Builder,将各个Part构建起来
  • 如果只有一种Product
    • Product可以是一个具体类
    • new Product()放在抽象Builder中,并对外提供一个createProduct()方法
    • 在Director的construct方法中调用Builder的createProduct()方法
    • 在Director中提供一个getProduct()方法,返回具体Product引用
总结
  • Builder Pattern作为创建型模式,其着眼点在于Product,一切都是围绕这个Product的创建而展开的
  • Product由于整体较为复杂,将其拆分成若干Part,Builder负责将每个部分build好
    • 静态层面Variable,Product类有许多成员变量需要设置值,用Builder模式来替代使用多参数的构造函数
    • 动态层面Method,Product可以拆分成若干步骤,每一步又有其不同的具体实现方法(参见GOF-P75)
  • 抽象Builder提供一个方法,用来给Client类通过Builder来得到最后生成的Product对象
  • Builder模式可以精简实现,参见Android的AlertDialog

参考资料

Sourcemaking
Design Patterns in Java Tutorial
Wiki:Builder pattern

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

推荐阅读更多精彩内容