Java設計模式學習記錄-建造者模式

前言

今天週末,有小雨,正好也不用出門了,那就在家學習吧,通過了兩週的面試,拿到了幾個offer,可是都不是本身很想去的那種,要麼就是幾我的的初創小公司,要麼就是開發企業內部系統的這種傳統開發,感受這種傳統開發已經不能給本身帶來多大的提高了,由於工做了這幾年這種系統經歷了很多了,成天的就是增刪改查。創業小公司已經不想再去了,工做了這幾年去的都是這種小公司,風險大,壓力大,節奏快,沒時間沉澱學習。上上家東家還欠我幾個月工資呢,就是由於創業公司資金鍊斷了,而後老闆忽悠領導,領導再忽悠咱們,後來實在發不出工資了,忽悠不住了,就大批大批的走人了。前端

因此如今非常糾結,大點公司又去不了小的公司還看不上,目前就是這麼個高不成低不就的狀態,因此仍是抓緊時間學習,充實本身吧,哪怕如今進不去稍微大點的公司,那通過努力的學習後說不定仍是有機會的,可是不努力是一點機會都沒有的。面試

好了,言歸正傳,此次要介紹的是建立型設計模式的最後一個,建造者模式,這個模式其實我在平時開發中用的不少,只不過是用了這個模式的更深一種形式吧。後面我會介紹到這一部份內容的。數據庫

建造者模式

建造者模式可以將一個複雜的構建與其表示相分離,使得一樣的構建過程能夠建立不一樣的表示。這句話理解起來可能有點抽象,簡單來講就是調用相同的建立對象的方法(建造過程)能夠建立出不一樣的對象。 後端

仍是舉例來講明吧,若是說我要建立一部手機,我須要先製造手機的幾個核心部件,例如:屏幕、電池、聽筒、話筒、機身等。設計模式

public class MobilePhone {

    //手機屏幕
    private String screen;
    //電池
    private String battery;
    //話筒
    private String microphone;
    //聽筒
    private String phoneReceiver;
    //機身
    private String phoneBody;

    public String getScreen() {
        return screen;
    }

    public void setScreen(String screen) {
        this.screen = screen;
    }

    public String getBattery() {
        return battery;
    }

    public void setBattery(String battery) {
        this.battery = battery;
    }

    public String getMicrophone() {
        return microphone;
    }

    public void setMicrophone(String microphone) {
        this.microphone = microphone;
    }

    public String getPhoneReceiver() {
        return phoneReceiver;
    }

    public void setPhoneReceiver(String phoneReceiver) {
        this.phoneReceiver = phoneReceiver;
    }

    public String getPhoneBody() {
        return phoneBody;
    }

    public void setPhoneBody(String phoneBody) {
        this.phoneBody = phoneBody;
    }
}

每一部手機都是這個類的對象,在建立一部手機的時候都要保證這幾個核心部件的建立。因此建立手機是須要一個標準規範的,由於這幾個核心部件均可以是不一樣的型號,不一樣的型號的部件製造出來的手機也是不一樣的,這樣就有了下面建造規範的接口。框架

public interface IBuildPhone {

    /**
     * 建造手機屏幕
     */
    void buildScreen();

    /**
     * 建造手機電池
     */
    void buildBattery();

    /**
     * 建造手機聽筒
     */
    void buildMicrophone();

    /**
     * 建造手機話筒
     */
    void buildPhoneReceiver();

    /**
     * 建造手機機身
     */
    void buildPhoneBody();
}

有了規範了,就能夠建立手機了,先建立一個iphoneX。iphone

public class IPhoneX implements IBuildPhone {

    private MobilePhone mobilePhone;

    public IPhoneX(){
        mobilePhone =  new MobilePhone();
    }

    /**
     * 建造手機屏幕
     */
    @Override
    public void buildScreen() {
        mobilePhone.setScreen("OLED顯示屏");
    }

    /**
     * 建造手機電池
     */
    @Override
    public void buildBattery() {
        mobilePhone.setBattery("2700mAh電池容量");
    }

