有群友催稿,需要看看建造者模式来满足工作上的需要,也就有了这篇文章。

什么是建造者模式?

传说女娲是用泥巴捏出人来的,当时捏出的小泥人虽然每个都是两条腿、两只手、一个脑袋的样子,但是具体到每个部位,却有有所不同:有的小泥人手长一点,有的手短一点;有的脑袋圆鼓鼓的,有的脑袋尖尖的······
造物者归纳起来其实就一句话:女娲造人,形态各异。

也就是说,当初女娲在造人的时候,她的心中是有一副蓝图——即是人型的样子,但是在塑造不同部位的时候,选择的是不同的“捏制”手法。女娲作为造物者,便是建造者模式最好的例子了。

让我们来看看建造者模式的定义:
建造者模式(生成器模式):将一个复杂对象的构建与它的表示分离,使得同样的构造器过程可以创建不同的表示。

它的UML类图表示如下:

造物者——建造者模式 - 图1

Builder是为创建一个具体产品、具体对象的各个部件指定的抽象接口,放到女娲造人的传说里,那就是人型蓝图

ConcreteBuilder则是实现Builder抽象方法的具体建造类,它可以看作是更为详细的蓝图,指明了高矮胖瘦、男女老幼。

Director则是根据ConcreteBuilder具体构造产品、对象的,它使用了Builder接口,在传说中,这就是造物者——女娲。

如何使用建造者模式?

举个例子

相信大家不少都玩过游戏,自然知道游戏的捏脸系统。捏脸系统可以让玩家选择人物的各个部位的大小、形状等等,体验一把造物者的感觉。除了玩家的角色,里面的NPC也是通过这个捏脸系统生成的造成游戏里的角色形态各异。就像下面这种:

造物者——建造者模式 - 图2

今天我就以一建造者模式来实现一个简单的游戏捏脸系统。这个捏脸系统可以设置姓名、选择人物的四肢、头部、服饰、武器、天赋等

UML类图设计

整个系统的UML类图如下:

造物者——建造者模式 - 图3

实现代码

  • 抽象建造类:声明建造一个角色所必备的部件。

    1. abstract public class AbstractBuilder {
    2. public abstract void BuildHead ();
    3. public abstract void BuildBody ();
    4. public abstract void BuildArms();
    5. public abstract void BuildLegs();
    6. public abstract void ChooseGenius();
    7. public abstract void ChooseCloth();
    8. public abstract void ChooseWeapon();
    9. public abstract void SetName();
    10. }

    由于该抽象类的所有方法都被声明为了抽象方法,继承它的子类必须实现所有的抽象方法,这就防止了某些部件的缺失

  • 具体建造类:用来选择角色每个部位具体的属性。

弓箭手

  1. public class ArcherBuilder extends AbstractBuilder {
  2. @Override
  3. public void BuildHead() {
  4. System.out.println("头部:精灵脑袋");
  5. }
  6. @Override
  7. public void BuildBody() {
  8. System.out.println("躯干:精灵身体");
  9. }
  10. @Override
  11. public void BuildArms() {
  12. System.out.println("手臂:精灵手臂");
  13. }
  14. @Override
  15. public void BuildLegs() {
  16. System.out.println("腿部:精灵腿");
  17. }
  18. @Override
  19. public void ChooseGenius() {
  20. System.out.println("天赋:鹰眼;急性;弯弓;");
  21. }
  22. @Override
  23. public void ChooseCloth() {
  24. System.out.println("服饰:初级弓箭手套装");
  25. }
  26. @Override
  27. public void ChooseWeapon() {
  28. System.out.println("武器:新手木弓");
  29. }
  30. @Override
  31. public void SetName() {
  32. System.out.println("姓名:潘德初级射手");
  33. }
  34. }

战神刑天

  1. public class GiantBuilder extends AbstractBuilder {
  2. @Override
  3. public void BuildHead() {
  4. System.out.println("头部:巨人头");
  5. }
  6. @Override
  7. public void BuildBody() {
  8. System.out.println("躯干:荒古圣体");
  9. }
  10. @Override
  11. public void BuildArms() {
  12. System.out.println("手臂:麒麟臂");
  13. }
  14. @Override
  15. public void BuildLegs() {
  16. System.out.println("腿部:刑天之腿");
  17. }
  18. @Override
  19. public void ChooseGenius() {
  20. System.out.println("天赋:威压;力巨;不死;");
  21. }
  22. @Override
  23. public void ChooseCloth() {
  24. System.out.println("服饰:刑天套");
  25. }
  26. @Override
  27. public void ChooseWeapon() {
  28. System.out.println("武器:刑天斧");
  29. }
  30. @Override
  31. public void SetName() {
  32. System.out.println("姓名:战神刑天");
  33. }
  34. }j

