+ -
当前位置:首页 → 问答吧 → 加工你的OO精华 工厂模式一

加工你的OO精华 工厂模式一

时间:2011-04-08

来源:互联网

加工你的OO精华 工厂模式


上几章都大致了解了在OOP设计中,应该针对抽象编程而不是具体实现编程。
  但是上面几章的代码中或多或少都又"new"来创建对象的实例。那么在这些地方,就不是针对抽象编程,而成了具体实现的编程。
  但使用"new" 有错吗?从本质上讲是没错的,因为这是OOP的基础。但是,从另一个角度去说,他是错误的。但错不在他。而在程序上面。
  简单的说,就是我们使用了new关键词将代码的执行硬编码进了程序之中。他不能在程序运行时来决定运行哪一个。也就是说,当我们希望一个项目需要改变的时候,需要添加新的对象的时候,需要打开文件进去修改
  第三节的装饰模式重点讲过一个原则:对修改关闭,对扩展打开。但是,频繁使用new这个创建实例的办法就是在破坏这个原则。因为你总是会要在不久的将来改变他的。只要你要添加新的对象。你必定要修改源文件。
  那错误具体是由什么导致的?第一章我说过,OO编程的基本原理不是那些原则,而是Change!改变。一切都是因为改变捣的鬼。
  看看下面这个例子:
  我们需要开一个面包店。已经建立好了面包订购的类(Bread):
  下面用另一个类的方法调用他。
  1. function orderBread(){
  2. $bread = new Bread();//如果说大家看了上面的设计模式,或者已经对设计模式有了一定的认识,这里肯定那个希望是建立一个抽象类或者接口。可是那样就没办法实例化了
  3. //对面包进行各样的操作
  4. $bread->prepare();
  5. $bread->bake();
  6. $bread->cut();
  7. $bread->box();
  8. }
复制代码
这里本身没有任何错误。但是,如果我要更多的面包呢?只能由我们来决定面包的种类,然后叫这个方法来制造面包了
  1. function orderBread($_bread){
  2.      if($_bread == "BlackBread"){
  3.       $this->break = new BlackBread();
  4.      }else if ($_break == "WhiteBread"){
  5.       $this->break = new WhiteBread();
  6.      }else if($_break == "SweetBread"){
  7.        $this->break = new SweetBread();
  8.     }else ($_break == "NutsBread"){
  9.        $this->break = new NutsBread();
  10.     }

  11. //对面包进行各样的操作
  12. $bread->prepare();
  13. $bread->bake();
  14. $bread->cut();
  15. $bread->box();
  16. }
复制代码
手写一堆代码。。真麻烦
  恩,这样就完成了。如果以后要改呢?要增加面包,或者减少面包种类呢?是不是必须得打开这个文件来修改他。删删改改的。
  这样就找到问题结症了。问题不在new,而在这一堆的改变上面。他们随时会改变的。你就得随时的去改变这些文件。
  那就把它拿出来。
  1. if($_bread == "BlackBread"){

  2. $this->break = new BlackBread();

  3. }else if ($_break == "WhiteBread"){

  4. $this->break = new WhiteBread();

  5. }else if($_break == "SweetBread"){

  6. $this->break = new SweetBread();

  7. }else ($_break == "NutsBread"){

  8. $this->break = new NutsBread();

  9. }
复制代码
这一块改变拿出来,放到另一个类里面。这样方法orderBread就不用关心是要定什么面包了。他只要知道调用它的时候需要制作一块面包。
这就是工厂!用工厂来建立对象!
先从工厂入手:
  1. <?php
  2. require("Bread.php");
  3. class BreadFactory{
  4. private $bread;
  5. //通过这个creatBread方法创建我们的面包
  6. public function creatBread($_bread){
  7.   if(is_string($_bread)){
  8.    //类型判断是必须的。php是弱类型语言。
  9.        if($_bread == "BlackBread"){
  10.         $this->bread = new BlackBread();
  11.        }elseif ($_bread == "WhiteBread"){
  12.         $this->bread = new WhiteBread();
  13.        }elseif($_bread == "SweetBread"){
  14.          $this->bread = new SweetBread();
  15.       }elseif($_bread == "NutsBread"){
  16.          $this->bread = new NutsBread();
  17.       }else{
  18.        echo "本工厂不提供这款面包";  
  19.       }
  20.       //以上没有任何变化。将他从原来的面包点的代码中拿出来
  21.   }
  22.   return $this->bread;
  23. }
  24. }
  25. ?>