    /**
     * 建造手機聽筒
     */
    @Override
    public void buildMicrophone() {
        mobilePhone.setMicrophone("聽筒");
    }

    /**
     * 建造手機話筒
     */
    @Override
    public void buildPhoneReceiver() {
        mobilePhone.setPhoneReceiver("話筒");
    }

    /**
     * 建造手機機身
     */
    @Override
    public void buildPhoneBody() {
        mobilePhone.setPhoneBody("iphoneX機身");
    }

    /**
     * 建立手機
     * @return
     */
    public MobilePhone build(){
        return mobilePhone;
    }
}

建立手機的工具寫好了,下面就可使用了。ide

public class Director {

    /**
     * 建造一部手機
     * @param buildPhone
     * @return
     */
    public MobilePhone createMobilePhone(IBuildPhone buildPhone){

        buildPhone.buildBattery();
        buildPhone.buildMicrophone();
        buildPhone.buildScreen();
        buildPhone.buildPhoneReceiver();
        buildPhone.buildPhoneBody();

        return buildPhone.createMobilePhone();
    }

    @Test
    public void thatTest(){
        System.out.println(JSON.toJSONString(createMobilePhone(new IPhoneX())));
    }
}

關鍵的方法在createMobilePhone()方法,這個方法接收一個IBuildPhone接口的對象,因此只要符合這個建立手機規範的對象均可以建立一部手機。createMobilePhone()方法能夠接收new IPhoneX()這樣一個對象,也能夠接收new IPhone8()、new FindX()等等。函數

具體使用方法在thatTest()方法中。這個方法的運行結果是:工具

{"battery":"2700mAh電池容量","microphone":"聽筒","phoneBody":"iphoneX機身","phoneReceiver":"話筒","screen":"OLED顯示屏"}

上面這個例子的實現過程就使用了咱們今天要說的建造者模式,咱們來分析一下建造者模式的結構。

以下圖:

在建造者模式的結構圖中包含以下4個角色。

Builder(抽象建造者):它(IBuildPhone)爲建立一個產品的各個部件指定了標準,規定了要建立複雜對象須要建立哪些部分,並不直接建立對象的具體部分。

ConcreteBuilder(具體建造者):它實現了Builder接口(IPhoneX),實現各個部分的具體構造和裝配方法,定義並明確它所建立的複雜對象,也能夠提供一個方法返回建立好的複雜產品對象。

Product(產品角色):它(MobilePhone)是被建造的複雜對象,包含多個組成部分,具體建造者建立該產品的內部表示並定義它的裝配過程。

Director(指揮者):指揮者(Director),它複雜安排複雜對象的建造次序,指揮者與抽象建造者之間存在關聯關係,能夠在Director的方法中調用建造者對象的部件構造與裝配方法,完成建造複雜對象的任務。客戶端通常只需與Director進行交互。

建造者模式靈活使用

好了,建造者模式到這裏就算是介紹完了,而後說一說咱們平時在項目中是怎麼使用建造者模式的。先說一下場景,咱們通常在開發的過程當中都是須要分層的,MVC這個不通常人都不陌生吧,Model-View-Controller。(我這裏只是舉例子不必定真的項目中就這樣用)那咱們的數據在每一層的傳輸過程當中若是須要增長或刪除些額外的功能怎麼實現呢?

仍是舉例子吧,以下面一個實體類:

public class Person {
    
    private Long id;
    
    private String name;
    
    private int age;
    
    private String address;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    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 String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}

若是說這個類是一個orm框架須要的實體類,它最好的場景是隻被後端的數據操做使用,可是controller中有一個add方法,這個方法是新增一我的員,add方法接收的參數是一我的員對象,可是這個對象和上面這個Person得屬性有些差異,例如這個對象裏面有請求ip,以及這個對象中沒有id這個字段(id在數據庫中自增,因此前端不容許傳過來id )。這個時候就不能使用Person類的對象做爲add的方法了,須要再建立一個類專門來給Controller使用。

