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

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

时间:2011-04-08

来源:互联网

ok。如果上面的都明白了。那你也明白了什么叫做工厂方法模式。
他和简单工厂很相似。所以很多人混为一谈!但他是通过子类来决定建立什么样的面包。而简单工厂是有其他的类来确定建立什么样的面包。
下面看看我们的面包加盟店的类图。可能这个关系更清楚一点:

解释都写在图上。就不啰嗦了=。=
定义我们的工厂方法模式:

定义一个创建对象的接口,但是具体对象有它的子类来决定。也就是工厂方法将对象实例化推迟到了子类
具体的工厂方法的类图

工厂方法让具体的对象解脱了出来。并不再依赖具体的类。而是抽象。
它正式是复合OOP设计中非常重要的一个原则:
依赖倒置原则(经常听到,却不明白到底什么意思)
定义:

依赖抽象编程。而不要依赖具体编程
看起来这个原则和上面的
针对接口编程,不针对实现编程
这个原则非常的相似。但是有本质区别的
他更注重的是依赖抽象。
就像上面的面包店。本身面包店BreadStore他不能决定生产什么样的面包,他只能依赖他的底层子类来决定。
面包本身(各种面包)也不能依赖自身去决定怎么样去切片怎么样去装箱。只能依赖他的抽象类Bread。
这个原则说明了:不要让高层组件依赖于底层组件。而是不管高还是底,都应该依赖抽象对象。
换句话说。如果这个例子里所有的面包对象都是依赖面包这个具体类而建立的,就是违反了这个原则,就导致了解耦的失败。
依赖倒置原则到底倒置了哪里呢?
先想想看,你要开一个面包店会怎么考虑?
A:为了能够烘烤,切片,装到盒子里给客户,我必须先准备好不同种类的面包。黑的白的,甜的或者果仁的。甚至更多。
这样是没错的,但是这样就依赖各种面包的具体类。也就是这是从高层开始依赖。而违反了这个原则。如果我倒过来呢?
A:先提供一个抽象接口,他告诉我可以提供这些面包,而我不用担心会如何制作。
很好。这样就不用理会那些具体的面包类了。剩下的就是如何去开面包店了。思维~~~倒过来了。从底层开始往上面思考。并且依赖抽象。而不是具体的类了。
下面几个原则来告诉你如何做到尽量不要违反这个原则:切记!是尽量不要违反。而不是任何地方都不违反。这和OO设计原则一个道理的!不可能完全遵循OO设计原则而增加自己的工作两。设计原则和设计模式只是为了解决问题而存在,而不是产生问题。如果你连建立一个字符对象都用到此原则说明你有问题了。你应该思考一下这个对象将来是否会发生什么改变,如果不会发生改变,不遵循任何原则也没什么影响。

变量不可以持有具体的引用!
   (用new关键词就是在次优具体的引用了。尽量用工厂模式中的一种避免它)
不要让类派生自具体类
  (如果派生自具体类,你就会依赖具体类。请派生自一个抽象)
不要覆盖基类中已经实现的方法
  (如果覆盖基类已经实现的方法,那么你的基类就不是一个真正适合被继承的抽象。基类中已实现的方法,应该由所有的子类共享)
