你听说过PHP 的面向方面编程吗?

  面向方面编程(AOP)对于PHP来说是一个新的概念。现在PHP对于 AOP 并没有官方支持,但有很多扩展和库实现了这个特性。本课中,我们将使用 Go! PHP library 来学习 PHP 如何进行 AOP 开发,或者在需要的时候,可以回来看一眼。

 AOP简史

Aspect-Oriented programming is like a new gadget for geeks.

  面向方面编程的思想在二十世纪90年代中期,于施乐帕洛阿尔托研究中心(PARC)成型。同很多有趣的新技术一样,由于缺少明确的定义,起初 AOP 备受争议。因此相关小组决定将未完成的想法公之于众,以便接受广大社区的反馈。关键问题在于“关注点分离(Separation of Concerns)”的概念。AOP 是一种可以分离关注的可行系方案。

  AOP 于90年代末趋于成熟,标识为施乐 AspectJ 的发布,IBM 紧随其后,于2001年发布了 Hyper/J。现在,AOP是一种对于常用编程语言来说都是一种成熟的技术。

 基本词汇

  AOP 的核心就是“方面”,但在我们定义「方面『aspect』」之前,我们需要先讨论两个术语;「切点『 point-cut』 」和「通知advise』」。切点代表我们代码中的一个时间点,特指运行我们代码的某个时间。在切点运行代码被称为通知,结合一个活多个切点及通知的即为方面

  通常,每个类都会有一个核心的行为或关注点,但有时,类可能存在次要的行为。例如,类可能会调用一个日志记录器或是通知一个观察员。因为类中的这些功能是次要的,其行为通常都是相同的。这种行为被称为“交叉关注点”;使用 AOP 可以避免。

 PHP的各种AOP工具

  Chris Peters 已经讨论过在PHP中实现 AOP 的Flow 框架。 Lithium 框架也提供了对AOP的实现。

  另一个框架采用了不同的方法,创建了一个 C/C++ 编写的PHP扩展,在PHP解释器的层级上宣示着它的魔力。名为AOP PHP Extension,我会在后续文章中讨论它。

  但正如我之前所言,本文将检阅Go! AOP-PHP 库。

 安装并配置 Go!

  Go! 库并未扩展;它完全由PHP编写,并为PHP5.4或更高版本使用。作为一个纯PHP库,它部署简易,即使是在不允许编译安装你自己的PHP扩展的受限及共享主机环境,也可以轻易安装。

  使用 Composer 安装 Go!

  Composer 是安装 PHP 包的首选方法。如果你没有使用过 Composer,你可以在Go! GitHub repository下载。

  首先,将下面几行加入你的 composer.json 文件。


1

2

3

4

5


{

    "require": {

        "lisachenko/go-aop-php": "*"

    }

}

  之后,使用 Composer 安装 go-aop-php。在终端中运行下面命令:


1

2


$ cd /your/project/folder

$ php composer.phar update lisachenko/go-aop-php

  Composer 将会在之后数秒中内安装引用的包以及需求。如果成功,你将看到类似下面的输出:


1

2

3

4

5

6

7

8

9

10

11

12

13


Loading composer repositories with package information

Updating dependencies

  - Installing doctrine/common (2.3.0)

    Downloading: 100%

  - Installing andrewsville/php-token-reflection (1.3.1)

    Downloading: 100%

  - Installing lisachenko/go-aop-php (0.1.1)

    Downloading: 100%

Writing lock file

Generating autoload files

  在安装完成后,你可以在你的代码目录中发现名为 vendor 的文件夹。Go! 库及其需求就安装在这。


1

2

3

4

5

6

7

8

9

10

11


$ ls -l ./vendor

total 20

drwxr-xr-x 3 csaba csaba 4096 Feb  2 12:16 andrewsville

-rw-r--r-- 1 csaba csaba  182 Feb  2 12:18 autoload.php

drwxr-xr-x 2 csaba csaba 4096 Feb  2 12:16 composer

drwxr-xr-x 3 csaba csaba 4096 Feb  2 12:16 doctrine

drwxr-xr-x 3 csaba csaba 4096 Feb  2 12:16 lisachenko

$ ls -l ./vendor/lisachenko/

total 4

drwxr-xr-x 5 csaba csaba 4096 Feb  2 12:16 go-aop-php

  整合到我们的项目

  我们需要创建一个调用,介于路由/应用程序的入口点。自动装弹机的然后自动包括类。开始吧!引用作为一个切面内核。


