什么是AOP

下面是Wikipedia的描述

In computing, aspect-oriented programming (AOP) is a patented[1] programming paradigm that aims to increase modularity by allowing the separation of cross-cutting concerns. It does so by adding additional behavior to existing code (an advice) without modifying the code itself, instead separately specifying which code is modified via a "pointcut" specification, such as "log all function calls when the function's name begins with 'set'". This allows behaviors that are not central to the business logic (such as logging) to be added to a program without cluttering the code core to the functionality. AOP forms a basis for aspect-oriented software development.

AOP为Aspect Oriented Programming的缩写,意为:面向切面编程(也叫面向方面),可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。AOP实际是GoF设计模式的延续,设计模式孜孜不倦追求的是调用者和被调用者之间的解耦,AOP可以说也是这种目标的一种实现。

AOP的好处是什么

可以将次要的业务代码从主业务代码中剥离,实现解耦

为什么要使用AOP

class PostController
{
    public function addPost()
    {
        //调用model写入数据库
        new Model()->save();
    }
}

某天产品经理要求加上用户权限验证,只有编辑组才可以发表文章,于是我们的代码变成如下

class PostController
{
    public function addPost()
    {
        //用户权限处理
        if (User::priv != 'editGroup'){
            // 提示用户权限不足
        }
        
        //调用model写入数据库
        new Model()->save();
    }
}

某天产品经理说运营需要增加积分奖励机制,于是我们的代码又变成如下

class PostController
{
    public function addPost()
    {
        //用户权限处理
        if (User::priv != 'editGroup'){
            // 提示用户权限不足
        }
        
        //调用model写入数据库
        new Model()->save();
        
        //用户积分机制
        if ($postAddSuccess){
            //增加用户积分
        }
    }
}

某天产品经理说能不能做一个通知,发表了,之后通知关注人查看

某天产品经理说XXX

长此以往这个业务代码将越来越庞大和复杂,可维护性也越来越差,那么我们怎么解决这个问题呢?

使用AOP编程之后,我们的代码将变成下面这样

//日志类
class Log
{
    private static $instance;
    public function __construct()
    {
    }
    
    public static function getInstance()
    {
        if (self::$instance == null) self::$instance = new Log;
        return self::$instance;
    }
    
    public function log($str)
    {
        echo $str . "<br />";
    }
}

// 业务接口类
interface bizExtension
{
    public function before();
    public function after();
}

//权限检查业务类
class userCheckBiz implements bizExtension
{
    public function before()
    {
        echo "权限检查<br />";
    }
    
    public function after()
    {
        echo "权限检查<br />";
    }
}

//积分业务类
class pointUpdateBiz implements bizExtension
{
    public function before()
    {
        echo "更新积分逻辑业务处理<br />";
    }
    public function after()
    {
        echo "更新积分逻辑业务处理<br />";
    }
}

//日志业务类
class logBiz implements bizExtension
{
    private $instance;
    
    public function __construct()
    {
        $this->instance = new Log;
    }
    
    public function before()
    {
    }
    
    public function after()
    {
        $this->instance->log("记录日志");
    }
}

//业务类
class bizGroup implements bizExtension
{
    private $_bizGroup = [];
    
    public function addBiz(bizExtension $bizItem)
    {
        $this->_bizGroup[] = $bizItem;
    }
    
    public function before()
    {
        foreach ($this->_bizGroup as $bitItem) {
            $bitItem->before();
        }
    }
    
    public function after()
    {
        foreach ($this->_bizGroup as $bitItem) {
            $bitItem->after();
        }
    }
}

// 文章次要业务逻辑工厂类
class PostBizFactory
{
    public static function getAddPostBizList()
    {
        $bizContainer = new bizGroup;
        $bizContainer->addBiz(new userCheckBiz());
        $bizContainer->addBiz(new pointUpdateBiz());
        $bizContainer->addBiz(new logBiz());
        return $bizContainer;
    }
}

class PostController
{
    
    public function __construct()
    {
        echo "Post 初始化<br />";
    }
    
    public function addPost()
    {
        
        $biz = PostBizFactory::getAddPostBizList();
        
        $biz->before();
        
        echo "Run addPost Action <br />";
        
        $biz->after();
    }
}

$post = new PostController();
$post->addPost(); 

输出如下

Post 初始化
权限检查
更新积分逻辑业务处理
Run addPost Action 
权限检查
更新积分逻辑业务处理
记录日志

这样,当addPost需要增加其他新业务时,可以不用修改主业务代码,通过增加业务接口类的方式达到实现。

以上是AOP编程的思路,具体实现有很多种。PHP AOP框架有很多种,这次就不详细说,另外PECL提供了一个AOP扩展,有兴趣的同学可以看一下:

http://pecl.php.net/package/AOP

https://github.com/AOP-PHP/AOP