你不可能完全遵守这些方针。你应该尽量达到这个原则。而不是随时都遵守这个原则。自己慢慢体会吧=。=
让我们进入真正的抽象工厂模式吧。
现在我们需要进行原料控制,因为面包加盟店有些可能会偷工减料。所以我们给其指定原料工厂提供原料。
先准备一下原料工厂
  1. interface MaterialFactory{
  2. public function creatDouch();
  3. public function creatSauce();
  4. public function creatToppings();
  5. }
  6. /**
  7. * 各个地区的原料工厂
  8. */
  9. class HNMaterialFactory implements MaterialFactory{
  10. public function creatDouch(){
  11.   return new  FermentDouch();
  12. }
  13. public function creatSauce(){
  14.   return new ChiliSauce();
  15. }
  16. public function creatToppings(){
  17.   return new setToppings(new Egg(),new NewChiliSauce(),new Jam());
  18. }
  19. }
  20. class GDMaterialFactory implements MaterialFactory{
  21. public function creatDouch(){
  22.   return new  NotFermentDouch();
  23. }
  24. public function creatSauce(){
  25.   return new Saccharin();
  26. }
  27. public function creatToppings(){
  28.   return new setToppings(new Egg(),new NewSaccharin(),new Jam());//作为示例,将这里写死也没什么影响
  29. }
  30. }

  31. 上面的原料工厂里也看到了。每个原料都来至不同的原料对象。下面是对原料进行简单的定义

  32. <?php
  33. /**
  34. * 存储各种各样的原材料,当然,每种原料都是依赖的接口
  35. */
  36. /**
  37. * 各种原材料的接口
  38. *
  39. */
  40. interface Douch{
  41. public function getDouch();
  42. }
  43. interface Sauce{
  44. public function getSauce();
  45. }
  46. interface Toppings{
  47. public function getToppings();
  48. }
  49. //面粉团集群
  50. class FermentDouch implements Douch {
  51. protected $douch;
  52. public function __construct(){
  53.   $this->douch = "发酵面包";
  54. }
  55. public function getDouch(){
  56.   return $this->douch;
  57. }
  58. }
  59. class NotFermentDouch implements Douch {
  60. protected $douch;
  61. public function __construct(){
  62.   $this->douch = "不发酵面包";
  63. }
  64. public function getDouch(){
  65.   return $this->douch;
  66. }
  67. }
  68. //酱汁集群
  69. class ChiliSauce implements Sauce {
  70. protected $sauce;
  71. public function __construct(){
  72.   $this->sauce = "辣椒酱";
  73. }
  74. public function getSauce(){
  75.   return $this->sauce;
  76. }
  77. }
  78. class Saccharin implements Sauce{
  79. protected $sauce;
  80. public function __construct(){
  81.   $this->sauce = "果酱";
  82. }
  83. public function getSauce(){
  84.   return $this->sauce;
  85. }
  86. }

  87. //作料集群
  88. class setToppings implements Toppings {
  89. protected $toppings;
  90. public function __construct(Toppings $_egg,Toppings $_sauce,Toppings $_jam){
  91.   $this->toppings=array($_egg->getToppings(),$_sauce->getToppings(),$_jam->getToppings());
  92.   
  93. }
  94. public function getToppings(){
  95.   return implode(",",$this->toppings);
  96. }
  97. }
  98. class Egg implements Toppings {
  99. protected $name;
  100. public function __construct(){
  101.   $this->name = "鸡蛋";
  102. }
  103. public function getToppings(){
  104.   return $this->name;
  105. }
  106. }
  107. class NewChiliSauce implements Toppings {
  108. protected $name;
  109. public function __construct(){
  110.   $this->name = "苏丹红辣椒酱";//辣飞你
  111. }
  112. public function getToppings(){
  113.   return $this->name;
  114. }
  115. }
  116. class Jam implements Toppings {
  117. protected $name;
  118. public function __construct(){
  119.   $this->name = "高级果酱";
  120. }
  121. public function getToppings(){
  122.   return $this->name;
  123. }
  124. }
  125. class NewSaccharin implements Toppings {
  126. protected $name;
  127. public function __construct(){
  128.   $this->name = "加量糖精";
  129. }
  130. public function getToppings(){
  131.   return $this->name;
  132. }
  133. }
  134. ?>
