资讯专栏INFORMATION COLUMN

建造者(Builder)模式 的若干使用场景

abson / 3773人阅读

摘要:建造的过程在指挥者这里完成,用户就不需要知道了客户端代码场景二遇到多个构造器参数时要考虑用构建器。重叠构造器模式可行,但是当有许多参数的时候,客户端代码会很难编写,并且仍然难以阅读。

1.场景一

如果我们需要将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示的意图时,我们可以使用 Builder模式,又叫生成器模式。如果我们用了Builder模式,那么用户就只需要指定需要建造的类型就可以得到它们,而具体建造的过程和细节就不需要知道了。
比如现在我们有一个这样的使用场景,需要在屏幕上画小人,人要有头手脚,要画不同的人,胖的小人,瘦的小人,矮的小人。按照通常的写法,会有很多的样板代码,画人的头,画人脚手,如果一不小心,非常容易缺胳膊少腿。
下面我们演示Builder模式的实现:

public class Person {
    //限于篇幅get和set方法此处省略
    Head head;
    Body body;
    Arm leftArm;
    Arm rightArm;
    Leg leftLeg;
    Leg rightLeg;
    
    public void drawHead(int size){...}
    public void drawBody(int size){...}
    public void drawLeftArm(int size){...}
    public void drawRightArm(int size){...}
    public void drawLeftLeg(int size){...}
    public void drawRightLeg(int size){...}
}

abstract class BuilderPerson {
    protected Person person = new Person();
    public abstract void buildHead();
    public abstract void buildBody();
    public abstract void buildLeftArm();
    public abstract void buildRightArm();
    public abstract void buildLeftLeg();
    public abstract void buildRightLeg();
}

public class BuilderThinPerson extends BuilderPerson{
    
    @Override
    public void buildHead() {
        person.drawHead(10);
    }
    
    @Override
    public void buildBody() {
        person.drawBody(10);   //画胖小人只需将这边的数值修改,
                               // 再生成一个类即可
    }
    
    @Override
    public void buildLeftArm() {
        person.drawLeftArm(5);
    }
    
    @Override
    public void buildRightArm() {
        person.drawRightArm(5);
    }
    
    @Override
    public void buildLeftLeg() {
        person.drawLeftLeg(7);
    }
    
    @Override
    public void buildRightLeg() {
        person.drawRightLeg(7);
    }
}
我们还缺Builder模式中一个非常重要的类,指挥者(Director),用它来控制建造过程,也用来隔离用户与建造过程的关联。
public class PersonDirector{
    private BuilderPerson pb;
    public PersonDirector(BuilderPerson pb){
        this.pb = pb;
    }
    //建造的过程在指挥者这里完成,用户就不需要知道了
    public void createPerson() {
        pb.buildHead();
        pb.buildBody();
        pb.buildLeftArm();
        pb.buildRightArm();
        pb.buildLeftLeg();
        pb.buildRightLeg();
    }
}
客户端代码
BuilderPerson bp = new BuilderThinPerson();
PersonDirector pd = new PersonDirector(bp);
pd.createPerson();

2.场景二

遇到多个构造器参数时要考虑用构建器。静态工厂和构造器有个共同的局限性:它们都不能很好地扩展到大量的可选参数。
考虑这样的一个场景:用一个类表示包装食品外面显示的营养成分标签。这些标签中有几个域是必需的:每份的含量、每罐的含量以及每份的卡路里,还有超过20个可选域:总脂肪量、饱和脂肪量、转化脂肪、胆固醇、钠等等。
程序员一向习惯采用重叠构造器模式,在这种模式下,你提供第一个只有必要参数的构造器,第二个构造器有一个可选参数,第三个有两个可选参数,以此类推,最后一个构造器包含所有可选参数。重叠构造器模式可行,但是当有许多参数的时候,客户端代码会很难编写,并且仍然难以阅读。一长串类型相同的参数会导致一些微妙的错误。如果客户端不小心颠倒了其中两个参数的顺序,编译器也不会出错,但是程序在运行时会出现错误的行为。
下面是Builder模式的代码:

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;
        private int fat;
        private int sodium;
        private int carbohydrate;
        
        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;
    }
}

NutritionFacts是不可变的。builder的setter方法返回builder本身,以便可以把调用链接起来。下面就是客户端代码:

NutritionFacts juice = new NutritionFacts.Builder(240, 8).
    calories(100).sodium(35).carbohydrate(27).build();

这样的客户端代码很容易编写,更重要的是,易于阅读。

参考:
1.《大话设计模式》
2.《Effective Java 第二版》

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/66005.html

相关文章

  • JS 设计模式 八(建造模式

    摘要:即建造者模式可以强制实行一种分步骤进行的建造过程。指导类负责调用适当的建造者来组建产品,指导类一般不与产品类发生依赖关系,与指导类直接交互的是建造者类建造者模式优点建造者模式的封装性很好。 建造者模式 将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。主要解决在软件系统中,有时候面临着一个复杂对象的创建工作,由于需求的变化,这个复杂对象的某些部分经常面临着剧烈的变化...

    tanglijun 评论0 收藏0
  • 一天一个设计模式之JS实现——建造模式

    摘要:参考文章设计模式之建造者模式一什么是建造者模式建造者模式是将一个复杂的对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。 参考文章:java设计模式之建造者模式 一、什么是建造者模式建造者模式:是将一个复杂的对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。工厂类模式提供的是创建单个类的模式,而建造者模式则是将各种产品集中起来进行管理,用来创建复合对象,所谓...

    boredream 评论0 收藏0
  • Java设计模式-建造模式

    摘要:在建造者模式比较庞大时,导演类可以有多个。该种场景只能是一个补偿方法,因为一个对象不容易获得,而在设计阶段竟然没有发觉,而要通过创建者模式柔化创建过程,本身已经违反设计的最初目标。源码地址参考文献设计模式之禅 定义 Separate the construction of a complex object from its representation so that the same...

    txgcwm 评论0 收藏0
  • 《源码中设计模式》之建造模式——链式调用

    摘要:上期原型模式发布以后,收到了粉丝的感谢,一条创作的动力更足了。今天我们一块看一下建造者模式,同样是创建型设计模式。为我们提供了建造者模式的快速实现,要应用到实际编码中。 ...

    wind3110991 评论0 收藏0
  • JavaScript设计模式系列三:建造模式

    摘要:优点建造者模式的封装性很好,对象本身与构建过程解耦。建造者模式很容易进行扩展。适用场景需要生成的对象具有复杂得内部结构且内部属性本身相互依赖建造者模式的代码实现建造者模式主要有个部分产品类建造者类指挥者类客户。建造者完成相应的部分。 建造者模式 建造者模式(builder pattern)比较简单,它属于创建型模式的一种,将一个复杂的对象分解成多个简单的对象来进行构建,将复杂的构建层与...

    CloudDeveloper 评论0 收藏0

发表评论

0条评论

abson

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<