大多数用户,并不关心部件的装配细节,也不单独使用某个部件,而是使用一辆完整的汽车。
建造者模式(Builder Pattern):将一个复杂对象的创建和它的表示分离,使得同样的构建过程可以创建不同的表示。
Builder既可以是抽象类,也可以是接口。
Product:
public class Product { private String partA; private String partB; private String partC; //partA的Getter方法和Setter方法省略 //partB的Getter方法和Setter方法省略 //partC的Getter方法和Setter方法省略 }
Builder:
public abstract class Builder { //创建产品对象 protected Product product = new Product(); public abstract void buildPartA(); public abstract void buildPartB(); public abstract void buildPartC(); //返回产品对象 public Product getResult() { return product; } }
ConcreteBuilder:
public class ConcreteBuilder extends Builder { @Override public void buildPartA() { product.setPartA("A"); } @Override public void buildPartB() { product.setPartB("B"); } @Override public void buildPartC() { product.setPartC("C"); } }
Director:
public class Director { private Builder builder; public Director(Builder builder) { this.builder = builder; } public void setBuilder(Builder builder) { this.builder = builder; } //产品构建与组装 public Product construct() { builder.buildPartA(); builder.buildPartB(); builder.buildPartC(); return builder.getResult(); } }
Client:
... Builder builder = new ConcreteBuilder(); Director director = new Director(builder); Product product = director.construct(); ...
某游戏软件公司决定开发一款基于角色扮演的多人在线网络游戏,玩家可以在游戏中扮演虚拟世界中的一个特定角色。角色根据不同的游戏情节和统计数据(例如力量、魔法、技能等)具有不同的能力,角色也会随着不断升级而拥有更加强大的能力。
作为该游戏的一个重要组成部分,需要对游戏角色进行设计,而且随着该游戏的升级将不断增加新的角色。通过分析发现,游戏角色是一个复杂对象,它包含性别、脸型等多个组成部分,不同类型的游戏角色,其性别、脸型、服装、发型等外部特性都有所差异。
例如“天使”拥有美丽的面容和披肩的长发,并身穿一袭白裙;而“恶魔”极其丑陋,留着光头并穿一件剌眼的黑衣。
无论是何种造型的游戏角色,它的创建步骤都大同小异,都需要逐步创建其组成部分,再将各组成部分装配成一个完整的游戏角色。
试使用建造者模式来实现游戏角色的创建。
package designpatterns.builder; public class Actor { private String type; private String sex; private String face; private String costume; private String hairstyle; public String getType() { return type; } public void setType(String type) { this.type = type; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public String getFace() { return face; } public void setFace(String face) { this.face = face; } public String getCostume() { return costume; } public void setCostume(String costume) { this.costume = costume; } public String getHairstyle() { return hairstyle; } public void setHairstyle(String hairstyle) { this.hairstyle = hairstyle; } }
package designpatterns.builder; public abstract class ActorBuilder { protected Actor actor = new Actor(); public abstract void builderType(); public abstract void builderSex(); public abstract void builderFace(); public abstract void builderCostume(); public abstract void builderHairstyle(); //工厂方法,返回一个完整的游戏角色对象 public Actor createActor() { return actor; } }
package designpatterns.builder; public class HeroBuilder extends ActorBuilder { @Override public void builderType() { actor.setType("英雄"); } @Override public void builderSex() { actor.setSex("男"); } @Override public void builderFace() { actor.setFace("英俊"); } @Override public void builderCostume() { actor.setCostume("盔甲"); } @Override public void builderHairstyle() { actor.setHairstyle("飘逸"); } }
package designpatterns.builder; public class AngelBuilder extends ActorBuilder { @Override public void builderType() { actor.setType("天使"); } @Override public void builderSex() { actor.setSex("女"); } @Override public void builderFace() { actor.setFace("漂亮"); } @Override public void builderCostume() { actor.setCostume("白裙"); } @Override public void builderHairstyle() { actor.setHairstyle("披肩长发"); } }
package designpatterns.builder; public class DevilBuilder extends ActorBuilder{ @Override public void builderType() { actor.setType("恶魔"); } @Override public void builderSex() { actor.setSex("妖"); } @Override public void builderFace() { actor.setFace("丑陋"); } @Override public void builderCostume() { actor.setCostume("黑衣"); } @Override public void builderHairstyle() { actor.setHairstyle("光头"); } }
package designpatterns.builder; public class ActorController { //逐步构建复杂产品对象 public Actor construct(ActorBuilder ab) { Actor actor; ab.builderType(); ab.builderSex(); ab.builderFace(); ab.builderCostume(); ab.builderHairstyle(); actor = ab.createActor(); return actor; } }
<?xml version="1.0"?> <config> <className>designpatterns.builder.AngelBuilder</className> </config>
package designpatterns.builder; 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 java.io.File; 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//designpatterns//builder//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; } } }
package designpatterns.builder; public class Client { public static void main(String[] args) { ActorBuilder ab; //针对抽象建造者编程 ab = (ActorBuilder) XMLUtil.getBean(); //反射生成具体建造者对象 ActorController ac = new ActorController(); Actor actor; actor = ac.construct(ab); //通过指挥者创建完整的建造者对象 String type = actor.getType(); System.out.println(type + "的外观:"); System.out.println("性别:" + actor.getSex()); System.out.println("面容:" + actor.getFace()); System.out.println("服装:" + actor.getCostume()); System.out.println("发型:" + actor.getHairstyle()); } }
<?xml version="1.0"?> <config> <className>designpatterns.builder.HeroBuilder</className> </config>
指挥者类Director是建造者模式的重要组成部分,简单的 Director类用于指导具体建造者如何构建产品,它按一定次序调用Builder的buildPartX()方法,控制调用的先后次序,并向客户端返回一个完整的产品对象。下面讨论几种Director 的变化形式。
package designpatterns.builder; public class ActorController { //逐步构建复杂产品对象 public Actor construct(ActorBuilder ab) { Actor actor; ab.builderType(); ab.builderSex(); ab.builderFace(); ab.builderCostume(); ab.builderHairstyle(); actor = ab.createActor(); return actor; } }
在有些情况下,为了简化系统结构,可以将Director和抽象建造者Builder进行合并.在Builder中提供逐步构建复杂产品对象的construct()方法。由于Builder类通常为抽象类因此可以将construct()方法定义为静态(static)方法,以便客户端能够直接调用。
如果将游戏角色实例中的指挥者类ActorController省略,ActorBuilder类的代码修改如下:
ActorBuilder:
package designpatterns.builder; public abstract class ActorBuilder { protected static Actor actor = new Actor(); public abstract void builderType(); public abstract void builderSex(); public abstract void builderFace(); public abstract void builderCostume(); public abstract void builderHairstyle(); public static Actor construct(ActorBuilder ab) { Actor actor; ab.builderType(); ab.builderSex(); ab.builderFace(); ab.builderCostume(); ab.builderHairstyle(); actor = ab.createActor(); return actor; } }
Client:
... ActorBuilder ab; ab = (ActorBuilder) XMLUtil.getBean(); Actor actor; actor = ActorBuilder.construct(ab); ...
另一种
ActorBuilder:
package designpatterns.builder; public abstract class ActorBuilder { protected Actor actor = new Actor(); public abstract void builderType(); public abstract void builderSex(); public abstract void builderFace(); public abstract void builderCostume(); public abstract void builderHairstyle(); public Actor construct() { Actor actor; ab.builderType(); ab.builderSex(); ab.builderFace(); ab.builderCostume(); ab.builderHairstyle(); actor = ab.createActor(); return actor; } }
以上两种对Director类的省略方式都不影响系统的灵活性和可扩展性,同时还简化了系统结构,但加重了抽象建造者类的职责。如果construct()方法较为复杂,待构建产品的组成部分较多,建议还是将construct()方法单独封装在Director中,这样更符合单一职责原则。
通过Director类更加精细地控制产品的创建过程,增加一类称为钩子方法(Hook Method) 的特殊方法控制是否对某个buildPartX()进行调用。
例如可以在游戏角色的抽象建造者类ActorBuilder中定义一个方法isBareheaded(),用于判断某个角色是否为“光头(Bareheaded)”,在ActorBuilder为之提供一个默认实现,其返回值为false,代码如下:
ActorBuilder:
package designpatterns.builder; public abstract class ActorBuilder { protected Actor actor = new Actor(); public abstract void builderType(); public abstract void builderSex(); public abstract void builderFace(); public abstract void builderCostume(); public abstract void builderHairstyle(); //钩子方法 pubic boolean isBareheaded(){ return false; } //工厂方法,返回一个完整的游戏角色对象 public Actor createActor() { return actor; } }
如果某个角色无需构建头发部件,例如“恶魔(Devil)”,则对应的具体构造器DevilBuilder将覆盖isBareheaded()方法,并将返回值改为true。
DevilBuilder:
package designpatterns.builder; public class DevilBuilder extends ActorBuilder{ @Override public void builderType() { actor.setType("恶魔"); } @Override public void builderSex() { actor.setSex("妖"); } @Override public void builderFace() { actor.setFace("丑陋"); } @Override public void builderCostume() { actor.setCostume("黑衣"); } @Override public void builderHairstyle() { actor.setHairstyle("光头"); } //覆盖钩子方法 public boolean isBareheaded(){ return true; } }
ActorController:
package designpatterns.builder; public class ActorController { //逐步构建复杂产品对象 public Actor construct(ActorBuilder ab) { Actor actor; ab.builderType(); ab.builderSex(); ab.builderFace(); ab.builderCostume(); //通过钩子方法来控制产品的构建 if(!ab.isBareheaded()){ ab.builderHairstyle(); } actor = ab.createActor(); return actor; } }
当在客户端代码中指定具体建造者类型并通过指挥者来实现产品的逐步构建时,将调用钩子方法isBareheaded()来判断游戏角色是否有头发,如果isBareheaded()方法返回true,即没有头发,将跳过构建发型的方法buildHairstyrle(),否则将执行buildHairstyle()方法。
通过引入钩子方法,可以在Director中对复杂产品的构建进行精细的控制,不仅指定buildPartX()方法的执行顺序,还可以控制是否需要执行某buildPartX()方法。