工厂模式属于创建型模式,它提供了一种创建对象的方式。工厂模式是先定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类。使用工厂模式的扩展性高,如果想增加一个产品,只要扩展一个工厂类酒可以了,其屏蔽了产品的具体实现,调用者只需关心产品的接口。工厂模式的精髓即是可以根据不同的参数生成不同的类实例。

比如我们定义一个类来实现两个数的加减乘除,代码如下:

    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

?>

当我们需要类再实现一个可以“求余”的运算,便可在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();

            }

        }

    }

?>

用以上方法实现给计算器添加新的功能运算有以下几个缺点:

1. 需要改动原有的代码块,可能会在为了“添加新功能”而改动原有代码的时候,不小心将原有的代码改错了。

2. 如果要添加的功能很多,比如:’乘方’,‘开方’,‘对数’,‘三角函数’,‘统计’,或者添加一些程序员专用的计算功能,比如:And, Or, Not, Xor,这样就需要在switch语句中添加N个分支语句。想象下,一个计算功能的函数如果有二三十个case分支语句,代码将超过一屏,不仅令代码的可读性大大降低,关键是,为了添加小功能,还得让其余不相关都参与解释,这令程序的执行效率大大降低。

为了解决以上问题我们可以采用工厂模式,思路是这样的,我们可以定义“加减乘除”四个类,这四个类中都有 getValue() 方法,然后定义一个可以创建“加减乘除”的类,我们称之为工厂类,该工厂类中有个工厂方法,我们根据可传入到工厂方法的不同参数(可以是”加减乘除“的数学符号),使用这个工厂类的工厂方法创建“加减乘除”类,然后调用其对应的 getValue() 方法获得返回结果。

工厂模式代码如下:


//定义接口

interface Calc{

public function getValue($num1,$num2);

}


//创建实现接口的实体类

class Add implements Calc{

public function getValue($num1,$num2) {

return $num1 + $num2;

}

}


class Sub implements Calc{

public function getValue($num1,$num2) {

return $num1 - $num2;

}

}


class Mul implements Calc{

public function getValue($num1,$num2) {

return $num1 * $num2;

}

}


class Div implements Calc{

public function getValue($num1,$num2) {

try {

if($num2 == 0) {

throw new Exception('除数不能为0');

} else {

return $num1/$num2;

}

} catch (Exception $e) {

echo "错误信息:" . $e->getMessage();

}

}

}



//创建一个工厂,生成基于给定信息的实体类的对象

class Factory{

public static function createObj($operate) {

switch ($operate) {

case '+':

return new Add();

break;

case '-':

return new Sub();

break;

case '*':

return new Mul();

break;

case '/':

return new Div();

break;

}

}

}


$test = Factory::createObj('-');

echo $test->getValue(1,4);

?>

这样我们就实现了根据用户输入的操作符实例化相应的对象,进而可完成接下来相应的操作。在软件开发中,php可能要链接mysql,也可能链接sqlserver或者其他数据库,这样我们就可以定义一个工厂类,动态产生不同的数据库链接对象。再比如设计一个连接服务器的框架,需要三个协议,”POP3”、”IMAP"、"HTTP",可以把这三个作为产品类,共同实现一个接口。工厂模式的使用场景有很多,需要读者在实际开发中尝试应用。