[关闭]
@zongwu 2017-02-05T09:34:39.000000Z 字数 4048 阅读 339

定义

动态的给一个对象添加一些额外的职责。

场景举例

现在是2187年,智能机器人已经发展到可以一个新的里程碑高度。全球知名厂商华为正在推广新一代“女友机器人”,该型号机器人除了不能生孩子其它都可以做。

今天顾客Alice 进入你的旗舰店正准备挑选她的下一任“机器人女友”。Alice希望她拥有 “大胸”、“肤白”、“瓜子脸”、“大眼睛”……(省略300个要求)。

好吧,Alice并不算本店遇到的最挑剔的客户,但幸好我们支持Alice作出他心爱的选择。因为我们有多于3000个可选方案,可供用户挑选自由组合。

思维导图1-0

最后Alice选择了,“C罩杯”、“瓜子脸”、“丰满”、“翘臀”套餐,确定付款之后。后台的3D打印机正在迅速的制造中,经历10分钟的漫长等待。Alice取到了他满意的机器人女友。

程序员视角

拆分角色

重新描述该场景

客户端(Alice)被装饰的对象(机器人) 添加了许多 装饰对象(肤色、胸型、脸型、身型)

现在分析得到了下面的类图,我们似乎找到了解决的方案,但是总还觉得差点什么?

类图1-1

顾客视角

我们进入商店并挑选了机器人的芯片、材质等,并确认下单。好吧,现在我们拥有了机器人未加任何装饰的原始对象,现在思考如何为它添加装饰。
一番思索后工程师Bob提出了方案1 :

  1. public class Customer {
  2. public static void main(String[] args) {
  3. Machine machine = new Machine();
  4. machine.add(new Body("纤细"));
  5. machine.add(new Chest("C罩杯"));
  6. machine.add(new Butt("翘臀"));
  7. machine.add(new Face("瓜子脸"));
  8. }
  9. }
  10. public class Machine {
  11. public void add(Body value) {
  12. System.out.println(value);
  13. }
  14. public void add(Chest value) {
  15. System.out.println(value);
  16. }
  17. public void add(Butt value) {
  18. System.out.println(value);
  19. }
  20. public void add(Face value) {
  21. System.out.println(value);
  22. }
  23. }
  24. // 此处示意下举出一个装饰对象,其他对象与其结构一致
  25. public class Body {
  26. public String value;
  27. public Body(String value) {
  28. this.value = value;
  29. }
  30. @Override
  31. public String toString() {
  32. return "身型{" +
  33. "value='" + value + '\'' +
  34. '}';
  35. }
  36. }

运行结果:

  1. 身型{value='纤细'}
  2. 胸型{value='C罩杯'}
  3. 臀型{value='翘臀'}
  4. 脸型{value='瓜子脸'}

Bob在思索后立即去实施,并发现了方案1的一处致命问题 —— 每添加一个装饰,就要修改机器人的源代码程序,去匹配添加的装饰

于是Bob在思索后考虑是否可以不改变机器人的源代码程序,动态的在外部修改其外观
于是出现了方案2:

  1. public class Customer {
  2. public static void main(String[] args) {
  3. // 先配置容器
  4. MachineContainer container = new MachineContainer();
  5. container.add(new Body("纤细"));
  6. container.add(new Chest("C罩杯"));
  7. container.add(new Butt("翘臀"));
  8. container.add(new Face("瓜子脸"));
  9. // 再用容器去配置机器人
  10. Machine machine = new Machine();
  11. machine.add(container);
  12. }
  13. }
  14. public class Machine {
  15. public void add(MachineContainer container) {
  16. System.out.println("TODO:解析容器并为Machine添加职责");
  17. }
  18. }
  19. public class MachineContainer {
  20. public void add(Body value) {
  21. System.out.println(value);
  22. }
  23. public void add(Chest value) {
  24. System.out.println(value);
  25. }
  26. public void add(Butt value) {
  27. System.out.println(value);
  28. }
  29. public void add(Face value) {
  30. System.out.println(value);
  31. }
  32. }