1

2

3

4

5

6

7

8

9

10

11

12

13

14


use Go\Core\AspectKernel;

use Go\Core\AspectContainer;

class ApplicationAspectKernel extends AspectKernel {

    protected function configureAop(AspectContainer $container) {

    }

    protected function getApplicationLoaderPath() {

    }

}

现在,AOP是一种在通用编程语言中相当成熟的技术。

  例如,我创建了一个目录,调用应用程序,然后添加一个类文件: ApplicationAspectKernel.php 。

  我们开始切面扩展!AcpectKernel 类提供了基础的方法用于完切面内核的工作。有两个方法,我们必须知道:configureAop()用于注册页面特征,和 getApplicationLoaderPath() 返回自动加载程序的全路径。

  现在,一个简单的建立一个空的 autoload.php 文件在你的程序目录。和改变 getApplicationLoaderPath() 方法。如下:


1

2

3

4

5

6

7

8

9

10


// [...]

class ApplicationAspectKernel extends AspectKernel {

    // [...]

    protected function getApplicationLoaderPath() {

        return __DIR__ . DIRECTORY_SEPARATOR . ‘autoload.php‘;

    }

}

  别担心 autoload.php 就是这样。我们将会填写被省略的片段。

  当我们第一次安装 Go语言!和达到这一点我的过程中,我觉得需要运行一些代码。所以开始构建一个小应用程序。

 创建一个简单的日志记录器

  我们的「方面」为一个简单的日志记录器,但在继续我们应用的主要部分之前,有些代码需要看一下。

  创建一个最小的应用

  我们的小应用是一个电子经纪人,能够购买和***。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19


class Broker {

    private $name;

    private $id;

    function __construct($name, $id) {

        $this->name = $name;

        $this->id = $id;

    }

    function buy($symbol, $volume, $price) {

        return $volume * $price;

    }

    function sell($symbol, $volume, $price) {

        return $volume * $price;

    }

}

  这些代码非常简单,Broker 类拥有两个私有字段,储存经纪人的名称和 ID。

  这个类同时提供了两个方法,buy() 和 sell(),分别用于收购和***。每个方法接受三个参数:股票标识、股票数量、每股价格。sell() 方法***,并计算总收益。相应的,buy()方法购买股票并计算总支出。

  考验我们的经纪人

  通过PHPUnit 测试程序,我们可以很容易的考验我们经纪人。在应用目录内创建一个子目录,名为 Test,并在其中添加 BrokerTest.php 文件。并添加下面的代码:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15


require_once ‘../Broker.php‘;

class BrokerTest extends PHPUnit_Framework_TestCase {

    function testBrokerCanBuyShares() {

        $broker = new Broker(‘John‘, ‘1‘);

        $this->assertEquals(500, $broker->buy(‘GOOGL‘, 100, 5));

    }

    function testBrokerCanSellShares() {

        $broker = new Broker(‘John‘, ‘1‘);

        $this->assertEquals(500, $broker->sell(‘YAHOO‘, 50, 10));

    }

}

  这个检验程序检查经纪人方法的返回值。我们可以运行这个检查程序检验我们的代码,至少是不是语法正确。

  添加一个自动加载器

  让我们创建一个自动加载器,在应用需要的时候加载类。这是一个简单的加载器,基于PSR-0 autoloader.


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19


ini_set(‘display_errors‘, true);

