从零开始学设计模式(6):MVC

Introduction

20世纪80年代,计算机发展迅速,编程技术也日益分化。桌面应用编程,也逐渐出现了用户图形界面和程序逻辑分离的程序设计。到了90年代,web的出现更是让这种程序设计模式得以延续。
这种设计模式便是MVC(Model-View-Control),除了MVC,还有MVC的变种,如MVVM(Model-View-View Model)等。

MVC

回到80年代的桌面应用编程,当时面向对象的编程设计模式(见PHP设计模式(一):基础编程模式)兴起,程序员将桌面应用分割成两个大的对象:领域对象(domain objects)和可视对象(presentation objects)。领域对象是对现实事物的抽象模型,可视对象是对用户界面部分的抽象模型。
后来人们发现,只有领域对象和可视对象是不够的,特别是在复杂的业务中。根据PHP设计模式(三):封装中介绍的设计原则,在面向对象程序设计中,类和类之间的访问、交互和更新应该是通过Accessors和Mutators。
那么如果操作领域对象呢?人们引入了控制器(controller)的对象,通过控制器来操作领域模型。
到此,MVC模型逐渐稳定下来,用户通过可视对象操作控制器对象,控制器对象再去操作领域对象。

MVC中的设计模式

上面介绍的MVC属于抽象度比较高的设计模式,在实际编程中,需要遵守下面的设计模式。

基于接口去编程

基于接口去编程的好处就是分离设计和实现,这一点我们在PHP设计模式(二):抽象类和接口已经介绍过了,下面我们举一个实际的例子来说明这个设计的好处。

  1. <?php
  2. abstractclassAnimal{
  3. protected $name;
  4. abstractprotectedfunction eatFish();
  5. abstractprotectedfunction eatMoss();
  6. publicfunction eat(){
  7. if($this->eatFish()){
  8. echo $this->name ." can eat fish.\n";
  9. }
  10. if($this->eatMoss()){
  11. echo $this->name ." can eat moss.\n";
  12. }
  13. }
  14. }
  15. ?>

我们创建一个鲸鱼类:

  1. <?php
  2. include_once('Animal.php');
  3. classWhaleextendsAnimal{
  4. publicfunction __construct(){
  5. $this->name ="Whale";
  6. }
  7. publicfunction eatFish(){
  8. return TRUE;
  9. }
  10. publicfunction eatMoss(){
  11. return FALSE;
  12. }
  13. }
  14. $whale =newWhale();
  15. $whale->eat();
  16. ?>

运行一下:

$ php Whale.php
Whale eats fish.
看上去没什么问题,对吧?我们创建一个鲤鱼类:

  1. <?php
  2. include_once('Animal.php');
  3. classCarpextendsAnimal{
  4. publicfunction __construct(){
  5. $this->name ="Carp";
  6. }
  7. publicfunction eatMoss(){
  8. return TRUE;
  9. }
  10. }
  11. $carp =newCarp();
  12. $carp->eat();
  13. ?>

运行一下:

  1. $ php Carp.php
  2. PHP Fatal error:ClassCarp contains 1abstract method and must therefore be
  3. declared abstractor implement the remaining method (Animal::eatFish)in
  4. Carp.php on line 9

报错了,对吧?因为我们实现Carp.php的时候故意没有去实现eatFish接口,基于接口的编程设计模式可以在开发期就发现这种逻辑错误。

使用组件而不是继承

将一个对象拆成更小的对象,这些小的对象成为组件(composition)。尽量使用组件而不是继承的设计模式的意义在于,多种继承之下,子类可能会拥有大量毫无意义的未实现方法。而通过组件的方式,子类可以选择需要的组件。
下面给出一个例子:

  1. <?php
  2. abstractclassAnimal{
  3. protected $name;
  4. abstractprotectedfunction eatFish();
  5. abstractprotectedfunction eatMoss();
  6. publicfunction eat(){
  7. if($this->eatFish()){
  8. echo $this->name ." can eat fish.\n";
  9. }
  10. if($this->eatMoss()){
  11. echo $this->name ." can eat moss.\n";
  12. }
  13. }
  14. }
  15. classWhaleextendsAnimal{
  16. protectedfunction __construct(){
  17. $this->name ="Whale";
  18. }
  19. protectedfunction eatFish(){
  20. return TRUE;
  21. }
  22. protectedfunction eatMoss(){
  23. return FALSE;
  24. }
  25. }
  26. classBullWhaleextendsWhale{
  27. publicfunction __construct(){
  28. $this->name ="Bull Whale";
  29. }
  30. publicfunction getGender(){
  31. return"Male";
  32. }
  33. }
  34. ?>

这里的BullWhale其实非常冗余,实际的业务模型可能并不需要这么复杂,这就是多重继承的恶果。
而组件则不同,通过将行为拆分成不同的部分,又最终子类决定使用哪些组件。
下面给出一个例子:

  1. <?php
  2. classAction{
  3. private $name;
  4. publicfunction __construct($name){
  5. $this->name = $name;
  6. }
  7. publicfunction eat($food){
  8. echo $this->name ." eat ". $food .".\n";
  9. }
  10. }
  11. classGender{
  12. private $gender;
  13. publicfunction __construct($gender){
  14. $this->gender= $gender;
  15. }
  16. publicfunction getGender(){
  17. return $this->gender;
  18. }
  19. }
  20. classBullWhale{
  21. private $action;
  22. private $gender;
  23. publicfunction __construct(){
  24. $this->action =newAction("Bull Whale");
  25. $this->gender =newGender("Male");
  26. }
  27. publicfunction eatFood($food){
  28. $this->action->eat($food);
  29. }
  30. publicfunction getGender(){
  31. return $this->gender->getGender();
  32. }
  33. }
  34. $bullWhale =newBullWhale();
  35. $bullWhale->eatFood("fish");
  36. echo $bullWhale->getGender()."\n";
  37. ?>

运行一下:

  1. $ php BullWhale.php
  2. BillWhale eat fish.
  3. Male

BullWhale由Action和Gender组件构成,不同的类可以选择不同的组件组合,这样就不会造成类冗余了。

Summary

实际编程中,更多的往往是混合架构,如既包含继承,又包含组件的编程设计模式。不过,掌握基本的编程架构设计是一切的基础。

未经允许不得转载:天宝寺||陈瑞轩 » 从零开始学设计模式(6):MVC

赞 (0) 打赏

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