这样一来Bob觉得 机器人 添加 装饰容器的逻辑几乎不会发生变化,而容器又可以单独隔离出来配置不会影响机器人本身。

但转念一想,不对啊。这类虽然是隔离出来了,可是还是没有解决每添加一个装饰对象,就要修改一下容器的悲惨现实!!!于是Bob想啊想,觉得虽然每次都添加了新的装饰组件,但是装饰组件的行为并没有太大的改变,是否可以抽象出来。

  1. // 抽象出接口 表示类型是装饰组件类型
  2. public interface IDecorComponent {
  3. }
  4. // 修改容器代码 —— 去掉了对具体类型的耦合
  5. public class MachineContainer implements IDecorComponent {
  6. public void add(IDecorComponent component) {
  7. System.out.println("添加了" + component);
  8. }
  9. }
  10. // 装饰物实现了无方法的接口
  11. public class Body implements IDecorComponent {
  12. public String value;
  13. public Body(String value) {
  14. this.value = value;
  15. }
  16. @Override
  17. public String toString() {
  18. return "身型{" +
  19. "value='" + value + '\'' +
  20. '}';
  21. }
  22. }

Bob编译执行看了下结果,发现自己还遗漏了一个TODO还未做。

  1. 添加了身型{value='纤细'}
  2. 添加了胸型{value='C罩杯'}
  3. 添加了臀型{value='翘臀'}
  4. 添加了脸型{value='瓜子脸'}
  5. TODO:解析容器并为Machine添加职责
  6. 机器人组装完成

于是继续想啊想,他意识到无论是委托给 装饰容器还是机器人本身都避免不了要为机器人添加装饰物这事。既然在劫难逃,那就只能勇敢面对了。最终的方案:

  1. public class Customer {
  2. public static void main(String[] args) {
  3. Machine machine = new Machine();
  4. // 配置装饰容器
  5. IDecorComponent component = new MachineContainer(machine);
  6. component = new Body(component, "纤细");
  7. component = new Butt(component, "翘臀");
  8. component = new Chest(component, "C罩杯");
  9. component = new Face(component, "瓜子脸");
  10. // 触发添加行为
  11. component.addBehiavor();
  12. }
  13. }
  14. public interface IDecorComponent {
  15. void addBehiavor();
  16. }
  17. public class Machine {
  18. // 对Machine 没有任何侵入
  19. }
  20. public class MachineContainer implements IDecorComponent {
  21. private Machine machine;
  22. public MachineContainer(Machine machine) {
  23. this.machine = machine;
  24. }
  25. public void add(IDecorComponent component) {
  26. System.out.println("添加了" + component);
  27. }
  28. @Override
  29. public void addBehiavor() {
  30. System.out.println("===触发了装饰物的行为===");
  31. }
  32. }
  33. // 新增的类,用于委托递归操作
  34. public class Decor implements IDecorComponent {
  35. IDecorComponent component;
  36. public Decor(IDecorComponent component) {
  37. this.component = component;
  38. }
  39. @Override
  40. public void addBehiavor() {
  41. // 委托装饰的组件继续执行相关的行为
  42. this.component.addBehiavor();
  43. }
  44. }
  45. public class Body extends Decor {
  46. public String value;
  47. public Body(IDecorComponent component, String value) {
  48. super(component);
  49. this.value = value;
  50. }
  51. @Override
  52. public String toString() {
  53. return "身型{" +
  54. "value='" + value + '\'' +
  55. '}';
  56. }
  57. @Override
  58. public void addBehiavor() {
  59. // 触发了递归操作
  60. super.addBehiavor();
  61. System.out.println("添加了" + toString());
  62. }
  63. }

运行结果如下:

  1. ===触发了装饰物的行为===
  2. 添加了身型{value='纤细'}
  3. 添加了臀型{value='翘臀'}
  4. 添加了胸型{value='C罩杯'}
  5. 添加了脸型{value='瓜子脸'}
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注