复杂对象的组装与创建——建造者模式(二)

8.3 完整解决方案

Sunny公司开发人员决定使用建造者模式来实现游戏角色的创建,其基本结构如图8-3所示:

复杂对象的组装与创建——建造者模式(二) - 图1

图8-3 游戏角色创建结构图

在图8-3中,ActorController充当指挥者,ActorBuilder充当抽象建造者,HeroBuilder、AngelBuilder和DevilBuilder充当具体建造者,Actor充当复杂产品。完整代码如下所示: //Actor角色类:复杂产品,考虑到代码的可读性,只列出部分成员属性,且成员属性的类型均为String,真实情况下,有些成员属性的类型需自定义

  1. class Actor
  2. {
  3. private String type; //角色类型
  4. private String sex; //性别
  5. private String face; //脸型
  6. private String costume; //服装
  7. private String hairstyle; //发型
  8. public void setType(String type) {
  9. this.type = type;
  10. }
  11. public void setSex(String sex) {
  12. this.sex = sex;
  13. }
  14. public void setFace(String face) {
  15. this.face = face;
  16. }
  17. public void setCostume(String costume) {
  18. this.costume = costume;
  19. }
  20. public void setHairstyle(String hairstyle) {
  21. this.hairstyle = hairstyle;
  22. }
  23. public String getType() {
  24. return (this.type);
  25. }
  26. public String getSex() {
  27. return (this.sex);
  28. }
  29. public String getFace() {
  30. return (this.face);
  31. }
  32. public String getCostume() {
  33. return (this.costume);
  34. }
  35. public String getHairstyle() {
  36. return (this.hairstyle);
  37. }
  38. }
  39. //角色建造器:抽象建造者
  40. abstract class ActorBuilder
  41. {
  42. protected Actor actor = new Actor();
  43. public abstract void buildType();
  44. public abstract void buildSex();
  45. public abstract void buildFace();
  46. public abstract void buildCostume();
  47. public abstract void buildHairstyle();
  48. //工厂方法,返回一个完整的游戏角色对象
  49. public Actor createActor()
  50. {
  51. return actor;
  52. }
  53. }
  54. //英雄角色建造器:具体建造者
  55. class HeroBuilder extends ActorBuilder
  56. {
  57. public void buildType()
  58. {
  59. actor.setType("英雄");
  60. }
  61. public void buildSex()
  62. {
  63. actor.setSex("男");
  64. }
  65. public void buildFace()
  66. {
  67. actor.setFace("英俊");
  68. }
  69. public void buildCostume()
  70. {
  71. actor.setCostume("盔甲");
  72. }
  73. public void buildHairstyle()
  74. {
  75. actor.setHairstyle("飘逸");
  76. }
  77. }
  78. //天使角色建造器:具体建造者
  79. class AngelBuilder extends ActorBuilder
  80. {
  81. public void buildType()
  82. {
  83. actor.setType("天使");
  84. }
  85. public void buildSex()
  86. {
  87. actor.setSex("女");
  88. }
  89. public void buildFace()
  90. {
  91. actor.setFace("漂亮");
  92. }
  93. public void buildCostume()
  94. {
  95. actor.setCostume("白裙");
  96. }
  97. public void buildHairstyle()
  98. {
  99. actor.setHairstyle("披肩长发");
  100. }
  101. }
  102. //恶魔角色建造器:具体建造者
  103. class DevilBuilder extends ActorBuilder
  104. {
  105. public void buildType()
  106. {
  107. actor.setType("恶魔");
  108. }
  109. public void buildSex()
  110. {
  111. actor.setSex("妖");
  112. }
  113. public void buildFace()
  114. {
  115. actor.setFace("丑陋");
  116. }
  117. public void buildCostume()
  118. {
  119. actor.setCostume("黑衣");
  120. }
  121. public void buildHairstyle()
  122. {
  123. actor.setHairstyle("光头");
  124. }
  125. }

指挥者类ActorController定义了construct()方法,该方法拥有一个抽象建造者ActorBuilder类型的参数,在该方法内部实现了游戏角色对象的逐步构建,代码如下所示:

  1. //游戏角色创建控制器:指挥者
  2. class ActorController
  3. {
  4. //逐步构建复杂产品对象
  5. public Actor construct(ActorBuilder ab)
  6. {
  7. Actor actor;
  8. ab.buildType();
  9. ab.buildSex();
  10. ab.buildFace();
  11. ab.buildCostume();
  12. ab.buildHairstyle();
  13. actor=ab.createActor();
  14. return actor;
  15. }
  16. }

为了提高系统的灵活性和可扩展性,我们将具体建造者类的类名存储在配置文件中,并通过工具类XMLUtil来读取配置文件并反射生成对象,XMLUtil类的代码如下所示:

  1. import javax.xml.parsers.*;
  2. import org.w3c.dom.*;
  3. import org.xml.sax.SAXException;
  4. import java.io.*;
  5. class XMLUtil
  6. {
  7. //该方法用于从XML配置文件中提取具体类类名,并返回一个实例对象
  8. public static Object getBean()
  9. {
  10. try
  11. {
  12. //创建文档对象
  13. DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();
  14. DocumentBuilder builder = dFactory.newDocumentBuilder();
  15. Document doc;
  16. doc = builder.parse(new File("config.xml"));
  17. //获取包含类名的文本节点
  18. NodeList nl = doc.getElementsByTagName("className");
  19. Node classNode=nl.item(0).getFirstChild();
  20. String cName=classNode.getNodeValue();
  21. //通过类名生成实例对象并将其返回
  22. Class c=Class.forName(cName);
  23. Object obj=c.newInstance();
  24. return obj;
  25. }
  26. catch(Exception e)
  27. {
  28. e.printStackTrace();
  29. return null;
  30. }
  31. }
  32. }

配置文件config.xml中存储了具体建造者类的类名,代码如下所示:

  1. <?xml version="1.0"?>
  2. <config>
  3. <className>AngelBuilder</className>
  4. </config>
  5. 编写如下客户端测试代码:
  6. class Client
  7. {
  8. public static void main(String args[])
  9. {
  10. ActorBuilder ab; //针对抽象建造者编程
  11. ab = (ActorBuilder)XMLUtil.getBean(); //反射生成具体建造者对象
  12. ActorController ac = new ActorController();
  13. Actor actor;
  14. actor = ac.construct(ab); //通过指挥者创建完整的建造者对象
  15. String type = actor.getType();
  16. System.out.println(type + "的外观:");
  17. System.out.println("性别:" + actor.getSex());
  18. System.out.println("面容:" + actor.getFace());
  19. System.out.println("服装:" + actor.getCostume());
  20. System.out.println("发型:" + actor.getHairstyle());
  21. }
  22. }

编译并运行程序,输出结果如下:

  1. 天使的外观:
  2. 性别:女
  3. 面容:漂亮
  4. 服装:白裙
  5. 发型:披肩长发

在建造者模式中,客户端只需实例化指挥者类,指挥者类针对抽象建造者编程,客户端根据需要传入具体的建造者类型,指挥者将指导具体建造者一步一步构造一个完整的产品(逐步调用具体建造者的buildX()方法),相同的构造过程可以创建完全不同的产品。在游戏角色实例中,如果需要更换角色,只需要修改配置文件,更换具体角色建造者类即可;如果需要增加新角色,可以增加一个新的具体角色建造者类作为抽象角色建造者的子类,再修改配置文件即可,原有代码无须修改,完全符合“开闭原则”。