java设计模式之建造者模式笔记记录

定义

建造者模式是一种对象创建型模式,它将客户端与包含多个部件的复杂对象的创建过程分离,客户端无须知道复杂对象的内部组成部分与装配方式,只需要知道所需建造者的类型即可。建造者模式关注如何一步一步地创建一个复杂对象,不同的建造者定义了不同的创建过程。

模式结构

一个完整的建造者模式一般由Director(指挥者) Builder(抽象建造者) ConcreteBuilder(具体建造者) Product(产品)四部分组成。
UML类图关系如下:


建造者模式结构图.jpg

模式结构代码实现

Director:

package com.zhaohy.app.builderModel;

import com.alibaba.fastjson.JSONObject;

public class Director {
    private Builder builder;
    
    public Director(Builder builder) {
        this.builder = builder;
    }

    public void setBuilder(Builder builder) {
        this.builder = builder;
    }
    
    public Product construct() {
        builder.buildPartA();
        builder.buildPartB();
        builder.buildPartC();
        return builder.getResult();
    }
    
    public static void main(String[] args) {
        Builder builder = new ConcreteBuilder1();//可通过配置文件用java反射的方式实现
        Director director = new Director(builder);
        Product product = director.construct();
        System.out.println(JSONObject.toJSONString(product));
    }
}

Builder:

package com.zhaohy.app.builderModel;

public abstract class Builder {
    protected Product product = new Product();
    
    public abstract void buildPartA();
    
    public abstract void buildPartB();
    
    public abstract void buildPartC();
    
    public Product getResult() {
        return product;
    }
}

ConcreteBuilder1:

package com.zhaohy.app.builderModel;

public class ConcreteBuilder1 extends Builder{

    @Override
    public void buildPartA() {
        product.setPartA("A1");
    }

    @Override
    public void buildPartB() {
        product.setPartB("B1");
    }

    @Override
    public void buildPartC() {
        product.setPartC("C1");
    }

}

Product:

package com.zhaohy.app.builderModel;

public class Product {
    private String partA;//定义部件,部件可以是任意类型,包括值类型和引用类型
    
    private String partB;
    
    private String partC;

    public String getPartA() {
        return partA;
    }

    public void setPartA(String partA) {
        this.partA = partA;
    }

    public String getPartB() {
        return partB;
    }

    public void setPartB(String partB) {
        this.partB = partB;
    }

    public String getPartC() {
        return partC;
    }

    public void setPartC(String partC) {
        this.partC = partC;
    }
}

如上代码在Director里运行main方法,输出如下:

{"partA":"A1","partB":"B1","partC":"C1"}

可以看到上面在Drector类中可以注入一个抽象Builder类型的对象,它提供了一个建造方法construct(),在该方法中调用了builder对象的构造部件的方法,最后返回一个产品对象。对于客户端而言,只需要关心具体建造者的类型,无须关心产品对象的具体组装过程。

注意

建造者模式与抽象工厂模式都是较为复杂的创建型模式,建造者模式返回一个完整的复杂产品,抽象工厂模式返回一系列相关的产品;在抽象工厂模式中,客户端通过选择具体工厂来生成所需对象,而在建造者模式中,客户端通过指定具体建造者类型来指导Director类如何去生成对象,侧重于一步步构造一个复杂对象,然后将结果返回。如果将抽象工厂模式看成一个汽车配件生产厂,生成不同类型的汽车配件,那么建造者模式就是一个汽车组装厂,通过对配件进行组装返回一辆完整的汽车。

应用实例

游戏角色的设计,比如有英雄,天使,恶魔等角色,都有性别,面部特征,发型,服装等属性,就可以用建造者模式来生成不同角色的游戏角色。

实例类图
建造者模式应用实例类图.jpg
实例代码

ActorDirector:

package com.zhaohy.app.actorBuilderModel;

import com.alibaba.fastjson.JSONObject;

public class ActorDirector {

    public Actor construct(ActorBuilder ab) {
        ab.buildType();
        ab.buildSex();
        ab.buildFace();
        ab.buildCostume();
        ab.buildHairstyle();
        return ab.createActor();
    }
    
