代码之家  ›  专栏  ›  技术社区  ›  yeradis

面向方面编程与面向对象编程

  •  230
  • yeradis  · 技术社区  · 16 年前

    与这里和全世界的大多数开发人员一样,我多年来一直在使用面向对象编程(OOP)技术开发软件系统。因此,当我读到面向方面编程(AOP)解决了传统OOP无法完全或直接解决的许多问题时,我停下来想,这是真的吗?

    我读过很多信息,试图了解这种AOP范式的关键,我也有同感,所以,我想更好地了解它在现实世界应用程序开发中的好处。

    有人知道答案吗?

    7 回复  |  直到 13 年前
        1
  •  289
  •   Cory Klein    10 年前

    为什么是“vs”?这不是“vs”。您可以将面向方面的编程与函数式编程结合使用,也可以与面向对象的编程结合使用。它不是“vs”,而是“面向方面编程” 具有 面向对象编程”。

    对我来说,AOP是一种“元编程”。AOP所做的一切也可以在没有它的情况下通过添加更多代码来完成。AOP只需要编写这段代码。

    维基百科有这种元编程的最佳例子之一。假设你有一个包含许多“set…()”方法的图形类。在每种设置方法之后,图形的数据都会发生变化,因此图形也会发生变化。因此,需要在屏幕上更新图形。假设要重新绘制图形,必须调用“Display.update()”。经典的方法是通过添加 更多代码 .在您编写的每个集合方法的末尾

    void set...(...) {
        :
        :
        Display.update();
    }
    

    如果你有三套方法,那就没问题了。如果你有200个(假设的),那么在任何地方添加它都会变得非常痛苦。此外,每当您添加一个新的set方法时,您必须确保不要忘记将其添加到末尾,否则您只是创建了一个bug。

    AOP在不添加大量代码的情况下解决了这个问题,而是添加了一个方面:

    after() : set() {
       Display.update();
    }
    

    就是这样!您只需告诉系统,在达到set()切入点后,它必须运行此代码,并且它将运行此代码即可,而不是自己编写更新代码。无需更新200个方法,也无需确保您不会忘记在新的set方法上添加此代码。此外,你只需要一个切入点:

    pointcut set() : execution(* set*(*) ) && this(MyGraphicsClass) && within(com.company.*);
    

    这是什么意思?这意味着,如果一个方法名为“set*”(*表示set后面可能有任何名称),无论该方法返回什么(第一个星号)或接受什么参数(第三个星号) 它是MyGraphicsClass的一种方法 这个类是包“com.company.*”的一部分,那么这是一个set()切入点。我们的第一个代码说“ 之后 运行任何作为设定切入点的方法,运行以下代码“。

    看看AOP是如何优雅地解决这个问题的?实际上,这里描述的一切都可以在编译时完成。AOP预处理器可以在编译类本身之前修改您的源代码(例如,在每个集切入点方法的末尾添加Display.update())。

    然而,这个例子也显示了AOP的一个大缺点。AOP实际上正在做一些许多程序员认为“ Anti-Pattern “。确切的模式称为” Action at a distance ".

    远处的动作是 反模式(公认的常见模式 错误)一个部分的行为 一个程序的变化很大,取决于 难以或不可能识别 在另一部分的操作 程序。

    作为一个项目的新手,我可能只是阅读了任何set方法的代码,就认为它坏了,因为它似乎没有更新显示。我不 看见 只需查看set方法的代码,在执行后,其他一些代码就会“神奇地”执行以更新显示。我认为这是一个严重的不利因素!通过更改方法,可能会引入奇怪的错误。进一步理解代码的代码流,其中某些东西似乎正常工作,但并不明显(正如我所说,它们只是神奇地工作……不知何故),真的很难。

    更新

    为了澄清这一点:有些人可能会觉得我说AOP是不好的,不应该使用。我不是这个意思!AOP实际上是一个很好的特性。我只是说“小心使用”。只有当你把普通代码和AOP混为一谈时,AOP才会引起问题 方面 在上面的示例中,我们有更新图形对象的值并绘制更新对象的方面。这实际上是一个方面。将其中一半编码为普通代码,另一半编码为方面是增加问题的原因。

    如果将AOP用于完全不同的方面,例如用于日志记录,则不会遇到反模式问题。在这种情况下,项目的新手可能会想“所有这些日志消息从哪里来?我在代码中没有看到任何日志输出”,但这并不是一个大问题。他对程序逻辑的更改几乎不会破坏日志功能,对日志功能的更改也几乎不会破坏他的程序逻辑——这些方面是完全分开的。使用AOP进行日志记录的优点是,您的程序代码可以完全专注于做它应该做的任何事情,并且您仍然可以进行复杂的日志记录,而不会让您的代码到处被数百条日志消息弄乱。此外,当引入新代码时,神奇的日志消息将在正确的时间出现,并包含正确的内容。新手程序员可能不明白他们为什么在那里或从哪里来,但由于他们会在“正确的时间”记录“正确的事情”,他可以愉快地接受他们在那里的事实,并继续做其他事情。

    因此,在我的示例中,AOP的一个很好的用法是始终记录是否有任何值通过set方法更新。这不会产生反模式,也几乎不会成为任何问题的原因。

    有人可能会说,如果你可以很容易地滥用AOP来制造这么多问题,那么全部使用它是一个坏主意。然而,哪些技术不能被滥用?你可以滥用数据封装,也可以滥用继承。几乎所有有用的编程技术都可能被滥用。考虑一种编程语言,它非常有限,只包含不能被滥用的功能;一种语言,其中特征只能按照最初的意图使用。这样的语言将是如此有限,以至于它是否可以用于现实世界的编程也是有争议的。

        2
  •  26
  •   Norbert B. MJB    16 年前

    面向方面的编程提供了一种很好的方法来实现跨领域的关注点,如日志记录、安全性。 这些跨领域的问题是必须在许多地方应用的逻辑,但实际上与业务逻辑无关。

    您不应该将AOP视为OOP的替代品,而应该将其视为一个不错的附加组件 使您的代码更加干净、松散耦合,并专注于业务逻辑。

    因此,通过应用AOP,您将获得两个主要好处:

    1. 每个关注点的逻辑现在都在一个地方,而不是分散在代码库中。

    2. 类更干净,因为它们只包含主要关注点(或核心功能)的代码,次要关注点已转移到方面。

        3
  •  26
  •   nkr1pt    16 年前

    OOP和AOP并不相互排斥。 AOP可以很好地补充OOP。 AOP特别便于在方法中添加日志记录、性能跟踪等标准代码,而不会用这些标准代码堵塞方法代码。

        4
  •  10
  •   fhe    10 年前

    我认为这个问题没有通用的答案,但有一点需要注意,AOP没有 更换 OOP,但添加了某些分解功能,以解决所谓的 主导成分的暴政 ( 1 )(或横切关注点)。

    在某些情况下,只要您能够控制用于特定项目的工具和语言,它肯定会有所帮助,但也会增加方面交互的复杂性,以及对其他工具的需求,如 AJDT 仍然理解你的程序。

    Gregor Kiczales曾在Google Tech Talks上就AOP做过一次有趣的介绍性演讲,我建议观看: Aspect Oriented Programming: Radical Research in Modularity .

        5
  •  8
  •   Mendelt    16 年前

    首先,AOP不会取代OOP。AOP扩展了OOP。面向对象编程的思想和实践仍然具有相关性。拥有一个好的对象设计可能会使它更容易用方面进行扩展。

    我认为AOP带来的想法很重要。我们需要找到方法,在不改变类本身的情况下,对程序中的不同类实施跨领域关注。但我认为AOP最终将成为我们使用的其他工具的一部分,而不是一个单独的工具或技术。我们已经看到这种情况发生了。

    像Ruby和Python这样的动态语言具有像mixin这样的语言构造,可以解决相同的问题。这看起来很像AOP,但更好地集成在语言中。

    Spring和Castle以及其他几个依赖注入框架可以选择向它们注入的类添加行为。这是一种运行时编织的方法,我认为这有很大的潜力。

    我认为你不必学习一个全新的范式来使用AOP。这些想法很有趣,但正在慢慢被现有的工具和语言所吸收。请随时了解情况并尝试这些工具。

        6
  •  1
  •   Qing    11 年前

    空中接力 主要用于组织您的 业务逻辑 与…同时 面向方面的程序设计 有助于组织您的 非功能性事物 如审计、日志记录、事务管理、安全等。

    这样,您可以将业务逻辑与非功能逻辑解耦,从而使代码更清晰。

    另一个优点是,您可以非常一致地应用建议(如审计),而无需实现任何接口,这为修改提供了极大的灵活性,而不会触及业务逻辑。

    这样很容易实现 关注点分离 单一责任 .

    此外,当它只解决业务问题时,很容易将业务逻辑从一个框架(比如Spring)移植到其他地方(在未来)