另外,邓布利多、骑士长等代码就不贴了,请至源码查看。

  • 玩家类:用来调用具体的角色类的构造方法,实现所有角色的部件按照顺序生成,无一缺漏。

    1. public class Player {
    2. private AbstractBuilder abstractBuilder;
    3. public Player(AbstractBuilder abBuilder) {
    4. abstractBuilder=abBuilder;
    5. }
    6. public void CreateCharacter(){
    7. System.out.println("创建角色中......");
    8. abstractBuilder.BuildHead();
    9. abstractBuilder.BuildBody();
    10. abstractBuilder.BuildArms();
    11. abstractBuilder.BuildLegs();
    12. abstractBuilder.ChooseGenius();
    13. abstractBuilder.ChooseCloth();
    14. abstractBuilder.ChooseWeapon();
    15. abstractBuilder.SetName();
    16. System.out.println("角色创建完毕!");
    17. }
    18. }jj

运行结果

  1. 世界角色构造中,请稍候......
  2. 创建角色中......
  3. 头部:精灵脑袋
  4. 躯干:精灵身体
  5. 手臂:精灵手臂
  6. 腿部:精灵腿
  7. 天赋:鹰眼;急性;弯弓;
  8. 服饰:初级弓箭手套装
  9. 武器:新手木弓
  10. 姓名:潘德初级射手
  11. 角色创建完毕!
  12. 创建角色中......
  13. 头部:西方帝国头
  14. 躯干:骑士躯
  15. 手臂:百战臂
  16. 腿部:千夫长之腿
  17. 天赋:勇猛;不惧;冲锋
  18. 服饰:东罗帝国飞羽骑士套
  19. 武器:高仿战神枪
  20. 姓名:大骑士长
  21. 角色创建完毕!
  22. 创建角色中......
  23. 头部:普通人类头
  24. 躯干:老迈躯
  25. 手臂:骨瘦臂
  26. 腿部:不动冥王腿
  27. 天赋:无量;洞察;瞬发
  28. 服饰:大法师袍
  29. 武器:瓦巴杰克
  30. 姓名:阿不思·邓布利多
  31. 角色创建完毕!
  32. 创建角色中......
  33. 头部:巨人头
  34. 躯干:荒古圣体
  35. 手臂:麒麟臂
  36. 腿部:刑天之腿
  37. 天赋:威压;力巨;不死;
  38. 服饰:刑天套
  39. 武器:刑天斧
  40. 姓名:战神刑天
  41. 角色创建完毕!
  42. 世界角色构造完毕,祝您游戏愉快!

总结

应用场景

建造者模式主要是用于创建一些复杂的对象,这些对象内部间的构造顺序、构造部件通常是稳定的,但是对象内部的具体构建方法常常是变化的

除了之前说到的女娲造人、游戏捏人的例子,生活中的麦当劳、肯德基等流水线式的快餐食品其实也是建造者模式的一个例子:每种汉堡的用料、时间都是不同的,但是顺序和组成部件确实大同小异,比如都的要两片面包、中间加肉等等,只不过面包种类不一样,肉的种类也可以不一样。

另一个建造者模式的例子可以拿五一假期的安排来说,假期时长一共五天,但是每一天究竟干什么、去哪、干多久,都是可以任意规划和选择的

优点

  • 封装性强建造代码与表示代码分离,由于建造者隐藏了该产品是如何组装的,所以外部客户是无法知道其内部实现代码;
  • 扩展性强:若需要改变一个产品的内部表示,只需要再定义一个具体的建造者就可以了
  • 过程精细化:通过创建者模式可以实现每一个过程都依次无误进行,并且每一个过程还可以进一步分为更多的子过程:例如捏脸系统的头部,还可以细化为五冠、表情等等,这可以通过对头部进行另一个建造者模式来实现

    缺点

  • 由于每个角色之间的组件无法复用,当建造者类太多,代码较为臃肿,此时需要结合其他的设计模式进行优化,例如组合模式

  • 只适合于最终的实例组成部分相似的情况。

    项目地址

    https://github.com/white0dew/Design-pattern/tree/master

    往期文章

    大话设计模式(1)——设计原则与单例模式
    大话设计模式(2)——策略模式
    参考链接
    《大话设计模式》
    《Headfist 设计模式》