10.3提炼代码

既然用了继承,并且肯定这个继承有意义,就应该要成为子类的模板,所有重复的代码都应该要上升到父类去,而不是让每个子类都去重复。
当我们要完成在某一细节层次一致的一个过程或一系列步骤,但其个别步骤在更详细的层次上的实现可能不同时,我们通常考虑用模板方法模式来处理。
代码结构图 第十章 考题抄错会做也白搭——模板方法模式 - 图1代码实现
试卷父类代码

  1. //金庸小说考题试卷
  2. class TestPaper
  3. {
  4. public function TestQuestion1()
  5. {
  6. echo '杨过得到,后来给了郭靖,炼成倚天剑、屠龙刀的玄铁可能是[ ]' . PHP_EOL;
  7. echo 'a.球磨铸铁 b.马口铁 c.高速合金钢 d.碳素纤维' . PHP_EOL;
  8. echo '答案:' . $this->Answer1() . PHP_EOL;
  9. }
  10. public function TestQuestion2()
  11. {
  12. echo '杨过、程英、陆无双铲除了情花,造成[ ]' . PHP_EOL;
  13. echo 'a.使这种植物不再害人 b.使一种珍惜物种灭绝 c.破坏了那个生物圈的生态平衡 d.造成该地区沙漠化' . PHP_EOL;
  14. echo '答案:' . $this->Answer2() . PHP_EOL;
  15. }
  16. public function TestQuestion3()
  17. {
  18. echo '蓝凤凰致使华山师徒、桃谷六仙呕吐不止,如果你是大夫,会给他们开什么药[ ]' . PHP_EOL;
  19. echo 'a.阿司匹林 b.牛黄解毒片 c.氟哌酸 d.让他们喝大量的生牛奶 e.以上全不对' . PHP_EOL;
  20. echo '答案:' . $this->Answer3() . PHP_EOL;
  21. }
  22. protected function Answer1()
  23. {
  24. return '';
  25. }
  26. protected function Answer2()
  27. {
  28. return '';
  29. }
  30. protected function Answer3()
  31. {
  32. return '';
  33. }
  34. }

学生子类代码

  1. //学生甲抄的试卷
  2. class TestPaperA extends TestPaper
  3. {
  4. protected function Answer1()
  5. {
  6. return 'b';
  7. }
  8. protected function Answer2()
  9. {
  10. return 'c';
  11. }
  12. protected function Answer3()
  13. {
  14. return 'a';
  15. }
  16. }
  17. //学生乙抄的试卷
  18. class TestPaperB extends TestPaper
  19. {
  20. protected function Answer1()
  21. {
  22. return 'c';
  23. }
  24. protected function Answer2()
  25. {
  26. return 'a';
  27. }
  28. protected function Answer3()
  29. {
  30. return 'a';
  31. }
  32. }

客户端代码

  1. public function templateMethodImp()
  2. {
  3. echo '学生甲抄的试卷:' . PHP_EOL;
  4. $studentA = new TestPaperA();
  5. $studentA->TestQuestion1();
  6. $studentA->TestQuestion2();
  7. $studentA->TestQuestion3();
  8. echo '学生乙抄的试卷:' . PHP_EOL;
  9. $studentB = new TestPaperB();
  10. $studentB->TestQuestion1();
  11. $studentB->TestQuestion2();
  12. $studentB->TestQuestion3();
  13. }

10.4模板方法模式

模板方法模式,定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
模板方法模式(TemplateMethod)结构图 第十章 考题抄错会做也白搭——模板方法模式 - 图2代码示例
AbstractClass抽象类

  1. abstract class AbstractClass
  2. {
  3. //一些抽象行为,放到子类去实现
  4. abstract function PrimitiveOperation1();
  5. abstract function PrimitiveOperation2();
  6. //模板方法,给出了逻辑的骨架,
  7. //而逻辑的组成是一些相应的抽
  8. //象操作,它们都推迟到子类实现
  9. public function TemplateMethod()
  10. {
  11. $this->PrimitiveOperation1();
  12. $this->PrimitiveOperation2();
  13. echo '模板方法'. PHP_EOL;
  14. }
  15. }

ConcreteClass类

  1. class ConcreteClassA extends AbstractClass
  2. {
  3. //与ConcreteClassB不同的实现方法
  4. public function PrimitiveOperation1()
  5. {
  6. echo '具体类A方法1实现' . PHP_EOL;
  7. }
  8. public function PrimitiveOperation2()
  9. {
  10. echo '具体类A方法2实现' . PHP_EOL;
  11. }
  12. }
  13. class ConcreteClassB extends AbstractClass
  14. {
  15. //与ConcreteClassA不同的实现方法
  16. public function PrimitiveOperation1()
  17. {
  18. echo '具体类B方法1实现' . PHP_EOL;
  19. }
  20. public function PrimitiveOperation2()
  21. {
  22. echo '具体类B方法2实现' . PHP_EOL;
  23. }
  24. }

客户端代码

  1. public function templateMethodDemo()
  2. {
  3. $c = new ConcreteClassA();
  4. $c->TemplateMethod();
  5. $c = new ConcreteClassB();
  6. $c->TemplateMethod();
  7. }

10.5模板方法模式特点

模板方法模式的特点:

  • 模板方法模式是通过把不变行为搬移到超类,去除子类中的重复代码来体现它的优势。
  • 模板方法模式就是提供了一个很好的代码复用平台。有时候,我们会遇到由一系列步骤构成的过程需要执行。这个过程从高层次上看是相同的,但有些步骤的实现可能不同。这时候,我们通常就应该要考虑用模板方法模式了。
  • 碰到这个情况,当不变的和可变的行为在方法的子类实现中混合在一起的时候,不变的行为就会在子类中重复出现。我们通过模板方法模式把这些行为搬移到单一的地方,这样就帮助子类摆脱重复的不变行为的纠缠。

模板方法模式是很常用的模式,对继承和多态玩得好的人几乎都会在继承体系中多多少少用到它。比如在.NET或Java类库的设计中,通常都会利用模板方法模式提取类库中的公共行为到抽象类中。