spl_autoload_register(function($originalClassName) {

    $className = ltrim($originalClassName, ‘\\‘);

    $fileName  = ‘‘;

    $namespace = ‘‘;

    if ($lastNsPos = strripos($className, ‘\\‘)) {

        $namespace = substr($className, 0, $lastNsPos);

        $className = substr($className, $lastNsPos + 1);

        $fileName  = str_replace(‘\\‘, DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR;

    }

    $fileName .= str_replace(‘_‘, DIRECTORY_SEPARATOR, $className) . ‘.php‘;

    $resolvedFileName = stream_resolve_include_path($fileName);

    if ($resolvedFileName) {

        require_once $resolvedFileName;

    }

    return (bool) $resolvedFileName;

});

  这就是我们 autoload.php 文件中的全部内容。现在,变更 BrokerTest.php, 改引用Broker.php 为引用自动加载器 。


1

2

3

4

5


require_once ‘../autoload.php‘;

class BrokerTest extends PHPUnit_Framework_TestCase {

    // [...]

}

  运行 BrokerTest,验证代码运行情况。

  连接到应用方面核心

  我们最后的一件事是配置Go!.为此,我们需要连接所有的组件让们能和谐工作。首先,创建一个php文件AspectKernelLoader.php,其代码如下:


1

2

3

4

5

6

7

8

9

10

11

12

13

14


include __DIR__ . ‘/../vendor/lisachenko/go-aop-php/src/Go/Core/AspectKernel.php‘;

include ‘ApplicationAspectKernel.php‘;

ApplicationAspectKernel::getInstance()->init(array(

    ‘autoload‘ => array(

        ‘Go‘               => realpath(__DIR__ . ‘/../vendor/lisachenko/go-aop-php/src/‘),

        ‘TokenReflection‘  => realpath(__DIR__ . ‘/../vendor/andrewsville/php-token-reflection/‘),

        ‘Doctrine\\Common‘ => realpath(__DIR__ . ‘/../vendor/doctrine/common/lib/‘)

    ),

    ‘appDir‘ => __DIR__ . ‘/../Application‘,

    ‘cacheDir‘ => null,

    ‘includePaths‘ => array(),

    ‘debug‘ => true

));

我们需要连接所有的组件让们能和谐工作!

  这个文件位于前端控制器和自动加载器之间。他使用AOP框架初始化并在需要时调用autoload.php

  第一行,我明确地载入AspectKernel.php和ApplicationAspectKernel.php,因为,要记住,在这个点我们还没有自动加载器。

  接下来的代码段,我们调用ApplicationAspectKernel对象init()方法,并且给他传递了一个数列参数:

  • autoload 定义了初始化AOP类库的路径。根据你实际的目录机构调整为相应的值。
  • appDir 引用了应用的目录
  • cacheDir 指出了缓存目录(本例中中我们忽略缓存)。
  • includePaths 对aspects的一个过滤器。我想看到所有特定的目录,所以设置了一个空数组,以便看到所有的值。
  • debug  提供了额外的调试信息,这对开发非常有用,但是对已经要部属的应用设置为false。

  为了最后实现各个不同部分的连接,找出你工程中autoload.php自动加载所有的引用并且用AspectKernelLoader.php替换他们。在我们简单的例子中,仅仅test文件需要修改:


1

2

3

4

5

6

7


require_once ‘../AspectKernelLoader.php‘;

class BrokerTest extends PHPUnit_Framework_TestCase {

// [...]

}

  对大一点的工程,你会发现使用bootstrap.php作为单元测试但是非常有用;用require_once()做为autoload.php,或者我们的AspectKernelLoader.php应该在那载入。

  记录Broker的方法

  创建BrokerAspect.php文件,代码如下:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19


use Go\Aop\Aspect;

use Go\Aop\Intercept\FieldAccess;

use Go\Aop\Intercept\MethodInvocation;

use Go\Lang\Annotation\After;

use Go\Lang\Annotation\Before;

use Go\Lang\Annotation\Around;

use Go\Lang\Annotation\Pointcut;

use Go\Lang\Annotation\DeclareParents;

class BrokerAspect implements Aspect {

    /**

     * @param MethodInvocation $invocation Invocation

     * @Before("execution(public Broker->*(*))") // This is our PointCut

     */

    public function beforeMethodExecution(MethodInvocation $invocation) {

        echo "Entering method " . $invocation->getMethod()->getName() . "()\n";

    }

}

  我们在程序开始指定一些有对AOP框架有用的语句。接着,我们创建了自己的方面类叫BrokerAspect,用它实现Aspect。接着,我们指定了我们aspect的匹配逻辑。


1

* @Before("execution(public Broker->*(*))")
  • @Before 给出合适应用建议. 可能的参数有@Before,@After,@Around和@After线程.
  • "execution(public Broker->*(*))" 给执行一个类所有的公共方法指出了匹配规则,可以用任意数量的参数调用Broker,语法是:

1

[operation - execution/access]([method/attribute type - public/protected] [class]->[method/attribute]([params])

   请注意匹配机制不可否认有点笨拙。你在规则的每一部分仅可以使用一个星号‘*‘。例如public Broker->匹配一个叫做Broker的类;public Bro*->匹配以Bro开头的任何类;public *ker->匹配任何ker结尾的类。

public *rok*->将匹配不到任何东西;你不能在同一个匹配中使用超过一个的星号。

   紧接着匹配程序的函数会在有时间发生时调用。在本例中的方法将会在每一个Broker公共方法调用之前执行。其参数$invocation(类型为MethodInvocation)子自动传递到我们的方法的。这个对象提供了多种方式获取调用方法的信息。在第一个例子中,我们使用他获取了方法的名字,并且输出。

  注册切面

  仅仅定义一个切面是不够的;我们需要把它注册到AOP架构里。否则,它不会生效。编辑ApplicationAspectKernel.php同时在容器上的configureAop()方法里调用registerAspect():


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16


use Go\Core\AspectKernel;

use Go\Core\AspectContainer;

class ApplicationAspectKernel extends AspectKernel

{

    protected function getApplicationLoaderPath()

    {

        return __DIR__ . DIRECTORY_SEPARATOR . ‘autoload.php‘;

    }

    protected function configureAop(AspectContainer $container)

    {

        $container->registerAspect(new BrokerAspect());

    }

}

  运行测试和检查输出。你会看到类似下面的东西:


1

2

3

4

5

6

7

8

9


PHPUnit 3.6.11 by Sebastian Bergmann.

.Entering method __construct()

Entering method buy()

.Entering method __construct()

Entering method sell()

Time: 0 seconds, Memory: 5.50Mb

OK (2 tests, 2 assertions)

  就这样我们已设法让代码无论什么时候发生在broker上时都会执行。

  查找参数和匹配@After

  让我们加入另外的方法到BrokerAspect。


1

2

3

4

5

6

7

8

9

10

11

12

13

14


// [...]

class BrokerAspect implements Aspect {

    // [...]

    /**

     * @param MethodInvocation $invocation Invocation

     * @After("execution(public Broker->*(*))")

     */

    public function afterMethodExecution(MethodInvocation $invocation) {

        echo "Finished executing method " . $invocation->getMethod()->getName() . "()\n";

        echo "with parameters: " . implode(‘, ‘, $invocation->getArguments()) . ".\n\n";

    }

}

  这个方法在一个公共方法执行后运行(注意@After匹配器)。染污我们加入另外一行来输出用来调用方法的参数。我们的测试现在输出:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21


PHPUnit 3.6.11 by Sebastian Bergmann.

.Entering method __construct()

Finished executing method __construct()

with parameters: John, 1.

Entering method buy()

Finished executing method buy()

with parameters: GOOGL, 100, 5.

.Entering method __construct()

Finished executing method __construct()

with parameters: John, 1.

Entering method sell()

Finished executing method sell()

with parameters: YAHOO, 50, 10.

Time: 0 seconds, Memory: 5.50Mb

OK (2 tests, 2 assertions)

  获得返回值并操纵运行

  目前为止,我们学习了在一个方法执行的之前和之后,怎样运行额外的代码。当这个漂亮的实现后,如果我们无法看到方法返回了什么的话,它还不是非常有用。我们给aspect增加另一个方法,修改现有的代码:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32


//[...]

class BrokerAspect implements Aspect {

    /**

     * @param MethodInvocation $invocation Invocation

     * @Before("execution(public Broker->*(*))")

     */

    public function beforeMethodExecution(MethodInvocation $invocation) {

        echo "Entering method " . $invocation->getMethod()->getName() . "()\n";

        echo "with parameters: " . implode(‘, ‘, $invocation->getArguments()) . ".\n";

    }

    /**

     * @param MethodInvocation $invocation Invocation

     * @After("execution(public Broker->*(*))")

     */

    public function afterMethodExecution(MethodInvocation $invocation) {

        echo "Finished executing method " . $invocation->getMethod()->getName() . "()\n\n";

    }

    /**

     * @param MethodInvocation $invocation Invocation

     * @Around("execution(public Broker->*(*))")

     */

    public function aroundMethodExecution(MethodInvocation $invocation) {

        $returned = $invocation->proceed();

        echo "method returned: " . $returned . "\n";

        return $returned;

    }

}

仅仅定义一个aspect是不够的;我们需要将它注册到AOP基础设施。

  这个新的代码把参数信息移动到@Before方法。我们也增加了另一个特殊的@Around匹配器方法。这很整洁,因为原始的匹配方法调用被包裹于aroundMethodExecution()函数之内,有效的限制了原始的调用。在advise里,我们要调用$invocation->proceed(),以便执行原始的调用。如果你不这么做,原始的调用将不会发生。

  这种包装也允许我们操作返回值。advise返回的就是原始调用返回的。在我们的案例中,我们没有修改任何东西,输出应该看起来像这样:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25


PHPUnit 3.6.11 by Sebastian Bergmann.

.Entering method __construct()

with parameters: John, 1.

method returned:

Finished executing method __construct()

Entering method buy()

with parameters: GOOGL, 100, 5.

method returned: 500

Finished executing method buy()

.Entering method __construct()

with parameters: John, 1.

method returned:

Finished executing method __construct()

Entering method sell()

with parameters: YAHOO, 50, 10.

method returned: 500

Finished executing method sell()

Time: 0 seconds, Memory: 5.75Mb

OK (2 tests, 2 assertions)

  我们增加一点变化,赋以一个具体的broker一个discount。返回到测试类,写如下的测试:


1

2

3

4

5

6

7

8

9

10

11

12


require_once ‘../AspectKernelLoader.php‘;

class BrokerTest extends PHPUnit_Framework_TestCase {

    // [...]

    function testBrokerWithId2WillHaveADiscountOnBuyingShares() {

        $broker = new Broker(‘Finch‘, ‘2‘);

        $this->assertEquals(80, $broker->buy(‘MS‘, 10, 10));

    }

}

  这会失败:


1

2

3

4

5

6

7

8

9

10

11

12


Time: 0 seconds, Memory: 6.00Mb

There was 1 failure:

1) BrokerTest::testBrokerWithId2WillHaveADiscountOnBuyingShares

Failed asserting that 100 matches expected 80.

/home/csaba/Personal/Programming/NetTuts/Aspect Oriented Programming in PHP/Source/Application/Test/BrokerTest.php:19

/usr/bin/phpunit:46

FAILURES!

Tests: 3, Assertions: 3, Failures: 1.

  下一步,我们需要修改broker以便提供它的ID。只要像下面所示实现agetId()方法:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17


class Broker {

    private $name;

    private $id;

    function __construct($name, $id) {

        $this->name = $name;

        $this->id = $id;

    }

    function getId() {

        return $this->id;

    }

    // [...]

}

  现在,修改aspect以调整具有ID值为2的broker的购买价格。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18


// [...]

class BrokerAspect implements Aspect {

    // [...]

    /**

     * @param MethodInvocation $invocation Invocation

     * @Around("execution(public Broker->buy(*))")

     */

    public function aroundMethodExecution(MethodInvocation $invocation) {

        $returned = $invocation->proceed();

        $broker = $invocation->getThis();

        if ($broker->getId() == 2) return $returned * 0.80;

        return $returned;

    }

}

  无需增加新的方法,只要修改aroundMethodExecution()函数。现在它正好匹配方法,称作‘buy‘,并触发了$invocation->getThis()。这有效的返回了原始的Broker对象,以便我们可以执行它的代码。于是我们做到了!我们向broker要它的ID,如果ID等于2的话就提供一个折扣。测试现在通过了。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37


PHPUnit 3.6.11 by Sebastian Bergmann.

.Entering method __construct()

with parameters: John, 1.

Finished executing method __construct()

Entering method buy()

with parameters: GOOGL, 100, 5.

Entering method getId()

with parameters: .

Finished executing method getId()

Finished executing method buy()

.Entering method __construct()

with parameters: John, 1.

Finished executing method __construct()

Entering method sell()

with parameters: YAHOO, 50, 10.

Finished executing method sell()

.Entering method __construct()

with parameters: Finch, 2.

Finished executing method __construct()

Entering method buy()

with parameters: MS, 10, 10.

Entering method getId()

with parameters: .

Finished executing method getId()

Finished executing method buy()

Time: 0 seconds, Memory: 5.75Mb

OK (3 tests, 3 assertions)

  匹配异常

  我们现在可以在一个方法的开始和执行之后、绕过时,执行附加程序。但当方法抛出异常时又如何呢?

  添加一个测试方法来购买大量微软的股票:


1

2

3

4


function testBuyTooMuch() {

    $broker = new Broker(‘Finch‘, ‘2‘);

    $broker->buy(‘MS‘, 10000, 8);

}

  现在,创建一个异常类。我们需要它是因为内建的异常类不能被 Go!AOP 或 PHPUnit 捕捉.


1

2

3

4

5

6

7


class SpentTooMuchException extends Exception {

    public function __construct($message) {

        parent::__construct($message);

    }

}

  修改经纪人类,对大值抛出异常:


1

2

3

4

5

6

7

8

9

10

11

12

13

14


class Broker {

    // [...]

    function buy($symbol, $volume, $price) {

        $value = $volume * $price;

        if ($value > 1000)

            throw new SpentTooMuchException(sprintf(‘You are not allowed to spend that much (%s)‘, $value));

        return $value;

    }

    // [...]

}

  运行测试,确保它们产生失败消息:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15


Time: 0 seconds, Memory: 6.00Mb

There was 1 error:

1) BrokerTest::testBuyTooMuch