以下代碼:

/**
 * Controller使用的參數類
 */
public class PersonVO {

    private String name;

    private int age;

    private String address;

    //ip地址
    private String requestIP;

    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 String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getRequestIP() {
        return requestIP;
    }

    public void setRequestIP(String requestIP) {
        this.requestIP = requestIP;
    }
}

參數對象能夠建立了, 可是PersonVO的對象是須要轉成Person的對象的,這樣才能插入到數據庫中(數據庫的insert方法的參數是Person對象)。這種轉換操做其實也簡單以下代碼:

public Person convert2Person(PersonVO personVO){

        Person person = new Person();

        person.setName(personVO.getName());
        person.setAge(personVO.getAge());
        person.setAddress(personVO.getAddress());
        
        return person;
    }

可是咱們一般是不這麼作的,由於若是要轉換的這個對象的字段不少那須要寫不少次對象調setter方法來進行賦值。一種方式是直接寫一個將全部屬性當作參數的構造方法,直接一個一個把屬性值傳入就能夠了,這種方式最簡單暴力。還有一種方式就是須要包裝一下這種方式,把Person改造一下。

以下代碼:

public class Person {


    private Long id;

    private String name;

    private int age;

    private String address;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    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 String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

     Person(){}
     
     Person(String name,int age,String address){
        this.name = name;
        this.age = age;
        this.address = address;
    }

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

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

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

    public Person address(String address) {
        this.address = address;
        return this;
    }

    public Person build(){
        return new Person(name,age,address);
    }
    
}

後面新增了兩個構造函數,以及一個builder()方法和一個build()方法,還有幾個賦值方法,須要注意的是賦值方法和setter方法的區別,這樣的賦值方法是在賦值後將當前對象返回,用來實現鏈式調用。

這樣在對象轉換的時候就能夠這樣用了:

public Person convert2Person(PersonVO personVO){

        return Person.builder()
                .name(personVO.getName())
                .age(personVO.getAge())
          .address(personVO.getAddress()) .build(); }

這種方式其實也是一種建造者模式的應用,這種方式在構建對象的過程實現起來更靈活,例如若是這個對象就只有前兩個參數有值,address是沒有內容的,那能夠直接這樣寫:

public Person convert2Person(PersonVO personVO){

        return Person.builder()
                .name(personVO.getName())
                .age(personVO.getAge())
                .build();
    }

在填充了兩個屬性後就直接調用build()方法區建立對象。

其實爲了實現這種建立對象的方式,每次除了寫getter/setter方法後還須要寫這麼多其餘的代碼,這樣是有點麻煩的,因此在平常的開發過程當中,咱們是不必寫額外的代碼來實現這種方式,能夠用工具來實現。推薦一個工具包Lombok,咱們的開發工具是使用IDEA,IDEA在使用Lombok時是須要下載一個lombok的插件,而後在項目中依賴lombok的工具包,就可使用了。使用了lombok後的代碼變的很是簡潔,連getter/setter方法都不用寫了。

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


    private Long id;

    private String name;

    private int age;

    private String address;

}

@Data 這個註解表明實現了全部非final屬性的getter/setter方法,以及重寫了toString方法和hashCode方法。

@AllArgsConstructor 這個註解表明實現了一個包含所有屬性爲參數的構造方法(Person(Long id,String name,int age, String address))。

@NoArgsConstructor 這個註解表明實現了一個沒有任何參數的構造方法(Person())。

@Builder 這個註解表明實現了上面介紹的那種靈活的建立對象的建造者模式(使用這個註解時須要依賴上面3個註解,緣由看這種方式的實現過程就能明白了)。

在建立對象時,使用方式沒有變化也是鏈式調用方法賦值,這裏就再也不寫建立對象的過程了。

其實lombok還有一些其餘的註解也很強大,使用這個工具包的好處是,不但使代碼變得簡潔,也提升了開發效率。

在這裏想到了jQuery插件倡導的那個原則:「寫的更少,作的更多」

相關文章
相關標籤/搜索