模式介绍和动机

  1. 建造者模式也称生成器模式,它是最复杂的创建型模式将一个复杂对象的构建(建造者->造产品各个组成部分) 与 它的表示(指挥者->组成顺序)分离使得同样的构造过程可以创建不同的表示
  2. 建造者模式关注该复杂对象是如何一步一步创建而成的,而对于客户端而言,无需知道复杂对象的内部组成部分与装配方式,只需要知道建造者的类型即可
  3. 举个例子:
    3.1 建造者模式可以用于描述炸鸡店如何创建套餐:套餐是一个复杂对象,它一般包含主食(如汉堡,鸡肉卷)和饮料(如果汁,可乐)等组成部分,不同套餐有不同的组成部分,而炸鸡店的服务员会根据顾客的要求,一步一步装配这些组成部分,构造一份完美的套餐,然后返回给顾客。
    3.2 炸鸡店生产汉堡,鸡肉卷,果汁,可乐等,经过服务员的装配,形成多样的套餐,返回给顾客

模式结构的角色

1. 抽象建造者(Builder)

1.1 作用: 抽象建造者为 创建产品Product对象的各个部件指定抽象接口
1.2 内容:该接口 / 抽象类中一般声明两类方法
①、buildPartX():用于创建复杂对象的各个部件
②、getResult():用于返回复杂对象
1.3 例子:该类中包含建造主食和饮料的方法 以及返回套餐的方法

/**
 * @author 王胖子
 * @version 1.0
 */
public abstract class MealBuilder {
    protected Meal meal = new Meal();

    //建造产品组成部分
    public abstract void buildFood();

    public abstract void buildDrink();

    //返回建造的产品
    public Meal getMeal() {
        return meal;
    }
}

2. 具体建造者(ConcreteBuilder)

2.1 作用和内容:实现Builder接口,实现各个部件的构造和装配方法,定义并明确它所创建的复杂对象,也可以提供一个方法返回创建好的复杂对象
2.2 例子:在具体建造类中,明确建造哪种套餐,如具体建造类A中创建套餐A里的食品,具体建造类B中创建套餐B里的食品

/**
 * @author 王胖子
 * @version 1.0
 */
public class SubMealBuilderA extends MealBuilder {
    //实现父类的建造方法,用产品属性的set方法为属性初始化
    @Override
    public void buildFood() {
        meal.setFood("一个鸡腿堡");
    }

    @Override
    public void buildDrink() {
        meal.setDrink("一杯可乐");
    }
}

3. 产品(Product)

3.1 内容:被构建的复杂对象,包含多个组成部件
3.2 例子:
在这里插入图片描述

4. 指挥者(Director)

4.1 作用:负责安排复杂对象的建造次序
4.2 内容:指挥者和抽象建造者之间存在关联关系,在指挥者类的construct()方法中调用建造者对象的部件构造和装配方法,完成对复杂对象的建造
4.3 例子:在餐厅中,厨师做菜,服务员按要求组装这些做好的菜,再把这些菜端给顾客

/**
 * @author 王胖子
 * @version 1.0
 */
//指挥者类-->服务员类(按次序组装套餐)
//指挥者类组装,建造者类制作
public class Waiter {
    //需要使用具体建造者对象,所以使用关联关系
    //所以使用关联关系,在Waiter类中声明这个对象
    private MealBuilder mb;

    //set方法来 初始化具体建造者对象
    public void setMealBuilder(MealBuilder mb) {
        this.mb = mb;
    }

    //安排复杂对象的建造次序,并把建造好的复杂对象返回给客户
    //先建造主食,再建造饮料,把组装好的套餐返回给客户
    //调用体建造者中的各个组成部分的建造方法来建造
    public Meal construct() {
        mb.buildFood();
        mb.buildDrink();
        return mb.getMeal();
    }
}

5. 客户类(Client)

