14

【设计模式】第四篇:建造者模式也没那么难

 3 years ago
source link: http://www.cnblogs.com/ideal-20/p/13946868.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

jqEz6v6.png!mobile

一 引言

说明:如果想要直接阅读定义等理论内容,可以直接跳转到第二大点

在生活中有很多场景与我们今天要说的 “建造者模式” 是非常匹配的,打个比方一台计算机是由 CPU、内存、显卡、内存、鼠标、键盘、显示器等等内容组合而成的,我们想要一台电脑,我们不会可能自己去做这些配件,一般都是通过告诉销售公司,然后其派生产技术人员给你做好指定的配件。

先不管,谁买,谁做,谁管理的问题,我们可以分析得到,建造电脑的这个 “过程” 是稳定的 也就是说,不管什么配置的电脑,这些配件都是必须要有的,只是 具体的细节不一样 ,例如你的配置更好,他的差一些

但是,我作为一个买家,我并不想管这些,我只告诉你,我要一台中等配置的电脑,你负责“建造”好给我就行了,这就是建造者模式比较通俗的讲法

下面我们通过这个计算机的例子,循序渐进的看一下:

二 通过例子循序渐进认识建造者模式

首先,不管怎么建,怎么买,一个 Computer 电脑类是必须的,我们随便挑选三个组件来进行演示,CPU、内存、显示器,补充其 get set toString 方法

/**
 * 产品:电脑
 */
public class Computer {
    private String cpu; // CPU
    private String graphicsCard; // 内存
    private String displayScreen; // 显示器

    public String getCpu() {
        return cpu;
    }

    public void setCpu(String cpu) {
        this.cpu = cpu;
    }

    public String getGraphicsCard() {
        return graphicsCard;
    }

    public void setGraphicsCard(String graphicsCard) {
        this.graphicsCard = graphicsCard;
    }

    public String getDisplayScreen() {
        return displayScreen;
    }

    public void setDisplayScreen(String displayScreen) {
        this.displayScreen = displayScreen;
    }
    
    @Override
    public String toString() {
        return "Computer{" +
                "cpu='" + cpu + '\'' +
                ", graphicsCard='" + graphicsCard + '\'' +
                ", displayScreen='" + displayScreen + '\'' +
                '}';
    }
}

(一) 最简单直白的方式(不好)

这种方法基本可以说没什么技术含量了,直接 new + set 就行了

public class Test {
    public static void main(String[] args) {
        Computer computer = new Computer();
        computer.setCpu("英特尔酷睿 i5 处理器");
        computer.setGraphicsCard("4g内存");
        computer.setDisplayScreen("14寸 1080p 60hz显示器");
        System.out.println(computer.toString());
    }
}

打印一下结果:

Computer{cpu='英特尔酷睿 i5 处理器', graphicsCard='4g', displayScreen='14寸 1080p 60hz显示器'}

(二) 客户直接联系生产技术人员

上面这种方式,不用说也知道不合适了,所以技术人员(建造者)他来了!但是不同的技术人员会制作的配件也不一样,例如有的会做 144hz 的显示器,而有的专攻 60hz 的显示器,有高低配置,不同型号之分

为此我们为其抽象出一个 ComputerBuilder 的抽象类

/**
 * 电脑的建造者
 */
public abstract class ComputerBuilder {
    abstract void buildCpu(); // 建造CPU
    abstract void buildGraphicsCard(); // 建造内存
    abstract void buildDisplayScreen(); // 建造显示器

    abstract Computer getComputer(); // 拿到这台电脑
}

下面就来写建造者的具体实现,例如先写一个低配置电脑建造的实现

/**
 * 低配置电脑
 */
public class LowConfigurationComputerBuilder extends ComputerBuilder {

    private Computer computer;

    public LowConfigurationComputerBuilder(){
        computer = new Computer();
    }

    @Override
    void buildCpu() {
        computer.setCpu("英特尔酷睿 i5 处理器");
        System.out.println("buildCpu: 英特尔酷睿 i5 处理器");
    }

    @Override
    void buildGraphicsCard() {
        computer.setGraphicsCard("8g内存");
        System.out.println("buildGraphicsCard: 8g内存");
    }

    @Override
    void buildDisplayScreen() {
        computer.setDisplayScreen("1080p 60hz显示器");
        System.out.println("buildDisplayScreen: 1080p 60hz显示器");
    }

    @Override
    Computer getComputer() {
        return computer;
    }
}

测试一下:

public class Test {
    public static void main(String[] args) {
        // 创建低配置电脑建造者
        LowConfigurationComputerBuilder builder = new LowConfigurationComputerBuilder();
        builder.buildCpu();
        builder.buildGraphicsCard();
        builder.buildDisplayScreen();
        Computer computer = builder.getComputer();
        System.out.println("建造出的电脑: " + computer );
    }
}

执行结果:

buildCpu: 英特尔酷睿 i5 处理器

buildGraphicsCard: 8g内存