Exception: You are not allowed to spend that much (80000)

/home/csaba/Personal/Programming/NetTuts/Aspect Oriented Programming in PHP/Source/Application/Broker.php:20

// [...]

/home/csaba/Personal/Programming/NetTuts/Aspect Oriented Programming in PHP/Source/Application/Broker.php:47

/home/csaba/Personal/Programming/NetTuts/Aspect Oriented Programming in PHP/Source/Application/Test/BrokerTest.php:24

/usr/bin/phpunit:46

FAILURES!

Tests: 4, Assertions: 3, Errors: 1.

  现在,期待异常(在测试中),确保它们通过:


1

2

3

4

5

6

7

8

9

10

11

12

13


class BrokerTest extends PHPUnit_Framework_TestCase {

    // [...]

    /**

     * @expectedException SpentTooMuchException

     */

    function testBuyTooMuch() {

        $broker = new Broker(‘Finch‘, ‘2‘);

        $broker->buy(‘MS‘, 10000, 8);

    }

}

  在我们的“方面”中建立一个新方法来匹配@AfterThrowing,别忘记指定 Use Go\Lang\Annotation\AfterThrowing;


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16


// [...]

Use Go\Lang\Annotation\AfterThrowing;

class BrokerAspect implements Aspect {

    // [...]

