《PHP设计模式》之 工厂模式

PHP工厂模式就是用一个工厂方法来替换掉直接new对象的操作。
在传统习惯中,如果要生成一个类的话,在代码中直接new一个对象,比如:

//普通封装方式
class Calc{
    /**
     * 计算结果
     *
     * @param int|float $num1
     * @param int|float $num2
     * @param string $operator
     * @return int|float
     */
    public function calculate($num1,$num2,$operator){
        try {
            $result=0;
            switch ($operator){
                case '+':
                    $result= $num1+$num2;
                    break;
                case '-':
                    $result= $num1-$num2;
                    break;
                case '*':
                    $result= $num1*$num2;
                    break;
                case '/':
                    if ($num2==0) {
                        throw new Exception("除数不能为0");
                    }
                    $result= $num1/$num2;
                    break;
            }
         return $result;
        }catch (Exception $e){
            echo "您输入有误:".$e->getMessage();
        }
    }
}
$test=new Calc();
//echo $test->calculate(2,3,'+');//打印:5
echo $test->calculate(5,0,'/');//打印:您输入有误:除数不能为0

优点:以上代码使用了面向对象的封装特性,只要有了include这个类,其他页面就可以随便使用了
缺点:无法灵活的扩展和维护
比如:想要增加一个“求余”运算,需要在switch语句块中添加一个分支语句,代码需要做如下改动

//添加分支语句
class Calc{
    public function calculate($num1,$num2,$operator){
        try {
            $result=0;
            switch ($operator){
                //......省略......
                case '%':
                    $result= $num1%$num2;
                    break;
                //......省略......
            }
        }catch (Exception $e){
            echo "您输入有误:".$e->getMessage();
        }
    }
}

代码分析:用以上方法实现给计算器添加新的功能运算有以下几个缺点
①需要改动原有的代码块,可能会在为了“添加新功能”而改动原有代码的时候,不小心将原有的代码改错了
②如果要添加的功能很多,比如:‘乘方’,‘开方’,‘对数’,‘三角函数’,‘统计’,或者添加一些程序员专用的计算功能,比如:And, Or, Not, Xor,这样就需要在switch语句中添加N个分支语句。想象下,一个计算功能的函数如果有二三十个case分支语句,代码将超过一屏,不仅令代码的可读性大大降低,关键是,为了添加小功能,还得让其余不相关都参与解释,这令程序的执行效率大大降低

解决途径:采用OOP的继承和多态思想

/**
 * 操作类简单工厂模式的初步实现
 * 因为包含有抽象方法,所以类必须声明为抽象类
 */
abstract class Operation{
    //抽象方法不能包含函数体
    abstract public function getValue($num1,$num2);//强烈要求子类必须实现该功能函数
}
/**
 * 加法类
 */
class OperationAdd extends Operation {
    public function getValue($num1,$num2){
        return $num1+$num2;
    }
}
/**
 * 减法类
 */
class OperationSub extends Operation {
    public function getValue($num1,$num2){
        return $num1-$num2;
    }
}
/**
 * 乘法类
 */
class OperationMul extends Operation {
    public function getValue($num1,$num2){
        return $num1*$num2;
    }
}
/**
 * 除法类
 */
class OperationDiv extends Operation {
    public function getValue($num1,$num2){
        try {
            if ($num2==0){
                throw new Exception("除数不能为0");
            }else {
                return $num1/$num2;
            }
        }catch (Exception $e){
            echo "错误信息:".$e->getMessage();
        }
    }
}

这里采用了面向对象的继承特性,首先声明一个虚拟基类,在基类中指定子类务必实现的方法(getValue())

分析:通过采用面向对象的继承特性,我们可以很容易就能对原有程序进行扩展,比如:‘乘方’,‘开方’,‘对数’,‘三角函数’,‘统计’等等。

/**
 * 求余类(remainder)
 *
 */