复制代码
然后是面包种类
  1. <?php
  2. /**
  3. * 这里存放很多很多种类的面包类。共用抽象类,Bread
  4. */
  5. abstract class Bread {
  6. abstract  function prepare();
  7. abstract function bake();
  8. abstract function cut();
  9. abstract function box();
  10. }

  11. class BlackBread extends Bread {
  12. public function prepare(){
  13.   echo "准备好黑面包</br>";
  14. }

  15. public function bake(){
  16.   echo "烘烤黑面包</br>";
  17. }
  18. public function cut(){
  19.   echo "切片黑面包</br>";
  20. }
  21. public function box(){
  22.   echo "黑面包已经打包好了</br>";
  23. }
  24. }

  25. class  WhiteBread extends Bread{
  26. public function prepare(){
  27.   echo "准备好白面包</br>";
  28. }

  29. public function bake(){
  30.   echo "烘烤白面包</br>";
  31. }
  32. public function cut(){
  33.   echo "切片白面包</br>";
  34. }
  35. public function box(){
  36.   echo "白面包已经打包好了</br>";
  37. }
  38. }


  39. class SweetBread extends Bread{
  40. public function prepare(){
  41.   echo "准备好甜面包</br>";
  42. }

  43. public function bake(){
  44.   echo "烘烤甜面包</br>";
  45. }
  46. public function cut(){
  47.   echo "切片甜面包</br>";
  48. }
  49. public function box(){
  50.   echo "甜面包已经打包好了</br>";
  51. }
  52. }

  53. class NutsBread extends Bread{
  54. public function prepare(){
  55.   echo "准备好果仁面包</br>";
  56. }

  57. public function bake(){
  58.   echo "烘烤果仁面包</br>";
  59. }
  60. public function cut(){
  61.   echo "切片果仁面包</br>";
  62. }
  63. public function box(){
  64.   echo "果仁面包已经打包好了</br>";
  65. }
  66. }
  67. ?>

  68. 面包和工厂准备好了。最后开店

  69. /**
  70. * 面包店类。开店咯
  71. */
  72. require("BreadFactory.php");
  73. class BreadStore{
  74. private $bread;
  75. //通过构造函数来创建面包.你开店必须得找到源头啊。所以得定义工厂是谁
  76. public function __construct(BreadFactory $_bread){
  77.   $this->bread = $_bread;
  78. }

  79. public function orderBread($_type){
  80.   $orderBread = $this->bread->creatBread($_type);
  81.    
  82.   $orderBread->prepare();
  83.    $orderBread->bake();
  84.    $orderBread->cut();
  85.    $orderBread->box();
  86. }
  87. }
复制代码
最后测试一下
  1. $a=new BreadFactory();
  2. $b=new BreadStore($a);
  3. //测试一下.我先订购一款黑面包
  4. $b->orderBread("BlackBread");
  5. //再来个白面包如何
  6. $b->orderBread("WhiteBread");
复制代码
结果是



准备好黑面包
烘烤黑面包
切片黑面包
黑面包已经打包好了


准备好白面包
烘烤白面包
切片白面包
白面包已经打包好了
是不是很简单,思路也非常清晰~
注意,我在Bread文件里面让所有的面包都继承了个抽象类,单是在工厂里却没使用这个抽象类。实际上,所有的变量前面你应该理解成Bread的类型。
这个其实是简单工厂。并不能叫做完全的模式。但是我个人在很多的教程里面都看到将这样的过程称为工厂模式。事实上,这只是个编程习惯,但经常使用。所以还是能将其归纳进工厂模式的。

下面来看看我的简单面包点类图是如何的



