第六章 装饰模式(穿什么有这么重要吗)

  1. <?php
  2. abstract class Component
  3. {
  4. abstract public function Operation();
  5. }
  6. /**
  7. * ConcreteComponent
  8. */
  9. class ConcreteComponent extends Component
  10. {
  11. public function Operation()
  12. {
  13. echo "具体对象的操作.\n";
  14. }
  15. }
  16. abstract class Decorator extends Component
  17. {
  18. protected $component;
  19. // 设置component
  20. public function SetComponent($component)
  21. {
  22. $this->component = $component;
  23. }
  24. // 重写Operation(),实际执行的是component的Operation方法
  25. public function Operation()
  26. {
  27. if ($this->component != null)
  28. {
  29. $this->component->Operation();
  30. }
  31. }
  32. }
  33. /**
  34. * ConcreteDecoratorA
  35. */
  36. class ConcreteDecoratorA extends Decorator
  37. {
  38. // 本类的独有功能,以区别于ConcreteDecoratorB
  39. private $addedState;
  40. public function Operation()
  41. {
  42. // 首先运行原Component的Operation(),再执行本类的功能,
  43. // 如addedState,相当于对原Component进行了装饰
  44. parent::Operation();
  45. $this->addedState = "ConcreteDecoratorA Status";
  46. echo $this->addedState."\n";
  47. echo "具体装饰对象A的操作.\n";
  48. }
  49. }
  50. /**
  51. * ConcreteDecoratorB
  52. */
  53. class ConcreteDecoratorB extends Decorator
  54. {
  55. public function Operation()
  56. {
  57. // 首先运行原Component的Operation(),再执行本类的功能,
  58. // 如addedBehavior,相当于对原Component进行了装饰
  59. parent::Operation();
  60. $this->addedBehavior();
  61. echo "具体装饰对象B的操作.\n";
  62. }
  63. // 本类的独有功能,以区别于ConcreteDecoratorA
  64. private function addedBehavior()
  65. {
  66. echo "ConcreteDecoratorB Status.\n";
  67. }
  68. }
  69. // 客户端代码
  70. // 装饰的方法是:首先用ConcreteComponent实例化对象c,
  71. // 然后用ConcreteDecoratorA的实例对象$di来包装$c,
  72. // 然后再用ConcreteDecoratorB的实例$d2包装$d1,
  73. // 最终执行$d2的Operation();
  74. $c = new ConcreteComponent();
  75. $d1 = new ConcreteDecoratorA();
  76. $d2 = new ConcreteDecoratorB();
  77. $d1->SetComponent($c);
  78. $d2->SetComponent($d1);
  79. $d2->Operation();

当只有一个ConcreteComponent类而没有抽象的Component类 ,那么Decorator类可以是ConcreteComponent的一个子类。同样的道理,如果只有一个ConcreteDecorator类 ,那么就没有必要建立一个单独的Decorator类,而可以把Decorator类和ConcreteComponent类的责任合并成一个类。

书中用打扮的代码阐释了这样情况下的写法:

  1. <?php
  2. // 人
  3. class Person
  4. {
  5. private $name;
  6. function __construct($name)
  7. {
  8. $this->name = $name;
  9. }
  10. public function show()
  11. {
  12. echo "打扮".$this->name."\n";
  13. }
  14. }
  15. // 服饰类
  16. class Finery
  17. {
  18. protected $person;
  19. public function decorate($person)
  20. {
  21. $this->person = $person;
  22. }
  23. public function show()
  24. {
  25. if ($this->person != null)
  26. {
  27. $this->person->show();
  28. }
  29. }
  30. }
  31. // 具体服饰类
  32. class TShirts extends Finery
  33. {
  34. public function show()
  35. {
  36. echo "大T恤\n";
  37. parent::show();
  38. }
  39. }
  40. class BigTrouser extends Finery
  41. {
  42. public function show()
  43. {
  44. echo "跨裤\n";
  45. parent::show();
  46. }
  47. }
  48. class Sneakers extends Finery
  49. {
  50. public function show()
  51. {
  52. echo "破球鞋\n";
  53. parent::show();
  54. }
  55. }
  56. class Suit extends Finery
  57. {
  58. public function show()
  59. {
  60. echo "西装\n";
  61. parent::show();
  62. }
  63. }
  64. class Tie extends Finery
  65. {
  66. public function show()
  67. {
  68. echo "领带\n";
  69. parent::show();
  70. }
  71. }
  72. class LeatherShoes extends Finery
  73. {
  74. public function show()
  75. {
  76. echo "跨裤\n";
  77. parent::show();
  78. }
  79. }
  80. // 客户端代码
  81. $person = new Person("alex");
  82. $sneakers = new Sneakers();
  83. $bigTrouser = new BigTrouser();
  84. $tShirts = new TShirts();
  85. $sneakers->decorate($person);
  86. $bigTrouser->decorate($sneakers);
  87. $tShirts->decorate($bigTrouser);
  88. $tShirts->show();

总结

装饰模式,动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。

装饰模式是为已有功能动态的添加更多功能的一种方式。

当系统需要新功能的时候,是向旧的类中添加新的代码。这些新的代码通常装饰了原有类的核心职责或主要行为。

在主类中加入新的字段,新的方法和新的逻辑,从而增加了主类的复杂度,而这些新加入的东西仅仅是为满足一些只在某种特定情况下才会执行的特殊行为的需要。装饰模式却提供了一个非常好的解决方案,它把每个要装饰的功能放在单独的类中,并让这个类包装它要装饰的对象,对此,当需要执行特殊行为时,客户代码就可以在运行时根据需要选择地,按顺序地使用装饰功能包装对象了。

装饰模式的优点就是把类中的装饰功能从类中搬移去除,这样可以简化原有的类。

有效地把类的核心职责和装饰功能区分开了,而且可以去除相关类中的重复的装饰逻辑。