    /**

     * @param MethodInvocation $invocation Invocation

     * @AfterThrowing("execution(public Broker->buy(*))")

     */

    public function afterExceptionMethodExecution(MethodInvocation $invocation) {

        echo ‘An exception has happened‘;

    }

}

  @AfterThrowing匹配器抑制抛出的异常,并允许你去采取自己的行动。在我们的代码中,我们简单的显示一个信息,但你可以做任何你的应用程序需要的事情。

 最后的思考

这就是为什么我建议你小心使用“方面”。

  面向方面编程就像给怪人们的新玩意儿;您可以立即看到其巨大的潜力。方面允许我们在我们的系统的不同部分引入额外的代码,而无需修改原始代码。当你需要实现一些通过紧耦合引用和方法调用会污染你的方法和类的模块时,这会非常有用。

  然而,这种灵活性,是有代价的:阴暗朦胧。有没有办法告诉如果一方面表的方法只是在寻找方法或类。例如,在我们的Broker类中执行方法时没有迹象表明发生任何事情。这就是为什么我建议你小心使用“方面”的原因。

  我们使用“方面”来给一个特定的经纪人提供折扣是误用的一个例子。不要在一个真实的项目中这样做。经纪人的折扣与经纪人相关;所以,在Broker类中保持这个逻辑。“方面”应该只执行不直接关系到对象主要行为的任务。

  乐在其中吧!

  英文原文:Aspect-Oriented Programming in PHP with Go!