复制代码
原料准备完毕。那如何将这个原料提供给具体的客户呢?
  1. //在湖南开店
  2. class HNBreadStore extends BreadStore{
  3. public function creatBread($_bread){
  4.   $material = new HNMaterialFactory();
  5.   if(is_string($_bread)){
  6.    //类型判断是必须的。php是弱类型语言。
  7.        if($_bread == "BlackBread"){
  8.         $bread = new BlackBread($material);
  9.         $bread->setName("湖南口味的黑面包");
  10.        }elseif ($_bread == "WhiteBread"){
  11.          $bread = new WiteBread($material);
  12.           $bread->setName("湖南口味的白面包");
  13.        }elseif($_bread == "SweetBread"){
  14.          $bread = new SweetBread($material);
  15.          $bread->setName("湖南口味的甜面包");
  16.       }elseif($_bread == "NutsBread"){
  17.         $bread = new NutsBread($material);
  18.         $bread->setName("湖南口味的果仁面包");
  19.       }else return null;
  20.   }
  21.   return $bread;
  22. }
  23. }
  24. //在广东开店
  25. class GDBreadStore extends BreadStore{
  26. public function creatBread($_bread){
  27.   $material = new GDMaterialFactory();
  28.   if(is_string($_bread)){
  29.    //类型判断是必须的。php是弱类型语言。
  30.        if($_bread == "BlackBread"){
  31.         $bread = new BlackBread($material);
  32.         $bread->setName("广东口味的黑面包");
  33.        }elseif ($_bread == "WhiteBread"){
  34.          $bread = new WiteBread($material);
  35.           $bread->setName("广东口味的白面包");
  36.        }elseif($_bread == "SweetBread"){
  37.          $bread = new SweetBread($material);
  38.          $bread->setName("广东口味的甜面包");
  39.       }elseif($_bread == "NutsBread"){
  40.         $bread = new NutsBread($material);
  41.         $bread->setName("广东口味的果仁面包");
  42.       }else return null;
  43.   }
  44.   return $bread;
  45. }
  46. }
复制代码
重点在
  1. $material = new HNMaterialFactory();
  2. $bread = new BlackBread($material);
复制代码
这里重写了新建不同面包的对象方法。
$material就是原料工厂。告诉$bread从这个原料工厂里提取原料。这样保证完成正确的面包种类。
面包的抽象类并没有发生任何改变:
  1. /**
  2. * 面包店类。开店咯
  3. */
  4. abstract class BreadStore{
  5. private $bread;
  6. public function orderBread($_type){
  7.   //调用自身的抽象方法
  8.   $orderBread = $this->creatBread($_type);
  9.    
  10.   $orderBread->prepare();
  11.   $orderBread->toString();
  12.    $orderBread->bake();
  13.    $orderBread->cut();
  14.    $orderBread->box();
  15. }

  16. abstract function creatBread($_type);
  17. }

  18. 决定生产什么样的面包还是由各加盟店的子类来决定的。
  19. 下面是各种面包的定义

  20. class BlackBread extends Bread {
  21. protected $factory;
  22. public function __construct(MaterialFactory $_factory){
  23.   $this->factory = $_factory;
  24. }
  25. public function prepare(){
  26.   $this->name = "正在准备".$this->name;
  27.   $this->douch = $this->factory->creatDouch();
  28.   $this->sauce = $this->factory->creatSauce();
  29.   $this->toppings = $this->factory->creatToppings();
  30. }

  31. }
  32. class  WhiteBread extends Bread{
  33. public function __construct(MaterialFactory $_factory){
  34.   $this->factory = $_factory;
  35. }
  36. public function prepare(){
  37.   $this->name = "正在准备".$this->name;
  38.   $this->douch = $this->factory->creatDouch();
  39.   $this->sauce = $this->factory->creatSauce();
  40.   $this->toppings = $this->factory->creatToppings();
  41. }
  42. }

  43. class SweetBread extends Bread{
  44. public function __construct(MaterialFactory $_factory){
  45.   $this->factory = $_factory;
  46. }
  47. public function prepare(){
  48.   $this->name = "正在准备".$this->name;
  49.   $this->douch = $this->factory->creatDouch();
  50.   $this->sauce = $this->factory->creatSauce();
  51.   $this->toppings = $this->factory->creatToppings();
  52. }
  53. }
  54. class NutsBread extends Bread {
  55. public function __construct(MaterialFactory $_factory){
  56.   $this->factory = $_factory;
  57. }
  58. public function prepare(){
  59.   $this->name = "正在准备".$this->name;
  60.   $this->douch = $this->factory->creatDouch();
  61.   $this->sauce = $this->factory->creatSauce();
  62.   $this->toppings = $this->factory->creatToppings();
  63. }
  64. }