再次提醒!这里所说的实现接口并不是单指:写一个类,用implements关键字来实现一个接口。而是泛指实现某个超类型(可以是一个类也可以是具体接口)的某个方法
现在我们又有了新的改变。面包店生意太好了。有一些其他省市的面包店希望加盟我们。
好像问题不是很大,用上面的简单工厂方式。那就每个工厂一个工厂类。HNBreadFactory,GDBreadFactory。
这样并没多大问题。但是我们面包店生意好就是因为我们的一些质量有保证。如果我希望多点质量控制呢?都用同样的揉面,切面,装箱。流程都是一模一样的。如何操作?下面就用工厂方法来完成这个工作。看看他是如何工作的。
  先将creatBread方法从工厂类里面移出来,并放入我们原先的面包店。但他只知道建立面包。具体建立什么面包他并不知道。需要完成具体面包的是在他的子类里,也就是说将他抽象话。不仅抽象整个Bread类,也要抽象这个creatBread方法。
下面是代码
  1. <?php
  2. /**
  3. * 面包店类。开店咯
  4. */
  5. abstract class BreadStore{
  6. private $bread;

  7. public function orderBread($_type){
  8.   //调用自身的抽象方法
  9.   $orderBread = $this->creatBread($_type);
  10.    
  11.   $orderBread->prepare();
  12.    $orderBread->bake();
  13.    $orderBread->cut();
  14.    $orderBread->box();
  15. }
  16. abstract function creatBread($_type);
  17. }
  18. ?>

  19. 这样做的好处是抽象的类BreadStore并不能决定建立什么样的面包。当然,我这里说的由子类建立面包,并不是他自身在运行时决定。而是在下面的子类来决定他应该如何提供面包。你从这个代码里面只能理解到。
  20.   $orderBread = $this->creatBread($_type);
  21. 订购面包。
  22. 订购什么样的面包,BreadStore并不知道这个面包是什么样的。可他是抽象的,不能实例化。所以是由他的子类来重写creatBread类来决定建立什么样的面包。
  23. 看看他的子类如何做的

  24. <?php
  25. require("Bread.php");
  26. require("BreadStore.php");
  27. //在湖南开店
  28. class HNBreadStore extends BreadStore{
  29. public function creatBread($_bread){
  30.   if(is_string($_bread)){
  31.    //类型判断是必须的。php是弱类型语言。
  32.        if($_bread == "BlackBread"){
  33.         return new HNBlackBread();
  34.        }elseif ($_bread == "WhiteBread"){
  35.         return new HNiteBread();
  36.        }elseif($_bread == "SweetBread"){
  37.         return new HNSweetBread();
  38.       }elseif($_bread == "NutsBread"){
  39.         return new HNNutsBread();
  40.       }
  41.   }
  42. }
  43. }
  44. //在广东开店
  45. class GDBreadStore extends BreadStore{
  46. public function creatBread($_bread){
  47.   if(is_string($_bread)){
  48.    //类型判断是必须的。php是弱类型语言。
  49.        if($_bread == "BlackBread"){
  50.         return new GDBlackBread();
  51.        }elseif ($_bread == "WhiteBread"){
  52.         return new GDiteBread();
  53.        }elseif($_bread == "SweetBread"){
  54.         return new GDSweetBread();
  55.       }elseif($_bread == "NutsBread"){
  56.         return new GDNutsBread();
  57.       }else return null;
  58.   }
  59. }
  60. }
  61. ?>