内容:
5.1 具体建造者 --> 做套餐A的厨师
5.2 指挥者 --> 服务员
5.3 为指挥者类中的具体建造者对象赋值 --> 服务员通知做套餐A的厨师
5.4 服务员把组装好的套餐A打包在一起 端给顾客

/**
 * @author 王胖子
 * @version 1.0
 */
public class Client {
    public static void main(String[] args) {
        //具体建造者 --> 做套餐A的厨师
        MealBuilder mb = new SubMealBuilderA();
        //指挥者    --> 服务员
        Waiter waiter = new Waiter();
        //为指挥者类中的具体建造者对象赋值 --> 服务员通知做套餐A的厨师
        waiter.setMealBuilder(mb);
        //服务员把组装好的套餐A打包在一起 端给顾客
        Meal meal = waiter.construct();

        //检查套餐组成
        System.out.println("套餐组成");
        System.out.println(meal.getFood());
        System.out.println(meal.getDrink());
    }
}

输出
在这里插入图片描述

建造者模式案例

案例背景

计算机组装工厂可以将CPU,内存,硬盘,主机,显示器等硬件设备组装在一起构成一台完整的计算机,且构成的计算机可以是笔记本电脑,也可以是台式机,还可以是不提供显示器的服务器主机。对于用户来言,无需关心计算机的组成设备和组装过程,工厂返回给用户的是完整的计算机对象。所以我们可以使用建造者模式来实现计算机的组成过程。

案例分析

同一个建造过程,不同的表示,用户不关系组装过程,只看最终返回的完整的复杂对象,所以可用建造者模式

实现步骤

  1. 抽象建造者类ComputerBuilder,
    具体建造者类Notebook,Desktop和Server,
    复合产品类Computer,

其中台式机和服务器主机使用相同的CPU,内存,硬盘和主机,但是服务器不包含显示器,而笔记本使用自己独自的一套硬件设备。

此外还需要指挥者类ComputerAssembleDirector,

此类中应有将硬件设备组合在一起的 建造方法assemble() 并返回用户需要的具体计算机

  1. 实现上述类的具体代码以及
    用户类Client、
    辅助类XMLUtil,以实现通过XML文件来制造不同的计算机

  2. 更改XML中的属性,观察用户类是否能够获取到不同的计算机以及这些计算机的组装是否符合要求

代码实现(按编写顺序)

文件结构或类图

在这里插入图片描述
在这里插入图片描述

1. 产品类

/**
 * @author 王胖子
 * @version 1.0
 */
public class Computer {
    private String cpu;
    private String ram;//内存
    private String disk;//硬盘
    private String host;//主机
    private String monitor;//显示器

    public String getMonitor() {
    	//如果为空,则抛出异常,交给调用者处理
        if (monitor == null) {
            throw new RuntimeException("服务器没有显示器");
        }
        return monitor;
    }

    public void setMonitor(String monitor) {
        this.monitor = monitor;
    }

    public String getCpu() {
        return cpu;
    }

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

    public String getRam() {
        return ram;
    }

    public void setRam(String ram) {
        this.ram = ram;
    }

    public String getDisk() {
        return disk;
    }

    public void setDisk(String disk) {
        this.disk = disk;
    }

    public String getHost() {
        return host;
    }

    public void setHost(String host) {
        this.host = host;
    }
}

2. 抽象建造者类

/**
 * @author 王胖子
 * @version 1.0
 */
public abstract class ComputerBuilder {
    //不允许不同包访问该属性
    protected Computer computer = new Computer();
    public abstract void buildCpu();
    public abstract void buildRam();
    public abstract void buildDisk();
    public abstract void buildHost();
    public abstract void buildMonitor();
    public Computer getComputer(){
        return computer;
    }
}

3. 具体建造者类(3个)

/**
 * @author 王胖子
 * @version 1.0
 */
public class Notebook extends ComputerBuilder {
    @Override
    public void buildCpu() {
        computer.setCpu("cpuB");
    }