复制代码
这里和上面的工厂方法是有区别的。大家可以自己翻上去查看。
重点改变是在新建这些面包种类的实例对象的时候在构造函数中引进了原料工厂。
真正起到作用的就是这里。
  1. public function __construct(MaterialFactory $_factory){
  2.   $this->factory = $_factory;
  3. }
复制代码
准备工作中只是在准备,并不知道他准备的是什么口味的。所以弱耦合还是存在,并没破坏的。
由什么源材料决定什么样的面包。这是很自然的道理。
现在一切都有,只欠东风了。
  1. $b =new GDBreadStore();
  2. $b->orderBread("BlackBread");
  3. $b =new HNBreadStore();
  4. $b->orderBread("BlackBread");
复制代码
输出其实和上面的一样。只是工作模式不同


正在准备广东口味的黑面包
不发酵面包
果酱
鸡蛋,加量糖精,高级果酱
烘烤10分钟
切成圆形
放入盒子

正在准备湖南口味的黑面包
发酵面包
辣椒酱
鸡蛋,苏丹红辣椒酱,高级果酱
烘烤10分钟
切成圆形
放入盒子
上面的工厂方法和简单工厂理解了的这个正式的工厂模式其实就非常容易理解了。。只是方式不同而已。可能细心的朋友已经发现了。工厂方法是蕴藏在抽象工厂模式中。
通过抽象工厂所提供的接口,可以创建产品的家族。利用这个接口书写代码,我们的代码将从实际工厂解耦,以便在不同上下文中实现各种各样的工厂,制造出各种不同的产品。例如,不同的区域,不同的操作系统,不同的外观以及操作。
因为代码从实际的产品中解耦了,所以我们可以替换不同的工厂来取得不同的行为(例如,取得番茄酱料,而不是取得大蒜酱料);
好了。让我们来定义抽象工厂模式吧:

提供一个接口,用于创建相关或者依赖对象的家族,而不需要明确指定具体类。

抽象工厂允许客户使用抽象的接口来创建一组相关的产品。而不需要知道(或关心)实际产出的具体产品是什么。这样一来,客户就从具体的产品中被解耦。
类图。。。。。如下。。

画得比较乱。。。呃。。其实我已经理解了。只是不知道怎么用我这个软件话出来。。不能画折线。囧
至于为什么抽象工厂的方法经常以工厂方法的方式实现。。截一段深入潜出设计模式中的说明吧。

抽象工厂的任务是定义一个负责创建一组产品的接口。这个接口内的每个方法都负责创建一个具体的产品,同时,我们利用实现抽象工厂的子类来提供这些具体的做法。所以,在抽象工厂中利用工厂方法实现生产方法是相当自然的做法。
一下来区别工厂方法和抽象工厂之间:


工厂方法是使用类,而抽象工厂是操作的对象。
工厂方法创建对象的方法是继承,抽象工厂创建工厂的方法是组合。
抽象工厂是通过抽象方法(MaterialFactory),由这个类型的子类定义产品被产品的方法。但必须先实例化。然后将他传入一些针对抽象类型所写的代码中
工厂方法是直接通过抽象方法的子类决定如何创建产品。不需要在外部再实例化产品传入具体代码。
不管是使用工厂方法还是抽象工厂,都可以将对象创建封装起来,使应用程序解耦,并降低对特定实现的依赖。
抽象工厂是在需要创建产品家族和想让制造的相关产品集合其来的时候使用。
工厂方法在把客户代码需要实例化的具体类中解耦或者如果目前不知道将来需要实例化那些具体类的时候使用。
相对而言进过个人经验来判断是使用哪一个工厂模式。工厂方法相当于轻量级的设计模式。简单处理就可以使用他,只需要把抽象方法继承成子类并实现这个工厂方法就可以了。
抽象工厂的范围更广泛一点。

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

坐沙发

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

热门下载

更多