设计模式 - 建造者模式

实例

软件界面多种显示模式

假设一个视频播放软件提供多种界面显示模式的应用场景,该软件支持完整模式、精简模式、记忆模式等;在不同的显示模式下主界面的组成元素有所差异,如:在完整模式下显示主菜单、播放列表、主窗口、控制条;在精简模式下只显示主窗口、控制条;在记忆模式下显示主窗口、控制条、收藏列表

  • 软件结构示意图如下:

在这里插入图片描述

可以看出,显示模式是一个复杂对象,包含主菜单、播放列表、主窗口、控制条等多个组成部分,不同的显示模式其组成部分有所差异,如何将这些组成部分组装成一个完整的显示模式返回给用户,建造者模式为解决此类问题而诞生


建造者模式

概念

  • 建造者模式(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、把复杂对象的创建和使用分离
  • Java中应用建造者模式的案例
StringBuilder、StringBuffer、(Spring)BeanDefinitionBuilder、
(Mybatis)SqlSessionFactoryBuilder、(Mybatis)XmlConfigBuilder

源码


- End -
- 个人学习笔记 -
- 仅供参考 -

posted @ 2022-03-07 10:50  Maggieq8324  阅读(42)  评论(0编辑  收藏  举报