buildDisplayScreen: 1080p 60hz显示器

建造出的电脑: Computer{cpu='英特尔酷睿 i5 处理器', graphicsCard='8g内存', displayScreen='1080p 60hz显示器'}

(三) 客户联系销售公司

虽然上面的方法是比第一种强一些,但是客户自己去联系生产技术人员,显然不是很合理,正常的做法,我们都是先去联系销售公司,告诉他们我想要什么配置的电脑就可以了,细节我并不想管

public class SalesCompany {
    public Computer buildComputer(ComputerBuilder builder){
        builder.buildCpu();
        builder.buildGraphicsCard();
        builder.buildDisplayScreen();
        return builder.getComputer();
    }
}

测试代码

public class Test {
    public static void main(String[] args) {
        // 创建低配置电脑建造者
        LowConfigurationComputerBuilder builder = new LowConfigurationComputerBuilder();
        // 创建电脑销售中心
        SalesCompany salesCompany = new SalesCompany();
        // 指定具体的电脑建造者去完成 电脑 这个产品
        Computer computer = salesCompany.buildComputer(builder);
        System.out.println("建造出的电脑: " + computer );
    }
}

现在代码已经比较完善了,也就是我们买家通过联系电脑销售中心,然后销售中心去调用用户想要的配置的建造者,刚才我们创建的是一个“低配置电脑建造者”,如果我们想要换成中等配置的电脑,该怎么做呢?

现在只需要增加一个中等配置电脑建造者,实现Builder抽象类就可以了

/**
 * 低配置电脑
 */
public class MiddleConfigurationComputerBuilder extends ComputerBuilder {

    private Computer computer;

    public MiddleConfigurationComputerBuilder(){
        computer = new Computer();
    }

    @Override
    void buildCpu() {
        computer.setCpu("英特尔酷睿 i7 处理器");
        System.out.println("buildCpu: 英特尔酷睿 i7 处理器");
    }

    @Override
    void buildGraphicsCard() {
        computer.setGraphicsCard("16g内存");
        System.out.println("buildGraphicsCard: 16g内存");
    }

    @Override
    void buildDisplayScreen() {
        computer.setDisplayScreen("2k 144hz显示器");
        System.out.println("buildDisplayScreen: 2k 60hz显示器");
    }

    @Override
    Computer getComputer() {
        return computer;
    }
}

测试一下

public class Test {
    public static void main(String[] args) {
        MiddleConfigurationComputerBuilder builder = new MiddleConfigurationComputerBuilder();
        // 创建电脑销售中心
        SalesCompany salesCompany = new SalesCompany();
        // 指定具体的电脑建造者去完成 电脑 这个产品
        Computer computer = salesCompany.buildComputer(builder);
        System.out.println("建造出的电脑: " + computer );
    }
}

运行结果

buildCpu: 英特尔酷睿 i7 处理器

buildGraphicsCard: 16g内存

buildDisplayScreen: 2k 60hz显示器

建造出的电脑: Computer{cpu='英特尔酷睿 i7 处理器', graphicsCard='16g内存', displayScreen='2k 144hz显示器'}

其实到这里一个建造者模式的实例就写完了,下面我们结合概念,来深入理解一下建造者模式

三 建造者模式

(一) 概念

建造者模式:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示

  • 也就是说, 产品的生成过程 或者说 组成 ,是 不变 的,而每一部分都是可以自行选择的,即其 内部表象是可以变化 的,这也就是所说的变与不变相分离

  • 此种情况下,用户只需要指定建造的类型就可以得到他们,而具体的过程和细节就不需要知道了

(二) 优缺点

首先,此模式封装性好,构建和表示进行了分离,每一个建造者都是相互独立的,利于解耦和扩展,符合 “开闭原则” 同时客户调用时,不需要知道产品细节

但是也正是因为产品生成过程这个不变的部分,限制了它的使用范围,同时如果产品内部日后发生什么改变,则建造者也得同样修改,维护成本不小

(三) 结构

q2i2aqZ.png!mobile

根据上面的结构图,我们分别说明一下其中的四个角色(除 Client 调用者以外)

  • Product(产品角色):多个组件构成的复杂对象,即上述例子中的电脑
  • Builder(抽象建造者):一个包含创建产品各个子部件的抽象方法的接口/抽象类,一般还包含一个返回结果的方法,如上述中的 ComputerBuilder 类
  • ConcreteBuilder(具体建造者):Builder 的具体实现类,如上述中具体的 低配置电脑建造者 和 中等配置电脑建造者
  • Director(指挥者):调用建造者对象中的部件构造与装配方法,以创建一个复杂对象
    • 指挥者中不含具体产品信息
    • 其隔离了客户与对象的生产过程

(四) 适用场景

  • 顺序会对同一方法的结果产生影响,例如建房子,应当先打地基,再架钢筋水泥
  • 同一个对象可以装配不同的部件或者零件,同时结果不同
  • 产品类有复杂的内部结构,且这些产品对象具有共性

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK