1. 程式人生 > 其它 >設計模式 - 建造者模式

設計模式 - 建造者模式

目錄

例項

軟體介面多種顯示模式

假設一個視訊播放軟體提供多種介面顯示模式的應用場景,該軟體支援完整模式、精簡模式、記憶模式等;在不同的顯示模式下主介面的組成元素有所差異,如:在完整模式下顯示主選單、播放列表、主視窗、控制條;在精簡模式下只顯示主視窗、控制條;在記憶模式下顯示主視窗、控制條、收藏列表

  • 軟體結構示意圖如下:

可以看出,顯示模式是一個複雜物件,包含主選單、播放列表、主視窗、控制條等多個組成部分,不同的顯示模式其組成部分有所差異,如何將這些組成部分組裝成一個完整的顯示模式返回給使用者,建造者模式為解決此類問題而誕生


建造者模式

概念

  • 建造者模式(Builder Pattern),將一個複雜物件的構建與它的表示分離,使得同樣的構建過程可以建立不同的表示
  • 建造者模式又稱為生成器模式,它是一種建立型模式

解決方案

  • Mode.java
/**
 * @Description 模式產品類
 */
public class Mode {

    /**
     * 模式名稱
     */
    private String modeName;

    /**
     * 是否顯示主選單
     */
    private Boolean showMainMenu;

    /**
     * 是否顯示播放列表
     */
    private Boolean showPlayList;

    /**
     * 是否顯示主視窗
     */
    private Boolean showMainWindow;

    /**
     * 是否顯示控制條
     */
    private Boolean showControlBar;

    /**
     * 是否顯示收藏列表
     */
    private Boolean showFavoriteList;

	// ...get set省略
	
    @Override
    public String toString() {
        return "Mode{" +
                "modeName='" + modeName + '\'' +
                ", showMainMenu=" + showMainMenu +
                ", showPlayList=" + showPlayList +
                ", showMainWindow=" + showMainWindow +
                ", showControlBar=" + showControlBar +
                ", showFavoriteList=" + showFavoriteList +
                '}';
    }
}
  • ModeBuilder.java
/**
 * @Description 模式抽象建造者
 */
public abstract class ModeBuilder {

    protected static Mode mode = new Mode();

    /**
     * 構造模式名稱
     */
    public abstract void buildModeName();

    /**
     * 構造主選單
     */
    public abstract void buildShowMainMenu();

    /**
     * 構造播放列表
     */
    public abstract void buildShowPlayList();

    /**
     * 構造主視窗
     */
    public abstract void buildShowMainWindow();

    /**
     * 構造控制條
     */
    public abstract void buildShowControlBar();

    /**
     * 構造收藏列表
     */
    public abstract void buildShowFavoriteList();

	/**
     * 構建產品物件
     * @return
     */
    public Mode construct() {
        this.buildModeName();
        this.buildShowMainMenu();
        this.buildShowPlayList();
        this.buildShowMainWindow();
        this.buildShowControlBar();
        this.buildShowFavoriteList();
        return mode;
    }

}
  • FullModeBuilder.java
/**
 * @Description 完整模式建造者
 */
public class FullModeBuilder extends ModeBuilder {

    @Override
    public void buildModeName() { mode.setModeName("完整模式"); }

    @Override
    public void buildShowMainMenu() {
        mode.setShowMainMenu(true);
    }

    @Override
    public void buildShowPlayList() {
        mode.setShowPlayList(true);
    }

    @Override
    public void buildShowMainWindow() {
        mode.setShowMainWindow(true);
    }

    @Override
    public void buildShowControlBar() { mode.setShowControlBar(true); }

    @Override
    public void buildShowFavoriteList() { mode.setShowFavoriteList(false); }

}
  • SimplyModeBuilder.java
/**
 * @Description 精簡模式建造者
 * @author coisini
 * @date Mar 6, 2022
 * @Version 1.0
 */
public class SimplyModeBuilder extends ModeBuilder {

    @Override
    public void buildModeName() { mode.setModeName("精簡模式"); }

    @Override
    public void buildShowMainMenu() {
        mode.setShowMainMenu(false);
    }

    @Override
    public void buildShowPlayList() {
        mode.setShowPlayList(false);
    }

    @Override
    public void buildShowMainWindow() {
        mode.setShowMainWindow(true);
    }

    @Override
    public void buildShowControlBar() { mode.setShowControlBar(true); }

    @Override
    public void buildShowFavoriteList() { mode.setShowFavoriteList(false); }

}
  • MemoryModeBuilder.java
/**
 * @Description 記憶模式建造者
 * @author coisini
 * @date Mar 6, 2022
 * @Version 1.0
 */
