建造者模式
建造者模式(Builder Pattern) 也叫作生成器模式。
1建造者模式的定义
建造者模式的英文原话是:
Separate the construction of a complex object from its representation so that the same construction process can create different representations.
意思是:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
在建造者模式中,有如下4个角色。
- 抽象建造者(Builder) 角色:该角色用于规范产品的各个组成部分,并进行抽象,一般是独立于应用程序的逻辑。
- 具体建造者(Concrete Builder)角色:该角色实现重选ing建造者中定义的所有方法,并且返回一个组建好的产品实例。
- 产品(Product)角色:该角色是建造者中的复杂对象,一个系统终会有多于一个的产品类,这类产品并不一定有共同的接口,完全可以使不相关联的。
- 导演者(Director)角色:该角色负责安排已有模块的顺序,然后告诉Builder开始建造。
整体的结构
产品类
Product.java
package com.eric.创建型模式.建造者模式.引例;
/**
* @author Eric
* @ProjectName my_design_23
* @description 产品类
* @CreateTime 2020-11-25 20:58:00
*/
public class Product {
//产品类的业务处理方法
}
抽象建造者
Builder.java
package com.eric.创建型模式.建造者模式.引例;
/**
* @author Eric
* @ProjectName my_design_23
* @description 抽象建造者
* @CreateTime 2020-11-25 20:58:47
*/
public abstract class Builder {
//设置产品的不同部分,以获得不同的产品
public abstract void setPart1();
public abstract void setPart2();
public abstract void setPart3();
//...其他部件
//建造产品
public abstract Product builderProduct();
}
具体建造者
ConcreteBuilder.java
package com.eric.创建型模式.建造者模式.引例;
/**
* @author Eric
* @ProjectName my_design_23
* @description 具体建造者
* @CreateTime 2020-11-25 21:01:32
*/
public class ConcreteBuilder extends Builder {
private Product product = new Product();
//设置产品零件
@Override
public void setPart1() {
//为Product安装部件1
}
@Override
public void setPart2() {
//为Product安装部件2
}
@Override
public void setPart3() {
//为Product安装部件3
}
//。。。其他部件
//建造一个产品
@Override
public Product builderProduct() {
return product;
}
}
导演者类
Director.java
package com.eric.创建型模式.建造者模式.引例;
/**
* @author Eric
* @ProjectName my_design_23
* @description 导演者类
* @CreateTime 2020-11-25 21:04:33
*/
public class Director {
private Builder builder = new ConcreteBuilder();
//构造产品,负责调用各个零件建造方法
public Product build(){
builder.setPart1();
builder.setPart2();
builder.setPart3();
//...其他部件
return builder.builderProduct();
}
}
2建造者模式应用
A.建造者模式的优点
建造者模式的优点有以下几个方面
- 封装性,使用建造者模式可以使客户端不必知道产品内部组成的细节。
- 建造者独立,容易扩展。
- 便于控制细节风险,由于具体的建造者是独立的,因此可以对建造过程逐步细化,而不对其他的模块产生任何影响。
B.建造者模式使用场景
使用建造者模式的典型场景如下
- 相同的方法,不同的执行顺序,产生不同的结果时,可以采用。
- 多个部件或零件,都可以装配到一个对象中,但是产生的运行结果有不同时,则可以使用该模式。
- 产品类非常复杂,或者产品类中的方法调用顺序不同产生了不同的效能,这时候使用建造者模式。
- 在对象的创建过程中会使用到系统的一些其他对象,这些对象在产品对象的创建过程中不易得到时,也可以采用建造者模式封装该对象的创建过程。这种场景只能是一个补偿方法,因为一个对象不容易获得,而在设计阶段没有发现,要通过创建者模式柔化创建过程,本身已经违反设计的最初目标。
建造者模式关注的是零件类型和装配工艺顺序,这是与工厂方法最大的不同之处,虽然同为创建类,但重点不同。
3建造者模式实例
(例1:计算机、例2:模拟快餐店、例3:利用建造者生成网页(html))
例1:使用建造者模式完成计算机的生产。
计算机生产过程中,计算机就是产品(Product),具有不同的型号,如T410和X201是两种型号的计算机,各种类型计算机的具体配置是不同的。
计算机产品类图
步骤1
Computer是计算机的抽象类
Computer.java
package com.eric.创建型模式.建造者模式.例1.product;
/**
* @author Eric
* @ProjectName my_design_23
* @description 电脑抽象类
* @CreateTime 2020-11-26 09:34:03
*/
public abstract class Computer {
private String type;
private String cpu;
private String ram;
private String hardDisk;
private String monitor;
private String os;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
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 getHardDisk() {
return hardDisk;
}
public void setHardDisk(String hardDisk) {
this.hardDisk = hardDisk;
}
public String getMonitor() {
return monitor;
}
public void setMonitor(String monitor) {
this.monitor = monitor;
}
public String getOs() {
return os;
}
public void setOs(String os) {
this.os = os;
}
}
T410和X201都是继承了Computer的实现类
T410.java
package com.eric.创建型模式.建造者模式.例1.product.impl;
import com.eric.创建型模式.建造者模式.例1.product.Computer;
/**
* @author Eric
* @ProjectName my_design_23
* @description T410型号电脑
* @CreateTime 2020-11-26 09:37:25
*/
public class T410 extends Computer {
private String graphicsCard; //显卡
public T410(){
this.setType("ThinkPad T410i");
}
public String getGraphicsCard(){
return graphicsCard;
}
public void setGraphicsCard(String graphicsCard){
this.graphicsCard = graphicsCard;
}
@Override
public String toString() {
return "型号: "+this.getType() + "
CPU: "+this.getCpu()+
"
内存: "+this.getRam()+"
硬盘: "+this.getHardDisk()+
"
显卡: "+this.getGraphicsCard()+"
显示器: "+this.getMonitor()+
"
操作系统: "+this.getOs();
}
}
X201.java
package com.eric.创建型模式.建造者模式.例1.product.impl;
import com.eric.创建型模式.建造者模式.例1.product.Computer;
/**
* @author Eric
* @ProjectName my_design_23
* @description X201型电脑
* @CreateTime 2020-11-26 09:46:12
*/
public class X201 extends Computer {
public X201(){this.setType("ThinkPad X201i");}
@Override
public String toString() {
return "型号: "+this.getType() + "
CPU: "+this.getCpu()+
"
内存: "+this.getRam()+"
硬盘: "+this.getHardDisk()+
"
显示器: "+this.getMonitor()+ "
操作系统: "+this.getOs();
}
}
其中T410和X201两种型号的计算机内部构造不同,例如,X201中没有独立显卡,所以X201中没有graphicsCard属性。
在计算机产品类图的基础上增加一个ComputerBuilder接口,以及两个实现类,以便对计算机进行生产。
增加建造者后的类图
步骤2
计算机建造者的接口
ComputerBuilder.java
package com.eric.创建型模式.建造者模式.例1.builder;
import com.eric.创建型模式.建造者模式.例1.product.Computer;
/**
* @author Eric
* @ProjectName my_design_23
* @description 计算机建造者接口
* @CreateTime 2020-11-26 09:49:42
*/
//计算机Builder
public interface ComputerBuilder {
void buildCpu();//建造cpu
void buildRam();//建造内存
void buildHardDisk();//建造硬盘
void buildGraphicCard();//建造显卡
void buildMonitor();//建造显示器
void buildOs();//建造操作系统
Computer getResult();//得到建造好的电脑
}
计算机的具体建造者T410Builder和X201Builder实现了ComputerBuilder接口,分别完成两种型号计算机的建造。
T410Builder.java
package com.eric.创建型模式.建造者模式.例1.builder.impl;
import com.eric.创建型模式.建造者模式.例1.builder.ComputerBuilder;
import com.eric.创建型模式.建造者模式.例1.product.Computer;
import com.eric.创建型模式.建造者模式.例1.product.impl.T410;
/**
* @author Eric
* @ProjectName my_design_23
* @description T410型电脑的建造者
* @CreateTime 2020-11-26 09:54:39
*/
public class T410Builder implements ComputerBuilder {
private T410 computer = new T410();
@Override
public void buildCpu() {
computer.setCpu("i5-450");
}
@Override
public void buildRam() {
computer.setRam("4GB 1333MHz");
}
@Override
public void buildHardDisk() {
computer.setHardDisk("500GB 7200转");
}
@Override
public void buildGraphicCard() {
computer.setGraphicsCard("Nvidia NVS 3100M");
}
@Override
public void buildMonitor() {
computer.setMonitor("14英寸 1280*800");
}
@Override
public void buildOs() {
computer.setOs("Windows 7 旗舰版");
}
@Override
public T410 getResult() {
return computer;
}
}
X201Builder.java
package com.eric.创建型模式.建造者模式.例1.builder.impl;
import com.eric.创建型模式.建造者模式.例1.builder.ComputerBuilder;
import com.eric.创建型模式.建造者模式.例1.product.impl.X201;
/**
* @author Eric
* @ProjectName my_design_23
* @description X201型电脑的建造者
* @CreateTime 2020-11-26 10:01:43
*/
public class X201Builder implements ComputerBuilder {
private X201 computer = new X201();
@Override
public void buildCpu() {
computer.setCpu("i3-350");
}
@Override
public void buildRam() {
computer.setRam("2GB 1333MHz");
}
@Override
public void buildHardDisk() {
computer.setHardDisk("250GB 5400转");
}
@Override
public void buildGraphicCard() {
//无显卡
}
@Override
public void buildMonitor() {
computer.setMonitor("12英寸 1280*800");
}
@Override
public void buildOs() {
computer.setOs("Windows 7 Home版");
}
@Override
public X201 getResult() {
return computer;
}
}
增加建造者之后,再增加导演者ComputerDirector类,对已有的模块进行封装。
增加导演类后的类图
步骤3
建造计算机的导演类
ComputerDirector.java
package com.eric.创建型模式.建造者模式.例1.director;
import com.eric.创建型模式.建造者模式.例1.builder.ComputerBuilder;
import com.eric.创建型模式.建造者模式.例1.builder.impl.T410Builder;
import com.eric.创建型模式.建造者模式.例1.builder.impl.X201Builder;
import com.eric.创建型模式.建造者模式.例1.product.impl.T410;
import com.eric.创建型模式.建造者模式.例1.product.impl.X201;
/**
* @author Eric
* @ProjectName my_design_23
* @description 建造计算机的导演类
* @CreateTime 2020-11-26 10:05:34
*/
public class ComputerDirector {
ComputerBuilder builder;
//构造T410型计算机
public T410 constructT410(){
builder = new T410Builder();
builder.buildCpu();
builder.buildRam();
builder.buildHardDisk();
builder.buildGraphicCard();
builder.buildMonitor();
builder.buildOs();
return (T410) builder.getResult();
}
//构造X201型计算机
public X201 constructX201(){
builder = new X201Builder();
builder.buildCpu();
builder.buildRam();
builder.buildHardDisk();
//无显卡
builder.buildMonitor();
builder.buildOs();
return (X201) builder.getResult();
}
}
导演类实现了对建造两种型号计算机的封装
步骤4
测试类
ComputerTest.java
package com.eric.创建型模式.建造者模式.例1.test;
import com.eric.创建型模式.建造者模式.例1.director.ComputerDirector;
import com.eric.创建型模式.建造者模式.例1.product.impl.T410;
import com.eric.创建型模式.建造者模式.例1.product.impl.X201;
/**
* @author Eric
* @ProjectName my_design_23
* @description 测试类
* @CreateTime 2020-11-26 10:10:10
*/
public class ComputerTest {
public static void main(String[] args) {
//得到导演类
ComputerDirector director = new ComputerDirector();
//创建并拿到T410型电脑
T410 t410 = director.constructT410();
System.out.println(t410);
//无情分割线
System.out.println("----------------------------");
//创建并拿到X201型电脑
X201 x201 = director.constructX201();
System.out.println(x201);
}
}
测试结果
完成计算机的建造。
例2:使用建造者模式完成一个快餐店的商业案例。
首先我们先假设一个快餐店场景。其中,一个典型的套餐可以是汉堡(Burger)和一杯冷饮(Cold drink)。汉堡(Burger)可以是素食汉堡(Veg Burger)或鸡肉汉堡(Chicken Burger),他们是包在纸盒中的。 冷饮(Cold Drink)可以是可口可乐(Coke)或百事可乐(Pepsi),它们是装在瓶子里的。
我们将创建一个表示事物条目(比如汉堡和冷饮)的Item接口和实现Item接口的实体类,以及一个表示食物包装的Packing接口和实现Packing接口的实体类,汉堡是包在纸盒中,冷饮是装在瓶子中。
然后创建Meal类,带有Item的ArrayList和一个通过结合Item来创建不同类型的Meal对象的MealBuilder类。
BuilderPatternDemo类使用MealBuilder来创建一个Meal。
创建Packing与Item接口
Packing.java 包装
package com.eric.创建型模式.建造者模式.例2.pack;
/**
* @author Eric
* @ProjectName my_design_23
* @description 食物包装的接口
* @CreateTime 2020-11-26 10:55:06
*/
public interface Packing {
public String pack();
}
Item.java 快餐购物项
package com.eric.创建型模式.建造者模式.例2.item;
import com.eric.创建型模式.建造者模式.例2.pack.Packing;
/**
* @author Eric
* @ProjectName my_design_23
* @description 食物清单的接口
* @CreateTime 2020-11-26 10:53:46
*/
public interface Item {
public String name();
public Packing packing();
public float price();
}
步骤2
创建Packing的实现类Bottle和Wrapper
Bottle.java 瓶子
package com.eric.创建型模式.建造者模式.例2.pack.impl;
import com.eric.创建型模式.建造者模式.例2.pack.Packing;
/**
* @author Eric
* @ProjectName my_design_23
* @description 瓶子类
* @CreateTime 2020-11-26 10:57:31
*/
public class Bottle implements Packing {
@Override
public String pack() {
return "Bottle";
}
}
Wrapper.java 包装纸
package com.eric.创建型模式.建造者模式.例2.pack.impl;
import com.eric.创建型模式.建造者模式.例2.pack.Packing;
/**
* @author Eric
* @ProjectName my_design_23
* @description 包装纸类
* @CreateTime 2020-11-26 10:56:34
*/
public class Wrapper implements Packing {
@Override
public String pack() {
return "Wrapper";
}
}
步骤3
创建两个实现Item接口的抽象实现类。--Burger和ColdDrink
Burger.java 汉堡包
package com.eric.创建型模式.建造者模式.例2.item.Burger;
import com.eric.创建型模式.建造者模式.例2.item.Item;
import com.eric.创建型模式.建造者模式.例2.pack.Packing;
import com.eric.创建型模式.建造者模式.例2.pack.impl.Wrapper;
/**
* @author Eric
* @ProjectName my_design_23
* @description 汉堡的抽象类
* @CreateTime 2020-11-26 10:59:15
*/
public abstract class Burger implements Item {
//汉堡的包装
@Override
public Packing packing() {
return new Wrapper();
}
//汉堡的价格
@Override
public abstract float price();
}
ColdDrink.java 冷饮
package com.eric.创建型模式.建造者模式.例2.item.ColdDrink;
import com.eric.创建型模式.建造者模式.例2.item.Item;
import com.eric.创建型模式.建造者模式.例2.pack.Packing;
import com.eric.创建型模式.建造者模式.例2.pack.impl.Bottle;
/**
* @author Eric
* @ProjectName my_design_23
* @description 冷饮抽象类
* @CreateTime 2020-11-26 11:02:31
*/
public abstract class ColdDrink implements Item {
@Override
public Packing packing() {
return new Bottle();
}
@Override
public abstract float price();
}
步骤4
创建继承Burger和ColdDrink的实现类
ChickenBurger.java 鸡肉堡
package com.eric.创建型模式.建造者模式.例2.item.Burger.impl;
import com.eric.创建型模式.建造者模式.例2.item.Burger.Burger;
/**
* @author Eric
* @ProjectName my_design_23
* @description 鸡肉堡类
* @CreateTime 2020-11-26 11:05:18
*/
public class ChickenBurger extends Burger {
@Override
public String name() {
return "Chicken Burger";
}
@Override
public float price() {
return 13.5f;
}
}
VegBurger.java 素汉堡
package com.eric.创建型模式.建造者模式.例2.item.Burger.impl;
import com.eric.创建型模式.建造者模式.例2.item.Burger.Burger;
/**
* @author Eric
* @ProjectName my_design_23
* @description 素汉堡类
* @CreateTime 2020-11-26 11:04:06
*/
public class VegBurger extends Burger {
@Override
public String name() {
return "Veg Burger";
}
@Override
public float price() {
return 5.2f;
}
}
Coke.java 可口可乐
package com.eric.创建型模式.建造者模式.例2.item.ColdDrink.impl;
import com.eric.创建型模式.建造者模式.例2.item.ColdDrink.ColdDrink;
/**
* @author Eric
* @ProjectName my_design_23
* @description 可口可乐类
* @CreateTime 2020-11-26 11:06:36
*/
public class Coke extends ColdDrink {
@Override
public String name() {
return "Coke";
}
@Override
public float price() {
return 5.0f;
}
}
Pepsi.java 百事可乐
package com.eric.创建型模式.建造者模式.例2.item.ColdDrink.impl;
import com.eric.创建型模式.建造者模式.例2.item.ColdDrink.ColdDrink;
/**
* @author Eric
* @ProjectName my_design_23
* @description 百事可乐类
* @CreateTime 2020-11-26 11:07:30
*/
public class Pepsi extends ColdDrink {
@Override
public String name() {
return "Pepsi";
}
@Override
public float price() {
return 2.0f;
}
}
步骤5
创建Meal类集中管理Item
Meal.java 购物清单
package com.eric.创建型模式.建造者模式.例2.meal;
import com.eric.创建型模式.建造者模式.例2.item.Item;
import java.util.ArrayList;
import java.util.List;
/**
* @author Eric
* @ProjectName my_design_23
* @description 购物清单
* @CreateTime 2020-11-26 11:09:05
*/
public class Meal {
private List<Item> items = new ArrayList<Item>();
//添加物品
public void addItem(Item item){
items.add(item);
}
//计算金额
public float getCost(){
float cost = 0.0f;
for (Item item : items) {
cost += item.price();
}
return cost;
}
//列出清单
public void showItems(){
for (Item item : items) {
System.out.print("Item:"+item.name());
System.out.print(" -- Packing:"+item.packing().pack());
System.out.println(" -- Price:"+item.price());
}
}
}
步骤6
创建Meal的建造者实现快餐店的套餐
MealBuilder.java 结算
package com.eric.创建型模式.建造者模式.例2.meal;
import com.eric.创建型模式.建造者模式.例2.item.Burger.impl.ChickenBurger;
import com.eric.创建型模式.建造者模式.例2.item.Burger.impl.VegBurger;
import com.eric.创建型模式.建造者模式.例2.item.ColdDrink.impl.Coke;
import com.eric.创建型模式.建造者模式.例2.item.ColdDrink.impl.Pepsi;
import com.eric.创建型模式.建造者模式.例2.item.Item;
/**
* @author Eric
* @ProjectName my_design_23
* @description 实际的Builder类负责创建Meal对象
* @CreateTime 2020-11-26 11:15:46
*/
public class MealBuilder {
public Meal prepareVegMeal(){
Meal meal = new Meal();
meal.addItem(new VegBurger());
meal.addItem(new Coke());
return meal;
}
public Meal prepareNonVegMeal(){
Meal meal = new Meal();
meal.addItem(new ChickenBurger());
meal.addItem(new Pepsi());
return meal;
}
}
步骤7
测试类
BuilderPatternDemo.java
package com.eric.创建型模式.建造者模式.例2;
import com.eric.创建型模式.建造者模式.例2.meal.Meal;
import com.eric.创建型模式.建造者模式.例2.meal.MealBuilder;
/**
* @author Eric
* @ProjectName my_design_23
* @description 使用MealBuilder来演示建造者模式
* @CreateTime 2020-11-26 11:19:29
*/
public class BuilderPatternDemo {
public static void main(String[] args) {
MealBuilder mealBuilder = new MealBuilder();
Meal vegMeal = mealBuilder.prepareVegMeal();
System.out.println("Veg Meal");
vegMeal.showItems();
System.out.println("Total Cost:"+vegMeal.getCost());
//无情分割线
System.out.println("---------------------------------------");
Meal nonVegMeal = mealBuilder.prepareNonVegMeal();
System.out.println("non-Veg Meal");
nonVegMeal.showItems();
System.out.println("Total Cost:"+nonVegMeal.getCost());
}
}
例3:建造者模式的高级应用案例----生成网页
我们可以利用建造者模式,提取出文本与html页面的共性,并使用建造者模式分别生成。
步骤1
创建建造者的抽象类:
Builder.java
package com.eric.创建型模式.建造者模式.例3;
/**
* @author Eric
* @ProjectName my_design_23
* @description 建造抽象类
* @CreateTime 2020-11-26 12:26:42
*/
public abstract class Builder {
//设置标题
public abstract void makeTitle(String title);
//设置字段
public abstract void makeString(String str);
//设置条目项
public abstract void makeItems(String[] items);
//结束操作
public abstract void close();
//返回结果
public abstract String getResult();
}
步骤2
创建继承Builder的实现类HtmlBuilder和TextBuilder
HtmlBuilder.java
package com.eric.创建型模式.建造者模式.例3;
import java.io.FileWriter;
import java.io.PrintWriter;
/**
* @author Eric
* @ProjectName my_design_23
* @description 网页的建造类
* @CreateTime 2020-11-26 12:28:31
*/
public class HtmlBuilder extends Builder{
//文件全名
private String filename;
//写入到文件
private PrintWriter pw;
@Override
public void makeString(String str) {
pw.println("<p>"+str+"</p>");
}
@Override
public void makeTitle(String title) {
filename = "D:\"+title+".html";
try {
pw = new PrintWriter(new FileWriter(filename));
}catch (Exception e){
e.printStackTrace();
}
pw.println("<html><head><title>"+title+"</title></head><body>");
pw.println("<h1>"+title+"</h1>");
}
@Override
public void makeItems(String[] items) {
pw.println("<ul>");
for (String item : items) {
pw.println("<li>"+item+"</li>");
}
pw.println("</ul>");
}
@Override
public void close() {
pw.println("</body></html>");
pw.close();
}
//返回文件名
public String getResult()
{
return filename;
}
}
TextBuilder.java
package com.eric.创建型模式.建造者模式.例3;
/**
* @author Eric
* @ProjectName my_design_23
* @description 文本的建造者
* @CreateTime 2020-11-26 12:36:41
*/
public class TextBuilder extends Builder {
StringBuffer sb = new StringBuffer();
@Override
public void makeString(String str) {
sb.append("~").append(str).append("
");
}
@Override
public void makeTitle(String title) {
sb.append("====================
");
sb.append(" [").append(title).append("]").append("
");
}
@Override
public void makeItems(String[] items) {
for (String item : items) {
sb.append(" -").append(item).append("
");
}
}
@Override
public void close() {
sb.append("====================");
}
public String getResult(){
return sb.toString();
}
}
步骤3
创建监管者(导演类)
Director.java
package com.eric.创建型模式.建造者模式.例3;
/**
* @author Eric
* @ProjectName my_design_23
* @description 监工类
* @CreateTime 2020-11-26 12:42:06
*/
public class Director {
private Builder builder;
public Director(Builder builder){
this.builder=builder;
}
public void conStruct(String[] itemsA,String[] itemsB){
String[] items1 = itemsA;
String[] items2= itemsB;
builder.makeTitle("日记一则");
builder.makeString("周一");
builder.makeItems(items1);
builder.makeString("周二");
builder.makeItems(items2);
builder.close();
}
}
步骤4
测试类
testBuilder.java
package com.eric.创建型模式.建造者模式.例3;
/**
* @author Eric
* @ProjectName my_design_23
* @description 测试类
* @CreateTime 2020-11-26 12:47:53
*/
public class testBuilder {
public static void main(String[] args) {
//String choice = "plain";
String choice = "html";
Builder builder = null;
Director director = null;
String [] items1=new String[]{"吃饭","睡觉","打豆豆"};
String [] items2=new String[]{"吃饭","打豆豆","睡觉"};
switch(choice.equals("html")?1:0){
case 0:
builder = new TextBuilder();
director= new Director(builder);
director.conStruct(items1,items2);
System.out.println(builder.getResult());
break;
case 1:
builder = new HtmlBuilder();
director= new Director(builder);
director.conStruct(items1,items2);
System.out.println(builder.getResult());
break;
}
}
}
测试结果:
1.html(控制台返回结果、生成的html页面、页面展示)
控制台返回结果
生成的html页面