php7实践指南-ch19 php设计模式

设计模式是软件工程的基石,如同大厦的一块块砖石一样。项目中合理地运用设计模式可以完美地解决很多问题,每种模式在现实中都有相应的原理来与之对应,每种模式都描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是设计模式能被广泛应用的原因。

19.2 工厂模式

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

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

<?php

class Calc{
    /*
 * 计算结果
 *
 * */

    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;
                case '%':
                    $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
echo $test->calculate(7,4,'%');//打印:3

当需要类再实现一个可以“求余”的运算时,便可在switch语句块中添加一个分支语句,代码需要做如下改动:

 

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

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

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

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

工厂模式代码如下:

<?php
//定义接口
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,0);

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

19.3 单例模式

单例模式涉及一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。单例模式主要解决一个全局使用的类被频繁创建与销毁的问题,由于只创建了一个类的实例,因此减少了内存开销、节省了系统资源。PHP中单例模式经常被用在数据库应用中。

<?php
/*
 * 设计模式的单便模式
 * $_instance必须声明为静态的私有变量
 * 构造函数必须声明为私有,防止外部程序new类从而失去单便模式的意义
 * getInstance()方法必须设置为公有的,必须调用此方法以返回实例的一个引用
 * ::操作符只能访问静态变量和静态函数
 * new对象都会消耗内存
 * 使用单便模式生成一个对象后,该对象可以被其他众多对象所使用
 * */
class man
{
    //保存实例在此属性中
    private static $_instance;

    //构造函数声明为private,防止直接创建对象
    private function __construct()
    {
        echo '我被实例化了!';
    }

    //单例方法
    public static function get_instance()
    {
        if(! isset(self::$_instance)){
            self::$_instance= new self();
        }
        return self::$_instance;
    }

    //阻止用户复制对象实例
    private function  __clone(){
        trigger_error('Clone ia not allow',E_USER_ERROR);
    }

    function test(){
        echo("test");
    }
}
//这个写法会出错,因为构造方法被声明为private
//$test = new man();

//下面将得到Example类的单例对象
$test = man::get_instance();
$test = man::get_instance();
$test->test();

//复制对象将导致一个E_USER_ERROR
//$test_clone = clone $test;

执行以上程序的输出结果为:

我被实例化了!test

19.4 观察者模式

https://zhuanlan.zhihu.com/p/188540121

 

19.5 策略模式

在策略模式(Strategy Pattern)中,一个类的行为或算法可以在运行时更改。这种类型的设计模式属于行为型模式。其实现原理是定义一系列的算法,将它们一个个封装起来,并且可以互相替换,这样避免了使用if … else语句所带来的复杂度和维护成本。如果一个系统里有许多的类,而这些类之间的区别仅在于它们行为的不同,系统也需要动态地选择几种算法中的一种,这时使用策略模式是一种很好的解决方案。

下面的示例演示两个数之间的“加减乘除”运算。在工厂模式中,我们使用工厂模式实现了这种计算,根据传入的参数而分别生成不同的类实例。这里的示例使用策略模式来实现这种数学运算,代码如下:

<?php
//定义接口
interface Calc{
    public function getValue($num1,$num2);
}

//创建实现接口的实体类
class AddStrategy implements Calc{

    public function getValue($num1, $num2)
    {
       echo $num1 + $num2;
    }
}

class SubStrategy implements Calc{

    public function getValue($num1, $num2)
    {
        echo $num1 - $num2;
    }
}

class MulStrategy implements Calc{

    public function getValue($num1, $num2)
    {
        echo $num1 * $num2;
    }
}

class DivStrategy implements Calc{

    public function getValue($num1, $num2)
    {
        try{
            if($num2==0){
                throw new Exception('除数不能为0');
            }else{
                echo $num1/$num2;
            }
        }
        catch (Exception $e){
            echo '错误信息'.$e->getMessage();
        }
    }
}

//Calc策略类,传入的参数(具体行为的类)
class CalcContext{
    //存储选择的策略对象
    private $_strategy= null;
    public function __construct(Calc $select)
    {
        $this->_strategy=$select;
    }

    //设置使用的策略类方法
    public function setCalc(Calc $select){
        return $this->_strategy=$select;
    }

    //获取计算结果方法
    public function calcResult($m,$n){
        $this->_strategy->getValue($m,$n);
    }
}

$result=new  CalcContext(new AddStrategy());
echo $result->calcResult(10,2);

echo " ";
//切换不同策略
$result->setCalc(new DivStrategy());
$result->calcResult(10,2);

执行以上程序,在浏览器中的输出结果为:

12 5

我们使用策略模式和工厂模式都可以实现这种功能,区别是:工厂模式关注对象的创建、提供创建对象的接口,是创建型的设计模式,接受指令,创建出符合要求的实例;策略模式是行为型的设计模式,接受已经创建好的实例,实现不同的行为。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值