折騰Java設計模式之建造者模式

大萌小路發表於2019-04-15

博文原址:折騰Java設計模式之建造者模式

建造者模式

Separate the construction of a complex object from its representation, allowing the same construction process to create various representations.

將複雜物件的構造與其表現分離,允許相同的構造過程用來建立不同的表現。通俗點就是,一個物件建立過程很複雜,我們將其每項元素建立過程抽離出來,通過相同的構造過程可以構造出不用的物件。還不懂可以看到如下的UML圖。

建造者模式UML圖

image-20190329153338350

AbstractPersonBuilder就是如上的相同的構造,而不同的表現就是此處的PersonOneBuilderPersonTwoBuilder兩個相同方式的構造器,但是具體的實現是不一樣而構造出不同的表現。所以就是相同的構造過程而構造出不同的物件。

建造者模式角色

抽象建造者(AbstractPersonBuilder或者Builder):抽象類或者介面,複雜物件的屬性的抽象方法,並不涉及具體的物件部件的建立;

具體建造者(PersonOneBuilderPersonTwoBuilder):實現抽象建造者,針對不同的業務,具體化複雜物件的各部分的建立。 在建造過程完成後,提供產品的例項;

指揮者(Director):呼叫具體建造者來建立複雜物件的各個部分,在指導者中不涉及具體產品的資訊,只負責保證物件各部分完整建立或按某種順序建立;

具體的產品(Person):需建立的複雜物件;

建造者模式原始碼乾貨

原始碼地址:請點選我

在這裡我分為三種情況講講建造者模式,第一種是我們最原始的建造者模式來構建,第二種是我們在實體物件時會使用的,第三種是我們平常對實體物件最常規使用方法藉助lombok。

第一種建造者模式

使用的真是上面按照角色來建造的方式,稍微比如下的兩種方法負責點。

抽象建造者

public abstract class AbstractPersonBuilder {

    protected Person product = new Person();

    public abstract void buildName();

    public abstract void buildAge();

    public abstract void buildChildren();

    public Person build() {

        return product;
    }
}
複製程式碼

第一個具體建造者

public class PersonOneBuilder extends AbstractPersonBuilder {

    public void buildName() {
        product.setName("老one");
    }

    public void buildAge() {
        product.setAge(44);
    }

    public void buildChildren() {
        product.setChildren(Lists.newArrayList("小one"));
    }

}
複製程式碼

第二個具體建造者

public class PersonTwoBuilder extends AbstractPersonBuilder {

    public void buildName() {
        product.setName("老two");
    }

    public void buildAge() {
        product.setAge(55);
    }

    public void buildChildren() {
        product.setChildren(Lists.newArrayList("小two"));
    }

}
複製程式碼

Person類充當產品資料

public class Person {

    private String name;

    private int age;

    private List<String> children;

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", children=" + children +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public List<String> getChildren() {
        return children;
    }

    public void setChildren(List<String> children) {
        this.children = children;
    }
}
複製程式碼

指揮者,指定具體的建造者用來建造物件

public class Director {

    private AbstractPersonBuilder builder;

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

    public void setBuilder(AbstractPersonBuilder builder) {
        this.builder = builder;
    }

    public Person construct() {
        builder.buildName();
        builder.buildAge();
        builder.buildChildren();
        return builder.build();
    }
}
複製程式碼

示例

@Slf4j
public class Application {

    public static void main(String[] args) {
        Director director = new Director(new PersonOneBuilder());
        Person person = director.construct();
        log.info("person的資訊:{}", person);

        director.setBuilder(new PersonTwoBuilder());
        person = director.construct();
        log.info("person的資訊:{}", person);
    }
}
複製程式碼

結果:

image-20190329155504038

第二種建造者模式

第二種方式比上面那種簡單些,因為我們只指定了一種構造方式,並且還可以借用第三方工具IDEA+Plugins。

在IDEA中可以搜尋

image-20190329160028861

使用方法:

1.找到對應需要新增bulid的類通過自動生成快捷鍵可以檢視到build

image-20190329160149182

2.根據自己的風格可以定義bulid的名字,各個bulid方法的字首以及包名,具體bulider如下程式碼中。

image-20190329160247685

PersonBuilder用來Person的構建者

public final class PersonBuilder {
    private String name;
    private int age;
    private List<String> children;

    private PersonBuilder() {
    }

    public static PersonBuilder builder() {
        return new PersonBuilder();
    }

    public PersonBuilder withName(String name) {
        this.name = name;
        return this;
    }

    public PersonBuilder withAge(int age) {
        this.age = age;
        return this;
    }

    public PersonBuilder withChildren(List<String> children) {
        this.children = children;
        return this;
    }

    public Person build() {
        Person person = new Person();
        person.setName(name);
        person.setAge(age);
        person.setChildren(children);
        return person;
    }
}
複製程式碼

Person類充當產品資料

public class Person {

    private String name;

    private int age;

    private List<String> children;

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", children=" + children +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public List<String> getChildren() {
        return children;
    }

    public void setChildren(List<String> children) {
        this.children = children;
    }
}
複製程式碼

示例

@Slf4j
public class Application {

    public static void main(String[] args) {
        Person wang = PersonBuilder.builder()
                .withAge(40)
                .withName("老王")
                .withChildren(Lists.newArrayList("李一一", "吳老三"))
                .build();
        log.info("老王的資訊:{}", wang);
    }
}
複製程式碼

結果如下:

image-20190329155831453

第三種建造者模式

第三種模式相對來說就簡單非常多,因為我們借用的是lombok的@Builder註解。lombok在18.2版本中引入了@SuperBulider註解用來解決@Builder類的繼承不生效的問題。詳細的使用闊以看我上篇文章 折騰Java設計模式之模板方法模式

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Person {

    private String name;

    private int age;

    private List<String> children;

}
複製程式碼
@Slf4j
public class Application {

    public static void main(String[] args) {
        Person wang = Person.builder()
                .age(40)
                .name("老王")
                .children(Lists.newArrayList("李一一", "吳老三"))
                .build();
        log.info("老王的資訊:{}", wang);
    }
}
複製程式碼

結果:

image-20190329160655205

總結

第二、三種模式在我們經常操作像對VO、DO、DTO物件時,常用如此定義。第一種標準的建造者模式,其實本身指揮者這個角色是不關心具體的產品實現的,相對於是一種解耦,對於如果新增一種建造者實現,可以方便擴充套件,符合開閉原則,但是無獨有偶,實現了上述優點後,但是缺點也跟著來,新增了很多類,維護成本高,如果建造者內部發生變更,就不太適合建造者這種模式了。總體而言還是有很多使用場景的。像StringBulider其實也是一種。像之前在spring-boot的spring-cache中的擴充套件redis快取的ttl和key名這篇文章中定義的RedisCacheManagerBuilder,以及我們常用的以後要講的Feign的Builder等等。

歡迎關注

微信公眾號

相關文章