    public static void main(String[] args) {
        String type = "天使";
        ActorDirector director = new ActorDirector();
        ActorType typeEnum = ActorType.resolve(type);
        if(null == typeEnum) {
            System.out.println("type不合法");
            return;
        }
        
        ActorBuilder ab = null;
        try {
            ab = (ActorBuilder) getActorBuilder(typeEnum.getClassName());
        } catch (Exception e) {
            e.printStackTrace();
        }
        Actor actor = director.construct(ab);
        System.out.println(JSONObject.toJSONString(actor));
    }

    private static Object getActorBuilder(String className) throws Exception {
        Class<?> c = Class.forName(className);
        return c.newInstance();
    }
    
}

ActorBuilder:

package com.zhaohy.app.actorBuilderModel;

public abstract class ActorBuilder {
    protected Actor actor = new Actor();
    
    public abstract void buildType();
    
    public abstract void buildSex();
    
    public abstract void buildFace();
    
    public abstract void buildCostume();
    
    public abstract void buildHairstyle();
    
    public Actor createActor() {
        return actor;
    }
}

Actor:

package com.zhaohy.app.actorBuilderModel;

public class Actor {
    private String type;//角色类型
    
    private String sex;
    
    private String face;//脸型
    
    private String costume;//服装
    
    private String hairstyle; //发型

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getFace() {
        return face;
    }

    public void setFace(String face) {
        this.face = face;
    }

    public String getCostume() {
        return costume;
    }

    public void setCostume(String costume) {
        this.costume = costume;
    }

    public String getHairstyle() {
        return hairstyle;
    }

    public void setHairstyle(String hairstyle) {
        this.hairstyle = hairstyle;
    }
}

AngelBuilder:

package com.zhaohy.app.actorBuilderModel;

public class AngelBuilder extends ActorBuilder {

    @Override
    public void buildType() {
        actor.setType("天使");
    }

    @Override
    public void buildSex() {
        actor.setSex("女");
    }

    @Override
    public void buildFace() {
        actor.setFace("漂亮");
    }

    @Override
    public void buildCostume() {
        actor.setCostume("白裙");
    }

    @Override
    public void buildHairstyle() {
        actor.setHairstyle("披肩长发");
    }
}

DevilBuilder:

package com.zhaohy.app.actorBuilderModel;

public class DevilBuilder extends ActorBuilder {

    @Override
    public void buildType() {
        actor.setType("恶魔");
    }

    @Override
    public void buildSex() {
        actor.setSex("妖");
    }

    @Override
    public void buildFace() {
        actor.setFace("丑陋");
    }

    @Override
    public void buildCostume() {
        actor.setCostume("黑衣");
    }

    @Override
    public void buildHairstyle() {
        actor.setHairstyle("光头");
    }

}

HeroBuilder:

package com.zhaohy.app.actorBuilderModel;

public class HeroBuilder extends ActorBuilder {

    @Override
    public void buildType() {
        actor.setType("英雄");
    }

    @Override
    public void buildSex() {
        actor.setSex("男");
    }

    @Override
    public void buildFace() {
        actor.setFace("英俊");
    }

    @Override
    public void buildCostume() {
        actor.setCostume("盔甲");
    }

    @Override
    public void buildHairstyle() {
        actor.setHairstyle("飘逸");
    }

}

ActorType枚举类:

package com.zhaohy.app.actorBuilderModel;

import com.baomidou.mybatisplus.core.toolkit.StringUtils;

public enum ActorType {
    HERO("英雄", "com.zhaohy.app.actorBuilderModel.HeroBuilder"),
    ANGEL("天使", "com.zhaohy.app.actorBuilderModel.AngelBuilder"),
    DEVIL("恶魔", "com.zhaohy.app.actorBuilderModel.DevilBuilder");
    
    private String type;
    
    private String className;
    
    ActorType(String type, String className){
        this.type = type;
        this.className = className;
    }
    
