设计之禅——生成器模式

时间:2022-07-24
本文章向大家介绍设计之禅——生成器模式,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

概述

Java是一门面向对象的语言,所以在使用它的时候我们首先就需要new一个对象,在创建一个简单对象new当然是没有任何问题的,但是在实际项目中我们往往需要构建一个个复杂的对象,且当某个对象需要多次创建时,我们再通过new去创建,不仅会产生大量冗余代码,而且极不利于维护(想象一下,某个多处被引用的对象需要修改内部属性,那么所有创建对象的代码都需要改变,简直是场灾难)。因此,生成器模式就出现了。

定义

生成器模式对外暴露一个接口,当调用此接口便自动创建好所需对象。它将对象固定的创建流程和其组件的具体实现解耦,使得客户可以专注于组件的实现。

通过类图我们不难发现生成器模式包含了四个角色,主导者、产品、抽象生成器接口以及具体生成器接口,需要注意的是产品最终是从具体生成器获取的而不是主导者。举个例子,比如你要修房子,房子包含了门、窗、墙等等,那你首先要找一队工人和设计师,这里工人就是具体的生成器,设计师则是主导者,由设计师告诉工人该做什么,也就是建造流程,具体怎么修建完成则是工人的事,最终房子修好了你是去找工人要,而不是找设计师要。

Coding

生成器模式的实现有两种,一种是按固定顺序构建,另一种是无序构建,先来看看第一种:

public class Room {

    private String door;
    private String window;
    private String floor;

    public void setDoor(String door) {
        this.door = door;
    }
    public void setWindow(String window) {
        this.window = window;
    }
    public void setFloor(String floor) {
        this.floor = floor;
    }
}

生成器族:

public interface Builder {

    Room room = new Room();

    void makeDoor(String door);

    void makeWindow(String window);

    void makeFloor(String floor);

    default Room getRoom() {
        return room;
    }

}

public class RoomBuilder implements Builder {

    @Override
    public void makeDoor(String door) {
        room.setDoor(door);
    }

    @Override
    public void makeWindow(String window) {
        room.setWindow(window);
    }

    @Override
    public void makeFloor(String floor) {
        room.setFloor(floor);
    }
}

主导者:

public class Designer {

    private Builder builder;

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

    public void build(String door, String window, String floor) {
        builder.makeDoor(door);
        builder.makeDoor(window);
        builder.makeFloor(floor);
    }

}

最后测试:

public static void main(String[] args) {
        Builder builder = new RoomBuilder();
        Designer designer = new Designer(builder);
        designer.build("door", "window", "floor");
        Room room = builder.getRoom();
}

如果产品的生成需要按照固定的顺序构建就可以采用上述的实现方式,而当需要灵活设置产品属性时就可以采用静态内部类的方式来实现无序的生成器模式,代码如下:

public class Room {

    private String door;
    private String window;
    private String floor;

    public Room(Builder builder) {
        this.door = builder.door;
        this.floor = builder.floor;
        this.window = builder.window;
    }

    public static class Builder {
        private String door;
        private String window;
        private String floor;

        public Builder makeDoor(String door) {
            this.door = door;
            return this;
        }
        public Builder makeWindow(String window) {
            this.window = window;
            return this;
        }
        public Builder makeFloor(String floor) {
            this.floor = floor;
            return this;
        }

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

}

测试

public static void main(String[] args) {
        Room room = new Room.Builder().makeDoor("door").makeWindow("window").makeFloor("floor").build();
}

可以看到这种方式更加简洁明了,并且用户可以自行设置所需属性,所以在《Effective Java 第2版》中也是推荐用来替代重叠构造器模式JavaBean模式

当遇到需要重载多个构造器时,考虑用生成器模式来实现,它既保证了代码的整洁可阅读性,还可以避免JavaBean模式的不安全。

所谓重叠构造器模式也就是根据产品的必须属性和可选属性重载多个构造器,这种方式不好的就是当属性多了后代码就难以阅读,用户也难以正确选择构造器;JavaBean模式即我们常用的setter方法,这种方式好在于可以灵活的选择属性,但却无法保证安全性,尤其在多线程环境下,一个线程new完对象还未调用setter时,另一个线程就调用getter,就无法保证类的一致性;并且该方式产生的重复代码也非常多,所以当遇到需要多个构造器的情况下,考虑使用生成器模式

总结

生成器模式也是平时常用到的,弄清楚它的两种实现方式,在实际项目中遇到需要构建复杂对象时考虑使用它吧。