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.隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

友情链接更多精彩内容