    public static ActorType resolve(String type) {
        if (StringUtils.isBlank(type)) {
            return null;
        }
        for (ActorType status : values()) {
            if (status.type.equals(type)) {
                return status;
            }
        }
        return null;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getClassName() {
        return this.className;
    }

    public void setClassName(String className) {
        this.className = className;
    }
}

如上运行ActorDirector里的main方法 得到如下结果:

{"costume":"白裙","face":"漂亮","hairstyle":"披肩长发","sex":"女","type":"天使"}
Director指挥者类的深入讨论

1.省略Director
当只有一种Builder业务类型时,可以简化结构省略掉Director类,把Director类的构造方法放进抽象Builder类中定义。

package com.zhaohy.app.actorBuilderModel;

public abstract class ActorBuilder {
   protected Actor actor = new Actor();
   
   public abstract void buildType();
   
   public abstract void buildSex();
   
   public abstract void buildFace();
   
   public abstract void buildCostume();
   
   public abstract void buildHairstyle();
   
   public Actor createActor() {
       return actor;
   }
   
   public Actor construct() {
       buildType();
       buildSex();
       buildFace();
       buildCostume();
       buildHairstyle();
       return actor;
   }
}

这样main方法就可以改成:

public static void main(String[] args) {
        String type = "天使";
//      ActorDirector director = new ActorDirector();
        ActorType typeEnum = ActorType.resolve(type);
        if(null == typeEnum) {
            System.out.println("type不合法");
            return;
        }
        
        ActorBuilder ab = null;
        try {
            ab = (ActorBuilder) getActorBuilder(typeEnum.getClassName());
        } catch (Exception e) {
            e.printStackTrace();
        }
//      Actor actor = director.construct(ab);
        Actor actor = ab.construct();
        System.out.println(JSONObject.toJSONString(actor));
    }

2.钩子方法的引入
建造者模式除了可以逐步构建一个复杂产品对象外,还可以通过Director类更加精细控制产品的创建过程,例如增加一类称为钩子方法的特殊方法来控制是否对某个buildPartX()进行调用。
钩子方法的返回一般是boolean类型,方法名一般为isXXX(),钩子方法定义在抽象建造者类中。
例如isBareheaded()用于判断是否为光头,在ActionBuilder提供一个默认实现,返回值为false:

package com.zhaohy.app.actorBuilderModel;

public abstract class ActorBuilder {
    protected Actor actor = new Actor();
    
    public abstract void buildType();
    
    public abstract void buildSex();
    
    public abstract void buildFace();
    
    public abstract void buildCostume();
    
    public abstract void buildHairstyle();
    
    public Actor createActor() {
        return actor;
    }
    
    public Actor construct() {
        buildType();
        buildSex();
        buildFace();
        buildCostume();
        buildHairstyle();
        return actor;
    }
    
    //钩子方法
    public boolean isBareHeaded() {
        return false;
    }
}

Devil覆盖isBareheaded()方法返回true:

package com.zhaohy.app.actorBuilderModel;

public class DevilBuilder extends ActorBuilder {

    @Override
    public void buildType() {
        actor.setType("恶魔");
    }

    @Override
    public void buildSex() {
        actor.setSex("妖");
    }

    @Override
    public void buildFace() {
        actor.setFace("丑陋");
    }

    @Override
    public void buildCostume() {
        actor.setCostume("黑衣");
    }

    @Override
    public void buildHairstyle() {
        actor.setHairstyle("光头");
    }

    //覆盖钩子方法
    public boolean isBareHeaded() {
        return true;
    }
}

指挥者类代码修改如下:

public class ActorDirector {

    public Actor construct(ActorBuilder ab) {
        ab.buildType();
        ab.buildSex();
        ab.buildFace();
        ab.buildCostume();
        if(!ab.isBareHeaded()) {
            ab.buildHairstyle();
        }
        return ab.createActor();
    }
}

可见,通过引入钩子方法,可以在Director中对复杂产品的构建进行精细的控制,不仅指定buildPartX()方法的执行顺序,还可以控制是否需要执行某个buildPartX()方法。

建造者模式优点

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

建造者模式缺点

1.建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,例如很多组成部分都不相同,不适合使用建造者模式,因此其使用范围受到一定的限制。
2.如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大,增加系统的理解难度和运行成本。

建造者模式适用环境

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

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

推荐阅读更多精彩内容