设计模式-建造者模式
一、建造者模式概念
建造者模式是最复杂的创建型模式,它将客户端与包含多个组成部分的复杂对象的创建分离,客户端无需知道复杂对象的内部组成部分与装配模式,只需要知道建造者的类型即可。
建造者模式关注 该复杂对象 是如何一步步创建而成的。
二、建造者模式的角色
- 产品类:被构建的复杂对象,有好多属性作为组成部件,具体建造者创建该产品的内部组成,表示并定义它的装配过程。
- 抽象建造者:创建一个产品对象 供子类设值(装配),一般有两类方法,一类是buildPartX(),子类实现对产品属性的设值,一类是getResult(),用于返回复杂对象。
- 具体建造者:实现抽象建造者中的方法,其中各个buildPartX()实现后 即确定了复杂产品的各属性值。也可以提供一个方法返回创建好的复杂产品对象。
- 指挥者:又称导演类,负责安排复杂对象的建造次序,指挥者和抽象建造者关联,在construct()建造方法中调用建造者对象的装配方法,完成复杂产品的建造。
理解:每一个具体建造者类相当于一个具体产品,通过设值装配复杂对象。指挥者里创建抽象建造者对象,当作具体建造者对象使用(设值),然后返回。 建造者里创建产品对象,设值后返回。
三、举例
KFC如何创建套餐:套餐是一个复杂对象,包含主食(如汉堡、鸡肉卷)和饮料(如果汁、可乐)等组成部分,不同套餐有不同的组成部分,然后KFC服务员根据客户需求一步一步装配套餐,构造出一份完整的套餐,返回给客户。套餐A=汉堡+可乐。套餐B=鸡肉卷+果汁。
产品类:Meal
package build; public class Meal {//产品类,套餐类 private String food; private String drink; public String getFood() { return food; } public void setFood(String food) { this.food = food; } public String getDrink() { return drink; } public void setDrink(String drink) { this.drink = drink; } }
抽象建造者MealBuilder 和 具体建造者MealBuilderA、MealBuilderB
package build; public abstract class MealBuilder {//抽象套餐,建造者类 protected Meal meal=new Meal();//这个类对象主要是给子类设值的 public abstract void buildFood(); public abstract void buildDrink(); public Meal getMeal() { return meal; } } class MealBuilderA extends MealBuilder{//套餐A 具体建造者 public void buildFood() { meal.setFood("汉堡"); } public void buildDrink() { meal.setDrink("可乐"); } } class MealBuilderB extends MealBuilder{//套餐B 具体建造者 public void buildFood() { meal.setFood("鸡肉卷"); } public void buildDrink() { meal.setDrink("果汁"); } }
指挥者:KFCWaiter
package build; public class KFCWaiter {//指挥者类 private MealBuilder mb; public void setMealBuilder(MealBuilder mb) { this.mb=mb; } public Meal construct() { mb.buildFood(); mb.buildDrink(); return mb.getMeal(); } }
模拟DAO:XMLUtil.java和config.xml
package build; import javax.xml.parsers.*; import org.w3c.dom.*; import java.io.*; import java.net.URL; public class XMLUtil { public static Object getBean() { try { //创建DOM文档对象 DocumentBuilderFactory dFactory=DocumentBuilderFactory.newInstance();//文档制造者工厂创建了一个 文档制造者工厂对象 DocumentBuilder builder=dFactory.newDocumentBuilder();//文档制造者类 通过 文档制造者工厂 创造一个 文档制造者对象 Document doc;//文档制造者 创建 文档 doc=builder.parse(new File("src/build/config.xml"));//解析xml文件 //获取包含类名的文本节点 NodeList nl=doc.getElementsByTagName("className");//文本节点列表里有很多被className标签夹着的内容 Node classNode=nl.item(0).getFirstChild(); //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; } } }
<?xml version="1.0"?> <config> <className>build.MealBuilderA</className> </config>
客户端:Client
package build; public class Client { public static void main(String[] args) { //动态确定套餐种类 MealBuilder mb=(MealBuilder)XMLUtil.getBean(); //服务员是指挥者 KFCWaiter waiter=new KFCWaiter(); //服务员准备套餐,这个套餐mb是从XML文件里搞来的一个具体建造者类 waiter.setMealBuilder(mb); //客户获得套餐 Meal meal=waiter.construct(); System.out.println("套餐组成:"); System.out.println(meal.getFood()); System.out.println(meal.getDrink()); } }
四、课后习题
某游戏软件中任务角色包括多种类型,不同类型的任务角色,其性别、脸型、服装、发型等外部特性有所差异,使用建造者模式创建任务角色对象,要求绘制类图并编程实现。
妲己:女,锥子脸,咖啡女仆,短发
甄姬:女,瓜子脸,花好人间,斜刘海
宫本:男,菱形脸,鬼剑武藏,冲天辫
产品类
package homework; public class Role {//产品类 角色Role private String name; private String sex; private String face; private String clothes; private String hair; public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getFace() { return face; } public void setFace(String face) { this.face = face; } public String getClothes() { return clothes; } public void setClothes(String clothes) { this.clothes = clothes; } public String getHair() { return hair; } public void setHair(String hair) { this.hair = hair; } }
抽象建造者和具体建造者类
package homework; public abstract class Rolebuilder {//建造者 角色建造者 protected Role role=new Role(); public abstract void build(); public Role getRole() { return role; } } class Daji extends Rolebuilder{//具体建造者 妲己:女,锥子脸,咖啡女仆,短发 public void build() { role.setName("妲己"); role.setSex("女"); role.setFace("锥子脸"); role.setClothes("咖啡女仆"); role.setHair("短发"); } } class Zhenji extends Rolebuilder{//具体建造者 甄姬:女,瓜子脸,花好人间,斜刘海 public void build() { role.setName("甄姬"); role.setSex("女"); role.setFace("瓜子脸"); role.setClothes("花好人间"); role.setHair("斜刘海"); } } class Gongben extends Rolebuilder{//具体建造者 宫本:男,菱形脸,鬼剑武藏,冲天辫 public void build() { role.setName("宫本武藏"); role.setSex("男"); role.setFace("菱形脸"); role.setClothes("鬼剑武藏"); role.setHair("冲天辫"); } }
指挥者
package homework; public class Player {//指挥者 玩家 private Rolebuilder rb; public void setRolebuilder(Rolebuilder rb) { this.rb=rb; } public Role construct() { rb.build(); return rb.getRole(); } }
模拟DAO
package homework; import javax.xml.parsers.*; import org.w3c.dom.*; import java.io.*; import java.net.URL; public class XMLUtil { public static Object getBean() { try { //创建DOM文档对象 DocumentBuilderFactory dFactory=DocumentBuilderFactory.newInstance();//文档制造者工厂创建了一个 文档制造者工厂对象 DocumentBuilder builder=dFactory.newDocumentBuilder();//文档制造者类 通过 文档制造者工厂 创造一个 文档制造者对象 Document doc;//文档制造者 创建 文档 doc=builder.parse(new File("src/homework/config.xml"));//解析xml文件 //获取包含类名的文本节点 NodeList nl=doc.getElementsByTagName("className");//文本节点列表里有很多被className标签夹着的内容 Node classNode=nl.item(0).getFirstChild(); //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; } } }
<?xml version="1.0"?> <config> <className>homework.Zhenji</className> </config>
客户端
package homework; public class Client { public static void main(String[] args) { //从XML文档里知道要做成哪个具体建造者,通过指挥者对象设值、建造、后返回 Rolebuilder rb=(Rolebuilder)XMLUtil.getBean(); Player player=new Player(); player.setRolebuilder(rb); Role role=player.construct(); System.out.println("姓名:"+role.getName()); System.out.println("性别:"+role.getSex()); System.out.println("脸型:"+role.getFace()); System.out.println("服饰:"+role.getClothes()); System.out.println("发行:"+role.getHair()); } }