    @Override
    public void buildRam() {
        computer.setRam("内存B");
    }

    @Override
    public void buildDisk() {
        computer.setDisk("硬盘B");
    }

    @Override
    public void buildHost() {
        computer.setHost("主机B");
    }

    @Override
    public void buildMonitor() {
        computer.setMonitor("显示器(笔记本)");
    }
}

/**
 * @author 王胖子
 * @version 1.0
 */
public class Desktop extends ComputerBuilder {
    @Override
    public void buildCpu() {
        computer.setCpu("cpuA");
    }

    @Override
    public void buildRam() {
        computer.setRam("内存A");
    }

    @Override
    public void buildDisk() {
        computer.setDisk("硬盘A");
    }

    @Override
    public void buildHost() {
        computer.setHost("主机A");
    }

    @Override
    public void buildMonitor() {
        computer.setMonitor("显示器(台式的)");
    }
}

/**
 * @author 王胖子
 * @version 1.0
 */
public class Server extends ComputerBuilder {
    @Override
    public void buildCpu() {
        computer.setCpu("cpuA");
    }

    @Override
    public void buildRam() {
        computer.setRam("内存A");
    }

    @Override
    public void buildDisk() {
        computer.setDisk("硬盘A");
    }

    @Override
    public void buildHost() {
        computer.setHost("主机A");
    }

    @Override
    public void buildMonitor() {
        //服务器没有主机,所以空方法
    }
}

4. 指挥者类

/**
 * @author 王胖子
 * @version 1.0
 */
public class ComputerAssembleDirector {
    ComputerBuilder cb;

    public void setCb(ComputerBuilder cb) {
        this.cb = cb;
    }

    public Computer assemble() {
        cb.buildCpu();
        cb.buildRam();
        cb.buildDisk();
        cb.buildHost();
        cb.buildMonitor();
        return cb.getComputer();
    }
}

5. 客户类

import com.sun.xml.internal.ws.util.xml.XmlUtil;

/**
 * @author 王胖子
 * @version 1.0
 */
public class Client {
    public static void main(String[] args) {
        ComputerBuilder cb = (ComputerBuilder) XMLUtil.getBean();
        ComputerAssembleDirector director = new ComputerAssembleDirector();
        director.setCb(cb);
        Computer computer = director.assemble();
        //检验
        System.out.println("计算机的组成");
        System.out.println(computer.getCpu());
        System.out.println(computer.getRam());
        System.out.println(computer.getDisk());
        System.out.println(computer.getHost());
        try {
            System.out.println(computer.getMonitor());
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }
}

6. 配置文件(config.xml)

<?xml version="1.0"?>
<config>
    <className>Notebook</className>
</config>

7. 读取配置文件,创对象(XMLUtil.java)

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.File;

/**
 * @author 王胖子
 * @version 1.0
 */
public class XMLUtil {
    //该方法用于从xml配置文件中提取具体类类名,并返回一个实例对象
    public static Object getBean() {
        try {
            //创建DOM对象
            DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = dFactory.newDocumentBuilder();
            Document doc;
            //这个地址从项目根目录开始读取
            doc = builder.parse(new File("src//config.xml"));

            //获取包含类名的文本节点
            NodeList nl = doc.getElementsByTagName("className");
            Node classNode = nl.item(0).getFirstChild();
            String cName = classNode.getNodeValue();

            //通过类名生成实例对象并返回
            Class c = Class.forName(cName);
            Object obj = c.newInstance();
            return obj;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}

输出结果

在这里插入图片描述
在这里插入图片描述

模式适用环境

建造者模式主要运用于:

  1. 需要生成的产品对象有复杂的内部结构,这些产品对象通常包含多个成员变量
  2. 需要生成的产品对象的属性相互依赖,需要指定其生成顺序
  3. 对象的创建过程独立于创建该对象的类。在建造者模式中通过引入了指挥者类,将创建过程封装在指挥者类中,而不在建造者类和客户类中
  4. 隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