一天一个设计模式---生成器模式

介绍:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示

场景:我们在创建对象时可能会碰到过一个问题,比如,如果需要赋值的属性过多,构造函数会很多,而且有的会很长。通过JavaBean的方式可能不需要担心构造函数,但是,我们的set部分又会很长很难看,这里推荐大家试试builder模式

一、角色及作用

角色作用
产品(Product)目标创建的复杂对象
抽象生成器(Builder)提供创建一个Product对象的各个组件的方法及返回Product对象的方法
具体生成器(ConcreteBuilder)实现Buidler接口的类。
管理者(Director)用于管理builder对象的工作

二、场景

玩过《我的世界》这类沙盒游戏的玩家,很多都是冲着其开放式的制作功能去的。比如,我们要建造一个房子就需要以下:墙,屋顶,门,床。但是这些东西的制作也十分麻烦。如果每次都让客户端来创建会需要很多代码

内部组件

// 床
public class Bed {
    public Bed(String s) {
        System.out.println(s);
    }
}

// 门
public class Door {
    public Door(String s) {
        System.out.println(s);
    }
}

// 屋顶
public class Roof {
    public Roof(String s) {
        System.out.println(s);
    }
}

// 墙
public class Wall {
    public Wall(String s) {
        System.out.println(s);
    }
}

1.使用构造函数的方式

public class House {
    public Bed bed;
    public Door door;
    public Roof roof;
    public Wall wall;

    public House(Bed bed){
        this.bed = bed;
    }

    public House(Bed bed,Door door){
        this.bed = bed;
        this.door = door;
    }

    // ......省略各种组合的构造函数

}

可见,这种方式会使此类中存在很多构造方法,而且不能满足所有的组合,客户端在制作房屋的时候,比较不适用。

2.使用setter方式

public class House {
    public Bed bed;
    public Door door;
    public Roof roof;
    public Wall wall;

    // setter and getter
}

典型的java bean 的形式,解决上面构造函数的不足,但是让我们来看看使用。

House house = new House();
house.setBed(1);
// ......省略其它set方法

如果我们的私有属性增加到二十,那么我们每次创建的时候就需要写二十行setter代码,累不累?

总结:
上面这两种方法,在每次创建房屋的时候,客户端还需要每次去创建各种类型的对象,会产生很多重复代码。

三、使用builder模式

产品-房子

public class House {
    public Bed bed;
    public Door door;
    public Roof roof;
    public Wall wall;
}

抽象生成器

public interface Builder {

    public void bed();

    public void door();

    public void roof();

    public void wall();

    public House getProduct();
}

生成器的具体实现:木屋

public class WoodHouseBuilder implements Builder {

    private House house = new House();

    @Override
    public void bed() {
        house.bed=new Bed("木床");
    }

    @Override
    public void door() {
        house.door = new Door("木门");
    }

    @Override
    public void roof() {
        house.roof = new Roof("木头屋顶");
    }

    @Override
    public void wall() {
        house.wall = new Wall("木墙");
    }

    @Override
    public House getProduct() {
        return house;
    }

}

生成器的具体实现:别墅

public class VillaHouseBuilder implements Builder {

    private House house = new House();

    @Override
    public void bed() {
        house.bed = new Bed("木床");
    }

    @Override
    public void door() {
        house.door = new Door("铁门");
    }

    @Override
    public void roof() {
        house.roof = new Roof("欧式带天窗屋顶");
    }

    @Override
    public void wall() {
        house.wall = new Wall("石墙");
    }

    @Override
    public House getProduct() {
        return house;
    }

}

build管理者

public class Director {
    private Builder builder;

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

    public void construct() {
        builder.wall();
        builder.roof();
        builder.door();
        builder.bed();
    }
}

开始制作房子

public static void main(String[] args) {
    System.out.println("开始做木屋=======");
    Builder builder = new WoodHouseBuilder();
    Director director = new Director(builder);
    director.construct();
    System.out.println("开始做别墅=======");
    Builder builder2 = new VillaHouseBuilder();
    director = new Director(builder2);
    director.construct();
}

输出

开始做木屋=======
木墙
木头屋顶
木门
木床
开始做别墅=======
石墙
欧式带天窗屋顶
铁门
木床

这样,我们可以明显感觉到在创建对象时候代码量少了很多。而且,对于不同款式的房子,我们只需要不同的Director,对象的属性和创建分离。

优点:
- 生产过程交给Director,客户端不必知道产品的内部结构
- 可以有多个生成器,方便扩展

四、《Effective Java》中的Builder模式

在《Effective Java》中也有对Builder模式的使用,但是感觉在实现上和上面还是有点区别。参考:《Effective Java》 第二章,第2条:遇到多个构造器参数时要考虑用构造器

public class NutFacts {
    private final int serSize;
    private final int fat;
    private final int car;
    private final int so;

    public static class Builder {

        // 必填字段
        private final int serSize;

        // 非必填字段
        private int fat = 0;
        private int car = 0;
        private int so = 0;

        public Builder(int serSize) {
            this.serSize = serSize;
        }

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

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

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

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

    private NutFacts(Builder builder) {
        serSize = builder.serSize;
        fat = builder.fat;
        car = builder.car;
        so = builder.so;
    }
}

创建对象的方法:

NutFacts no = new NutFacts.Builder(1).car(2).so(1).build();

比较:

这个创建对象的方法就像Python中的具名的可选参数。

builder可以对参数加强约束条件,build方法可以用于检验这些约束。

与构造器相比,builder的优势在于可以有多个可变参数,builder参数可以在创建对象期间调整。

和setter相比,属性可以不可变,如上面NutFacts,属性都是不可变得final。但是通过构造器对Builer对象修改,又可以修改属性。


更多模式: 一天一个设计模式—分类与六大原则

更多源码: https://github.com/oDevilo/Java-Base

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值