加工你的OO精华 工厂模式一
时间:2011-04-08
来源:互联网
加工你的OO精华 工厂模式
上几章都大致了解了在OOP设计中,应该针对抽象编程而不是具体实现编程。
但是上面几章的代码中或多或少都又"new"来创建对象的实例。那么在这些地方,就不是针对抽象编程,而成了具体实现的编程。
但使用"new" 有错吗?从本质上讲是没错的,因为这是OOP的基础。但是,从另一个角度去说,他是错误的。但错不在他。而在程序上面。
简单的说,就是我们使用了new关键词将代码的执行硬编码进了程序之中。他不能在程序运行时来决定运行哪一个。也就是说,当我们希望一个项目需要改变的时候,需要添加新的对象的时候,需要打开文件进去修改
第三节的装饰模式重点讲过一个原则:对修改关闭,对扩展打开。但是,频繁使用new这个创建实例的办法就是在破坏这个原则。因为你总是会要在不久的将来改变他的。只要你要添加新的对象。你必定要修改源文件。
那错误具体是由什么导致的?第一章我说过,OO编程的基本原理不是那些原则,而是Change!改变。一切都是因为改变捣的鬼。
看看下面这个例子:
我们需要开一个面包店。已经建立好了面包订购的类(Bread):
下面用另一个类的方法调用他。
复制代码
这里本身没有任何错误。但是,如果我要更多的面包呢?只能由我们来决定面包的种类,然后叫这个方法来制造面包了
复制代码
手写一堆代码。。真麻烦
恩,这样就完成了。如果以后要改呢?要增加面包,或者减少面包种类呢?是不是必须得打开这个文件来修改他。删删改改的。
这样就找到问题结症了。问题不在new,而在这一堆的改变上面。他们随时会改变的。你就得随时的去改变这些文件。
那就把它拿出来。
复制代码
这一块改变拿出来,放到另一个类里面。这样方法orderBread就不用关心是要定什么面包了。他只要知道调用它的时候需要制作一块面包。
这就是工厂!用工厂来建立对象!
先从工厂入手:
复制代码
然后是面包种类
复制代码
最后测试一下
复制代码
结果是
准备好黑面包
烘烤黑面包
切片黑面包
黑面包已经打包好了
准备好白面包
烘烤白面包
切片白面包
白面包已经打包好了
是不是很简单,思路也非常清晰~
注意,我在Bread文件里面让所有的面包都继承了个抽象类,单是在工厂里却没使用这个抽象类。实际上,所有的变量前面你应该理解成Bread的类型。
这个其实是简单工厂。并不能叫做完全的模式。但是我个人在很多的教程里面都看到将这样的过程称为工厂模式。事实上,这只是个编程习惯,但经常使用。所以还是能将其归纳进工厂模式的。
下面来看看我的简单面包点类图是如何的
再次提醒!这里所说的实现接口并不是单指:写一个类,用implements关键字来实现一个接口。而是泛指实现某个超类型(可以是一个类也可以是具体接口)的某个方法
现在我们又有了新的改变。面包店生意太好了。有一些其他省市的面包店希望加盟我们。
好像问题不是很大,用上面的简单工厂方式。那就每个工厂一个工厂类。HNBreadFactory,GDBreadFactory。
这样并没多大问题。但是我们面包店生意好就是因为我们的一些质量有保证。如果我希望多点质量控制呢?都用同样的揉面,切面,装箱。流程都是一模一样的。如何操作?下面就用工厂方法来完成这个工作。看看他是如何工作的。
先将creatBread方法从工厂类里面移出来,并放入我们原先的面包店。但他只知道建立面包。具体建立什么面包他并不知道。需要完成具体面包的是在他的子类里,也就是说将他抽象话。不仅抽象整个Bread类,也要抽象这个creatBread方法。
下面是代码
复制代码
子类里通过重写方法来决定如何建立面包。每一个加盟店都有自己的面包对象家族。他来决定采用哪样的面包。
下面是关键的地方。因为光又面包店,却没有面包,当然是不行的。
这里相对于上面的简单工厂模式有了点改变,因为为了更好的扩展面包的种类。所以面包抽象类进行了改变,直接由他来定义好了各种质量保证流程
复制代码
这样整个工作就完成了。好吧,订购两个不同地区的面包来尝一下吧。
复制代码
如果你将两个简单商店类的creaderBread设立成静态的那就更简单,大家可以自己测试一下。
下面是显示结果:
正在准备广东黑面包
揉入酵母面团...
加入调料糖精...
加入配料... 鸡蛋
烘烤10分钟
切成圆形
放入盒子
正在准备湖南黑面包
揉入酵母面团...
加入调料辣椒酱...
加入配料... 鸡蛋
烘烤10分钟
切成圆形
放入盒子
是不是很简单。再回头来看看测试代码
复制代码
我建立两个店,然后订购。非常的复合我们自身在生活中的习惯。
上几章都大致了解了在OOP设计中,应该针对抽象编程而不是具体实现编程。
但是上面几章的代码中或多或少都又"new"来创建对象的实例。那么在这些地方,就不是针对抽象编程,而成了具体实现的编程。
但使用"new" 有错吗?从本质上讲是没错的,因为这是OOP的基础。但是,从另一个角度去说,他是错误的。但错不在他。而在程序上面。
简单的说,就是我们使用了new关键词将代码的执行硬编码进了程序之中。他不能在程序运行时来决定运行哪一个。也就是说,当我们希望一个项目需要改变的时候,需要添加新的对象的时候,需要打开文件进去修改
第三节的装饰模式重点讲过一个原则:对修改关闭,对扩展打开。但是,频繁使用new这个创建实例的办法就是在破坏这个原则。因为你总是会要在不久的将来改变他的。只要你要添加新的对象。你必定要修改源文件。
那错误具体是由什么导致的?第一章我说过,OO编程的基本原理不是那些原则,而是Change!改变。一切都是因为改变捣的鬼。
看看下面这个例子:
我们需要开一个面包店。已经建立好了面包订购的类(Bread):
下面用另一个类的方法调用他。
- function orderBread(){
- $bread = new Bread();//如果说大家看了上面的设计模式,或者已经对设计模式有了一定的认识,这里肯定那个希望是建立一个抽象类或者接口。可是那样就没办法实例化了
- //对面包进行各样的操作
- $bread->prepare();
- $bread->bake();
- $bread->cut();
- $bread->box();
- }
- function orderBread($_bread){
- if($_bread == "BlackBread"){
- $this->break = new BlackBread();
- }else if ($_break == "WhiteBread"){
- $this->break = new WhiteBread();
- }else if($_break == "SweetBread"){
- $this->break = new SweetBread();
- }else ($_break == "NutsBread"){
- $this->break = new NutsBread();
- }
-
- //对面包进行各样的操作
- $bread->prepare();
- $bread->bake();
- $bread->cut();
- $bread->box();
- }
恩,这样就完成了。如果以后要改呢?要增加面包,或者减少面包种类呢?是不是必须得打开这个文件来修改他。删删改改的。
这样就找到问题结症了。问题不在new,而在这一堆的改变上面。他们随时会改变的。你就得随时的去改变这些文件。
那就把它拿出来。
- if($_bread == "BlackBread"){
-
- $this->break = new BlackBread();
-
- }else if ($_break == "WhiteBread"){
-
- $this->break = new WhiteBread();
-
- }else if($_break == "SweetBread"){
-
- $this->break = new SweetBread();
-
- }else ($_break == "NutsBread"){
-
- $this->break = new NutsBread();
-
- }
这就是工厂!用工厂来建立对象!
先从工厂入手:
- <?php
- require("Bread.php");
- class BreadFactory{
- private $bread;
- //通过这个creatBread方法创建我们的面包
- public function creatBread($_bread){
- if(is_string($_bread)){
- //类型判断是必须的。php是弱类型语言。
- if($_bread == "BlackBread"){
- $this->bread = new BlackBread();
- }elseif ($_bread == "WhiteBread"){
- $this->bread = new WhiteBread();
- }elseif($_bread == "SweetBread"){
- $this->bread = new SweetBread();
- }elseif($_bread == "NutsBread"){
- $this->bread = new NutsBread();
- }else{
- echo "本工厂不提供这款面包";
- }
- //以上没有任何变化。将他从原来的面包点的代码中拿出来
- }
- return $this->bread;
- }
- }
- ?>
- <?php
- /**
- * 这里存放很多很多种类的面包类。共用抽象类,Bread
- */
- abstract class Bread {
- abstract function prepare();
- abstract function bake();
- abstract function cut();
- abstract function box();
- }
-
- class BlackBread extends Bread {
- public function prepare(){
- echo "准备好黑面包</br>";
- }
-
- public function bake(){
- echo "烘烤黑面包</br>";
- }
- public function cut(){
- echo "切片黑面包</br>";
- }
- public function box(){
- echo "黑面包已经打包好了</br>";
- }
- }
-
- class WhiteBread extends Bread{
- public function prepare(){
- echo "准备好白面包</br>";
- }
-
- public function bake(){
- echo "烘烤白面包</br>";
- }
- public function cut(){
- echo "切片白面包</br>";
- }
- public function box(){
- echo "白面包已经打包好了</br>";
- }
- }
-
-
- class SweetBread extends Bread{
- public function prepare(){
- echo "准备好甜面包</br>";
- }
-
- public function bake(){
- echo "烘烤甜面包</br>";
- }
- public function cut(){
- echo "切片甜面包</br>";
- }
- public function box(){
- echo "甜面包已经打包好了</br>";
- }
- }
-
- class NutsBread extends Bread{
- public function prepare(){
- echo "准备好果仁面包</br>";
- }
-
- public function bake(){
- echo "烘烤果仁面包</br>";
- }
- public function cut(){
- echo "切片果仁面包</br>";
- }
- public function box(){
- echo "果仁面包已经打包好了</br>";
- }
- }
- ?>
-
- 面包和工厂准备好了。最后开店
-
- /**
- * 面包店类。开店咯
- */
- require("BreadFactory.php");
- class BreadStore{
- private $bread;
- //通过构造函数来创建面包.你开店必须得找到源头啊。所以得定义工厂是谁
- public function __construct(BreadFactory $_bread){
- $this->bread = $_bread;
- }
-
- public function orderBread($_type){
- $orderBread = $this->bread->creatBread($_type);
-
- $orderBread->prepare();
- $orderBread->bake();
- $orderBread->cut();
- $orderBread->box();
- }
- }
- $a=new BreadFactory();
- $b=new BreadStore($a);
- //测试一下.我先订购一款黑面包
- $b->orderBread("BlackBread");
- //再来个白面包如何
- $b->orderBread("WhiteBread");
准备好黑面包
烘烤黑面包
切片黑面包
黑面包已经打包好了
准备好白面包
烘烤白面包
切片白面包
白面包已经打包好了
是不是很简单,思路也非常清晰~
注意,我在Bread文件里面让所有的面包都继承了个抽象类,单是在工厂里却没使用这个抽象类。实际上,所有的变量前面你应该理解成Bread的类型。
这个其实是简单工厂。并不能叫做完全的模式。但是我个人在很多的教程里面都看到将这样的过程称为工厂模式。事实上,这只是个编程习惯,但经常使用。所以还是能将其归纳进工厂模式的。
下面来看看我的简单面包点类图是如何的
再次提醒!这里所说的实现接口并不是单指:写一个类,用implements关键字来实现一个接口。而是泛指实现某个超类型(可以是一个类也可以是具体接口)的某个方法
现在我们又有了新的改变。面包店生意太好了。有一些其他省市的面包店希望加盟我们。
好像问题不是很大,用上面的简单工厂方式。那就每个工厂一个工厂类。HNBreadFactory,GDBreadFactory。
这样并没多大问题。但是我们面包店生意好就是因为我们的一些质量有保证。如果我希望多点质量控制呢?都用同样的揉面,切面,装箱。流程都是一模一样的。如何操作?下面就用工厂方法来完成这个工作。看看他是如何工作的。
先将creatBread方法从工厂类里面移出来,并放入我们原先的面包店。但他只知道建立面包。具体建立什么面包他并不知道。需要完成具体面包的是在他的子类里,也就是说将他抽象话。不仅抽象整个Bread类,也要抽象这个creatBread方法。
下面是代码
- <?php
- /**
- * 面包店类。开店咯
- */
- abstract class BreadStore{
- private $bread;
-
- public function orderBread($_type){
- //调用自身的抽象方法
- $orderBread = $this->creatBread($_type);
-
- $orderBread->prepare();
- $orderBread->bake();
- $orderBread->cut();
- $orderBread->box();
- }
- abstract function creatBread($_type);
- }
- ?>
-
- 这样做的好处是抽象的类BreadStore并不能决定建立什么样的面包。当然,我这里说的由子类建立面包,并不是他自身在运行时决定。而是在下面的子类来决定他应该如何提供面包。你从这个代码里面只能理解到。
- $orderBread = $this->creatBread($_type);
- 订购面包。
- 订购什么样的面包,BreadStore并不知道这个面包是什么样的。可他是抽象的,不能实例化。所以是由他的子类来重写creatBread类来决定建立什么样的面包。
- 看看他的子类如何做的
-
- <?php
- require("Bread.php");
- require("BreadStore.php");
- //在湖南开店
- class HNBreadStore extends BreadStore{
- public function creatBread($_bread){
- if(is_string($_bread)){
- //类型判断是必须的。php是弱类型语言。
- if($_bread == "BlackBread"){
- return new HNBlackBread();
- }elseif ($_bread == "WhiteBread"){
- return new HNiteBread();
- }elseif($_bread == "SweetBread"){
- return new HNSweetBread();
- }elseif($_bread == "NutsBread"){
- return new HNNutsBread();
- }
- }
- }
- }
- //在广东开店
- class GDBreadStore extends BreadStore{
- public function creatBread($_bread){
- if(is_string($_bread)){
- //类型判断是必须的。php是弱类型语言。
- if($_bread == "BlackBread"){
- return new GDBlackBread();
- }elseif ($_bread == "WhiteBread"){
- return new GDiteBread();
- }elseif($_bread == "SweetBread"){
- return new GDSweetBread();
- }elseif($_bread == "NutsBread"){
- return new GDNutsBread();
- }else return null;
- }
- }
- }
- ?>
下面是关键的地方。因为光又面包店,却没有面包,当然是不行的。
这里相对于上面的简单工厂模式有了点改变,因为为了更好的扩展面包的种类。所以面包抽象类进行了改变,直接由他来定义好了各种质量保证流程
- <?php
- /**
- * 这里存放很多很多种类的面包类。共用抽象类,Bread
- */
- abstract class Bread {
- protected $name;
- protected $douch;
- protected $sauce;
- protected $toppings=array();
-
- public function prepare(){
- echo "正在准备".$this->name."</br>";
- echo "揉入".$this->douch."...<br>";
- echo "加入调料".$this->sauce."...<br>";
- echo "加入配料...";
- foreach ($this->toppings as $value){
- echo " ".$value." ";
- }
- echo "</br>";
- }
- function bake(){
- echo "烘烤10分钟<br>";
- }
- function cut(){
- echo "切成圆形<br>";
- }
- function box(){
- echo "放入盒子<br>";
- }
- }
- class HNBlackBread extends Bread {
- public function __construct(){
- $this->name = "湖南黑面包";
- $this->douch = "酵母面团";
- $this->sauce = "辣椒酱";
- $this->toppings = array("鸡蛋");
- }
- }
- class HNWhiteBread extends Bread{
- public function __construct(){
- $this->name = "湖南白面包";
- $this->douch = "不发酵面团";
- $this->sauce = "辣椒酱";
- $this->toppings = array("鸡蛋");
- }
- }
-
- class HNSweetBread extends Bread{
- public function __construct(){
- $this->name = "湖南甜面包";
- $this->douch = "酵母面团";
- $this->sauce = "辣椒酱";
- $this->toppings = array("鸡蛋","糖精","果酱");
- }
- }
- class HNNutsBread extends Bread {
- public function __construct(){
- $this->name = "湖南果仁面包";
- $this->douch = "酵母面团";
- $this->sauce = "辣椒酱";
- $this->toppings = array("鸡蛋","开心果");
- }
- }
-
- class GDBlackBread extends Bread {
- public function __construct(){
- $this->name = "广东黑面包";
- $this->douch = "酵母面团";
- $this->sauce = "糖精";
- $this->toppings = array("鸡蛋");
- }
- }
- class GDWhiteBread extends Bread{
- public function __construct(){
- $this->name = "广东白面包";
- $this->douch = "不发酵面团";
- $this->sauce = "糖精";
- $this->toppings = array("鸡蛋");
- }
- }
-
- class GDSweetBread extends Bread{
- public function __construct(){
- $this->name = "广东甜面包";
- $this->douch = "酵母面团";
- $this->sauce = "糖精";
- $this->toppings = array("鸡蛋","糖精","果酱");
- }
- }
- class GDNutsBread extends Bread {
- public function __construct(){
- $this->name = "广东果仁面包";
- $this->douch = "酵母面团";
- $this->sauce = "糖精";
- $this->toppings = array("鸡蛋","开心果");
- }
- }
- ?>
- $b =new GDBreadStore();
- $b->orderBread("BlackBread");
- $b =new HNBreadStore();
- $b->orderBread("BlackBread");
下面是显示结果:
正在准备广东黑面包
揉入酵母面团...
加入调料糖精...
加入配料... 鸡蛋
烘烤10分钟
切成圆形
放入盒子
正在准备湖南黑面包
揉入酵母面团...
加入调料辣椒酱...
加入配料... 鸡蛋
烘烤10分钟
切成圆形
放入盒子
是不是很简单。再回头来看看测试代码
- $b =new GDBreadStore();
- $b->orderBread("BlackBread");
- $b =new HNBreadStore();
- $b->orderBread("BlackBread");
作者: 听老歌 发布时间: 2011-04-08
好长啊, 不是自己打上去的吧, 《headfirst design pattern》上有现成的,一样,我也觉得有时用一些模式会比较好。前段公司写的一个项目,我们负责人不会软件设计,只会盲目实现功能,好像是很快,像瀑布一样的紧藕合让我好痛苦,特别是要改动时。。。。。维护真的是恶梦啊,,
作者: hbeimf 发布时间: 2011-04-08
相关阅读 更多
热门阅读
-
office 2019专业增强版最新2021版激活秘钥/序列号/激活码推荐 附激活工具
阅读:74
-
如何安装mysql8.0
阅读:31
-
Word快速设置标题样式步骤详解
阅读:28
-
20+道必知必会的Vue面试题(附答案解析)
阅读:37
-
HTML如何制作表单
阅读:22
-
百词斩可以改天数吗?当然可以,4个步骤轻松修改天数!
阅读:31
-
ET文件格式和XLS格式文件之间如何转化?
阅读:24
-
react和vue的区别及优缺点是什么
阅读:121
-
支付宝人脸识别如何关闭?
阅读:21
-
腾讯微云怎么修改照片或视频备份路径?
阅读:28