public class MemoryModeBuilder extends ModeBuilder {

    @Override
    public void buildModeName() { mode.setModeName("記憶模式"); }

    @Override
    public void buildShowMainMenu() {
        mode.setShowMainMenu(false);
    }

    @Override
    public void buildShowPlayList() {
        mode.setShowPlayList(false);
    }

    @Override
    public void buildShowMainWindow() {
        mode.setShowMainWindow(true);
    }

    @Override
    public void buildShowControlBar() { mode.setShowControlBar(true); }

    @Override
    public void buildShowFavoriteList() { mode.setShowFavoriteList(true); }

}
  • Test.java
/**
 * @Description 建造者模式測試類
 */
public class Test {
    public static void main(String[] args) {
        ModeBuilder modeBuilder = new FullModeBuilder();
        System.out.println(modeBuilder.construct());

        modeBuilder = new SimplyModeBuilder();
        System.out.println(modeBuilder.construct());

        modeBuilder = new MemoryModeBuilder();
        System.out.println(modeBuilder.construct());
    }
}
  • 輸出如下:
Mode{modeName='完整模式', showMainMenu=true, showPlayList=true, showMainWindow=true, showControlBar=true, showFavoriteList=false}
Mode{modeName='精簡模式', showMainMenu=false, showPlayList=false, showMainWindow=true, showControlBar=true, showFavoriteList=false}
Mode{modeName='記憶模式', showMainMenu=false, showPlayList=false, showMainWindow=true, showControlBar=true, showFavoriteList=true}
  • 類圖如下:
  • 建造者模式針對抽象建造者程式設計,根據具體建造者一步一步構造一個完整的產品,相同的構造過程可以建立完全不同的產品
  • 上述例子中,Builder抽象類的construct()方法定義了具體建造者的方法呼叫順序,這種方式加重了抽象建造者類的職責,在建造者模式中,如果construct()方法較為複雜,待構建產品的組成部分較多,還可以通過指揮者(Director)來更加精細的控制產品的建立過程(參考鏈式呼叫中的Director
  • 建造者模式將客戶端與包含多個組成部分(或部件)的複雜物件的建立過程分離,相較於工廠模式而言,建造者模式返回一個完整的複雜產品,抽象工廠模式返回一系列相關的產品;建造者模式更注重於方法的呼叫順序,工廠模式注重於建立產品

方案的演進(配置檔案)

  • 如上客戶端Test.java呼叫程式碼還具有可改進的地方,可通過配置檔案 + 反射實現在不修改客戶端程式碼的基礎上更換和擴充套件對顯示模式的支援

  • config.properties

builderPattern.className=MemoryModeBuilder
  • PropertiesUtil.java
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

/**
 * @Description Properties工具類
 */
public class PropertiesUtil {

    /**
     * 根據key讀取value
     * @Description: 相對路徑, properties檔案需在classpath目錄下,
     *               比如:config.properties在包com.coisini.util,
     *               路徑就是/com/coisini/util/config.properties
     * @param filePath
     * @param keyWord
     * @return String
     * @throws
     */
     private static String getProperties(String filePath, String keyWord){
        Properties prop = new Properties();
        String value = null;
        try {
            InputStream inputStream = PropertiesUtil.class.getResourceAsStream(filePath);
            prop.load(inputStream);
            value = prop.getProperty(keyWord);
        } catch (IOException e) {
            e.printStackTrace();
        }

        return value;
     }

    /**
     * 根據配置檔案提取類名返回例項物件
     * @param filePath
     * @param keyWord
     * @param packagePath
     * @return
     */
     private static Object getBean(String filePath, String keyWord, String packagePath) {
         try {
             String className = getProperties(filePath, keyWord);
             Class<?> c = Class.forName(packagePath + className);
             return c.newInstance();
         } catch (Exception e) {
             e.printStackTrace();
             return null;
         }

     }

    /**
     * 獲取建造者模式例項物件
     * @return
     */
    public static Object getBuilderPatternBean() {
        return getBean("/com/coisini/design/util/config.properties",
                "builderPattern.className",
                "com.coisini.design.pattern.creational.builder.v2.");
    }
}
  • Test.java
import com.coisini.design.util.PropertiesUtil;

/**
 * @Description 建造者模式測試類(配置檔案反射實現方式)
 */
public class Test {
    public static void main(String[] args) {
        ModeBuilder modeBuilder = (ModeBuilder) PropertiesUtil.getBuilderPatternBean();
        System.out.println(modeBuilder.construct());
    }
}
  • 輸出如下:
Mode{modeName='記憶模式', showMainMenu=false, showPlayList=false, showMainWindow=true, showControlBar=true, showFavoriteList=true}
  • 在該演進例項中,如果需要增加新的顯示模式,可以增加一個新的顯示模式建造者類作為抽象顯示模式建造者的子類,再修改配置檔案即可,原有程式碼無須修改,符合開閉原則

建造者模式(鏈式呼叫)

  • Mode.java
/**
 * @Description 模式產品類
 */
public class Mode {

    private String modeName;
    private Boolean showMainMenu;
    private Boolean showPlayList;
    private Boolean showMainWindow;
    private Boolean showControlBar;
    private Boolean showFavoriteList;

    public Mode(ModeBuilder modeBuilder) {
        this.modeName = modeBuilder.modeName;
        this.showMainMenu = modeBuilder.showMainMenu;
        this.showPlayList = modeBuilder.showPlayList;
        this.showMainWindow = modeBuilder.showMainWindow;
        this.showControlBar = modeBuilder.showControlBar;
        this.showFavoriteList = modeBuilder.showFavoriteList;
    }

    @Override
    public String toString() {
        return "Mode{" +
                "modeName='" + modeName + '\'' +
                ", showMainMenu=" + showMainMenu +
                ", showPlayList=" + showPlayList +
                ", showMainWindow=" + showMainWindow +
                ", showControlBar=" + showControlBar +
                ", showFavoriteList=" + showFavoriteList +
                '}';
    }
}
  • ModeBuilder.java
/**
 * @Description 模式建造者
 */
public class ModeBuilder {

    public String modeName;
    public Boolean showMainMenu;
    public Boolean showPlayList;
    public Boolean showMainWindow;
    public Boolean showControlBar;
    public Boolean showFavoriteList;

    public ModeBuilder buildModeName(String modeName) {
        this.modeName = modeName;
        return this;
    }

    public ModeBuilder buildShowMainMenu(Boolean isShowMainMenu) {
        this.showMainMenu = isShowMainMenu;
        return this;
    }

    public ModeBuilder buildShowPlayList(Boolean isShowPlayList) {
        this.showPlayList = isShowPlayList;
        return this;
    }

    public ModeBuilder buildShowMainWindow(Boolean isShowMainWindow) {
        this.showMainWindow = isShowMainWindow;
        return this;
    }

    public ModeBuilder buildShowControlBar(Boolean isShowControlBar) {
        this.showControlBar = isShowControlBar;
        return this;
    }

    public ModeBuilder buildShowFavoriteList(Boolean isShowFavoriteList) {
        this.showFavoriteList = isShowFavoriteList;
        return this;
    }

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

}
  • Director.java
/**
 * @Description 指揮者
 */
public class Director {
    /**
     * 構建產品物件
     * @return
     */
    public Mode construct() {
        return new ModeBuilder()
                .buildModeName("完整模式")
                .buildShowMainMenu(true)
                .buildShowPlayList(true)
                .buildShowMainWindow(true)
                .buildShowControlBar(true)
                .buildShowFavoriteList(false)
                .build();
    }
}
  • Test.java
/**
 * @Description 建造者模式(鏈式呼叫)測試類
 * @author coisini
 * @date Mar 6, 2022
 * @version 1.0
 */
public class Test {
    public static void main(String[] args) {
        Director director = new Director();
        System.out.println(director.construct());
    }
}
  • 輸出如下:
Mode{modeName='完整模式', showMainMenu=true, showPlayList=true, showMainWindow=true, showControlBar=true, showFavoriteList=false}
  • 類圖如下:
  • 如上例子,還可以通過鏈式呼叫來演進程式碼,通過指揮者(Director)來更加精細的控制產品的建立過程(建立順序),指揮者類Director在建造者模式中扮演重要的作用,簡單的Director類用於指導具體建造者如何構建產品,它按一定次序呼叫BuilderbuildXXX()方法,控制呼叫的先後次序,並向客戶端返回一個完整的產品物件

總結

  • 優點
1、封裝性好,建立和使用分離
2、擴充套件性好,建造類之間獨立、一定程度上解耦
  • 缺點
1、產生多餘的Builder物件
2、產品內部發生變化,建造者都要修改,成本較高
  • 適用場景
1、一個物件有複雜的內部結構(很多屬性)
2、把複雜物件的建立和使用分離

原始碼


- End -
- 個人學習筆記 -
- 僅供參考 -

以上為本篇文章的主要內容,希望大家多提意見,如果喜歡記得點個推薦哦 作者:Maggieq8324 出處:https://www.cnblogs.com/maggieq8324/ 本文版權歸作者和部落格園共有,歡迎轉載,轉載時保留原作者和文章地址即可。