复制代码
子类里通过重写方法来决定如何建立面包。每一个加盟店都有自己的面包对象家族。他来决定采用哪样的面包。
下面是关键的地方。因为光又面包店,却没有面包,当然是不行的。
这里相对于上面的简单工厂模式有了点改变,因为为了更好的扩展面包的种类。所以面包抽象类进行了改变,直接由他来定义好了各种质量保证流程
  1. <?php
  2. /**
  3. * 这里存放很多很多种类的面包类。共用抽象类,Bread
  4. */
  5. abstract class Bread {
  6. protected $name;
  7. protected $douch;
  8. protected $sauce;
  9. protected $toppings=array();

  10. public  function prepare(){
  11.   echo "正在准备".$this->name."</br>";
  12.   echo "揉入".$this->douch."...<br>";
  13.   echo "加入调料".$this->sauce."...<br>";
  14.   echo "加入配料...";
  15.   foreach ($this->toppings as $value){
  16.    echo " ".$value." ";
  17.   }
  18.   echo "</br>";
  19. }
  20. function bake(){
  21.   echo "烘烤10分钟<br>";
  22. }
  23. function cut(){
  24.   echo "切成圆形<br>";
  25. }
  26. function box(){
  27.   echo "放入盒子<br>";
  28. }
  29. }
  30. class HNBlackBread extends Bread {
  31. public function __construct(){
  32.   $this->name = "湖南黑面包";
  33.   $this->douch = "酵母面团";
  34.   $this->sauce = "辣椒酱";
  35.   $this->toppings = array("鸡蛋");
  36. }
  37. }
  38. class  HNWhiteBread extends Bread{
  39.   public function __construct(){
  40.   $this->name = "湖南白面包";
  41.   $this->douch = "不发酵面团";
  42.   $this->sauce = "辣椒酱";
  43.   $this->toppings = array("鸡蛋");
  44. }
  45. }

  46. class HNSweetBread extends Bread{
  47. public function __construct(){
  48.   $this->name = "湖南甜面包";
  49.   $this->douch = "酵母面团";
  50.   $this->sauce = "辣椒酱";
  51.   $this->toppings = array("鸡蛋","糖精","果酱");
  52. }
  53. }
  54. class HNNutsBread extends Bread {
  55.   public function __construct(){
  56.   $this->name = "湖南果仁面包";
  57.   $this->douch = "酵母面团";
  58.   $this->sauce = "辣椒酱";
  59.   $this->toppings = array("鸡蛋","开心果");
  60. }
  61. }

  62. class GDBlackBread extends Bread {
  63. public function __construct(){
  64.   $this->name = "广东黑面包";
  65.   $this->douch = "酵母面团";
  66.   $this->sauce = "糖精";
  67.   $this->toppings = array("鸡蛋");
  68. }
  69. }
  70. class  GDWhiteBread extends Bread{
  71.   public function __construct(){
  72.   $this->name = "广东白面包";
  73.   $this->douch = "不发酵面团";
  74.   $this->sauce = "糖精";
  75.   $this->toppings = array("鸡蛋");
  76. }
  77. }

  78. class GDSweetBread extends Bread{
  79. public function __construct(){
  80.   $this->name = "广东甜面包";
  81.   $this->douch = "酵母面团";
  82.   $this->sauce = "糖精";
  83.   $this->toppings = array("鸡蛋","糖精","果酱");
  84. }
  85. }
  86. class GDNutsBread extends Bread {
  87.   public function __construct(){
  88.   $this->name = "广东果仁面包";
  89.   $this->douch = "酵母面团";
  90.   $this->sauce = "糖精";
  91.   $this->toppings = array("鸡蛋","开心果");
  92. }
  93. }
  94. ?>
复制代码
这样整个工作就完成了。好吧,订购两个不同地区的面包来尝一下吧。
  1. $b =new GDBreadStore();
  2. $b->orderBread("BlackBread");
  3. $b =new HNBreadStore();
  4. $b->orderBread("BlackBread");
复制代码
如果你将两个简单商店类的creaderBread设立成静态的那就更简单,大家可以自己测试一下。
下面是显示结果:


正在准备广东黑面包
揉入酵母面团...
加入调料糖精...
加入配料... 鸡蛋
烘烤10分钟
切成圆形
放入盒子

正在准备湖南黑面包
揉入酵母面团...
加入调料辣椒酱...
加入配料... 鸡蛋
烘烤10分钟
切成圆形
放入盒子
是不是很简单。再回头来看看测试代码
  1. $b =new GDBreadStore();
  2. $b->orderBread("BlackBread");
  3. $b =new HNBreadStore();
  4. $b->orderBread("BlackBread");
复制代码
我建立两个店,然后订购。非常的复合我们自身在生活中的习惯。

作者: 听老歌   发布时间: 2011-04-08

好长啊, 不是自己打上去的吧, 《headfirst design pattern》上有现成的,一样,我也觉得有时用一些模式会比较好。前段公司写的一个项目,我们负责人不会软件设计,只会盲目实现功能,好像是很快,像瀑布一样的紧藕合让我好痛苦,特别是要改动时。。。。。维护真的是恶梦啊,,

作者: hbeimf   发布时间: 2011-04-08

热门下载

更多