时间: 2024-10-12 01:39:35

你听说过PHP 的面向方面编程吗?的相关文章

Spring面向切面编程

  1.面向切面编程(AOP)的概念:把项目中需要在多处用到的功能,比如日志.安全和事物等集中到一个类中处理,而不用在每个需要用到该功能的地方显式调用.   2.术语解释:        横切关注点:分布应用于多处的功能        切面:横切关注点可以被模块化为一个类,这个类被称为一个切面        通知(advice):切面要完成的工作.Spring的通知有5种类型:before.after.after-returning.after-throwing和around这五种类型.    

面向切面编程(转)

面向切面编程(AOP是Aspect Oriented Program的首字母缩写) ,我们知道,面向对象的特点是继承.多态和封装.而封装就要求将功能分散到不同的对象中去,这在软件设计中往往称为职责分配.实际上也就是说,让不同的类设计不同的方法.这样代码就分散到一个个的类中去了.这样做的好处是降低了代码的复杂程度,使类可重用.       但是人们也发现,在分散代码的同时,也增加了代码的重复性.什么意思呢?比如说,我们在两个类中,可能都需要在每个方法中做日志.按面向对象的设计方法,我们就必须在两个

Spring AOP 面向切面编程

AOP 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型.利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率. 在Spring中提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务(例

[Spring实战系列](16)面向切面编程(AOP)概述

1. 简介 在软件中,有些行为对于大多数应用都是通用的.日志,安全和事务管理的确很重要,但他们是都是应用对象主动参与的行为呢?如果让应用对象只关注自己所针对的业务领域问题,而其他方面的问题由其他应用对象来处理,这样会不会更好? 在软件开发中,分布于应用中多处的功能被称为横切关注点.通常,这些横切关注点从概念上是与应用的业务逻辑相分离的(但是往往直接嵌入到应用的业务逻辑中).将这些横切关注点与业务逻辑相分离是面向切面编程索要解决的. 上图展示了一个被划分为模块的典型应用.每个模块的核心功能都是为特

面向接口编程

系列——MEF实现设计上的“松耦合”(终结篇:面向接口编程) 序:忙碌多事的八月带着些许的倦意早已步入尾声,金秋九月承载着抗战胜利70周年的喜庆扑面而来.没来得及任何准备,似乎也不需要任何准备,因为生活不需要太多将来时.每天忙着上班.加班.白加班,忘了去愤,忘了去算计所谓的价值.天津爆炸事故时刻警示着我们生命的无常,逝者安息,活着的人生活还得继续,珍惜生命,远离伤害.武汉,这座炙热的城市,虽值金秋,却依然经受着“秋老虎”的烘烤,马路上蒸腾的热气迎面袭来,全身毛孔张开,汗流不止,在这般高温下,似乎

Spring框架——AOP(面向切面编程)详解

 1 AOP概述 ●AOP(Aspect-Oriented Programming,面向切面编程):是一种新的方法论,是对传统 OOP(Object-Oriented Programming,面向对象编程)的补充. ●AOP编程操作的主要对象是切面(aspect),而切面模块化横切关注点. ●在应用AOP编程时,仍然需要定义公共功能,但可以明确的定义这个功能应用在哪里,以什么方式应用,并且不必修改受影响的类.这样一来横切关注点就被模块化到特殊的类里--这样的类我们通常称之为"切面".

面向切面编程

面向切面编程 .Net的面向切面编程 .Net的服务端应用AOP很常见,在Asp.net MVC与Asp.net WebApi等新框架里到处都有AOP的影子,我们可以把一个服务方法"切"为很多面,日志面.验证面.请求方式处理.接口业务实现等多个面,有一些面可以使用过滤器特性(FilterAttribute)进行编写,然后很方便和打上特性即可,对于一般的工程师,只需要专注实现接口业务实现. 在流行SOAP的年代,很少需要一个强悍的Client,而今天restful时代,.net也出了一个

Spring使用 --- 基本概念(二):AOP,面向方面编程

Table of Contents 什么是面向方面编程 怎样使用 什么时候使用 好处 本文讲述sprint的第二个基本概念: AOP,即面向方面编程 什么是面向方面编程 软件项目中,日志系统等服务系统被核心功能系统调用,日志系统的代码分散在各处.面向方面编程将日志等服务系统独立出来,作为单独一个模块,形成一个"方面".然后通过一些手段将日志与核心代码再联系起来,叫做"织入".由此将原来混杂在一起的代码分离成单独的模块,代码质量提高,模块内聚性更高,核心模块专注于处理

Spring面向切面编程(AOP)

1 spring容器中bean特性 Spring容器的javabean对象默认是单例的. 通过在xml文件中,配置可以使用某些对象为多列. Spring容器中的javabean对象默认是立即加载(立即实例化:spring加载完成,立即创建对象) scope:属性 singleton:默认值为单例,默认也是立即加载,在加载完成spring容器的时候,bean对象已经创建完成 prototype:多例的,默认懒加载,spring容器加载完成的时候,不会创建bean的对象,只有从容器获得bean对象的