class OperationRem extends Operation {
    public function getValue($num1,$num2){
        return $num1%$num2;
    }
}

我们只需要另外写一个类(该类继承虚拟基类),在类中完成相应的功能(比如:求乘方的运算),而且大大的降低了耦合度,方便日后的维护及扩展

现在还有一个问题未解决,就是如何让程序根据用户输入的操作符实例化相应的对象呢?
解决办法:使用一个单独的类来实现实例化的过程,这个类就是工厂
代码如下:

/**
 * 工程类,主要用来创建对象
 * 功能:根据输入的运算符号,工厂就能实例化出合适的对象
 */
class Factory{
    public static function createObj($operate){
        switch ($operate){
            case '+':
                return new OperationAdd();
                break;
            case '-':
                return new OperationSub();
                break;
            case '*':
                return new OperationSub();
                break;
            case '/':
                return new OperationDiv();
                break;
        }
    }
}
$test=Factory::createObj('/');
$result=$test->getValue(23,0);
echo $result;

看到这里其实已经说明了一工厂模式的例子。。。。

如果想另一种实现方法,接下来继续:

让我们谈一下许多开发人员从事大型系统的艰苦历程。在更改一个代码片段时,就会发生问题,系统其他部分 —— 您曾认为完全不相关的部分中也有可能出现级联破坏。

该问题在于紧密耦合 。系统某个部分中的函数和类严重依赖于系统的其他部分中函数和类的行为和结构。您需要一组模式,使这些类能够相互通信,但不希望将它们紧密绑定在一起,以避免出现联锁。

在大型系统中,许多代码依赖于少数几个关键类。需要更改这些类时,可能会出现困难。例如,假设您有一个从文件读取的 User 类。您希望将其更改为从数据库读取的其他类,但是,所有的代码都引用从文件读取的原始类。这时候,使用工厂模式会很方便。

看下实例:

interface IUser{
    function getName();
}

class User implements IUser{        
    public $id;
    public function __construct( $id ) { }

    public function getName(){
        return "Jack";
    }
}

传统方法使用 User 类,一般都是这样:

//在页面1
$obj = new User(1);

//在页面2
$obj2 = new User(2);

//在页面3
$obj3 = new User(3);
....

这时候,由于新的需求,使得User类要新增个参数或者User类名称发生变化,User 类代码发生变动,即:

class User implements IUser{
    public $id,$pre;
    public function __construct( $id , $pre ) {...}

    public function getName(){
        return $this->pre."Jack";
    }
}

接着,恐怖的事情发生了,假设之前有 100 个页面引用了之前的 User 类,那么这 100 个页面都要发生相应的改动:

//在页面1
$obj = new User(1,'aaa');

//在页面2
$obj = new User(2,'aaa');

//在页面3
$obj = new User(3,'aaa');
...

本来是一个小小的改动,但因紧密耦合的原因使得改动大吐血。而使用工厂模式则可以避免发生这种情况:

//User类为变动前
class UserFactory{
    public static function Create( $id ){
        return new User( $id );
    }
}

//页面1
$uo1 = UserFactory::Create( 1 );

//页面2
$uo12 = UserFactory::Create( 2 );
....

这时候需求变动,User 类也发生变动:

class User implements IUser{
    public $id,$pre;
    public function __construct( $id , $pre ) {...}

    public function getName(){
        return $this->pre."Jack";
    }
}

但是,我们不再需要去改动这 100 个页面,我们要改的仅仅是这个工厂类:

class UserFactory{
    public static function Create( $id,$pre = 'aaa' ) {
        return new User( $id ,$pre);
    }
}

其他100个页面不用做任何改动,这就是工厂设计模式带来的好处。看下UML图:
工厂模式

整理自两篇稿子,分别是:
《PHP中“简单工厂模式”实例讲解》http://www.cnblogs.com/hongfei/archive/2012/07/07/2580776.html
《PHP工厂模式》http://coderschool.cn/1521.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值