【问题标题】:What is the difference between a Decorator, Attribute, Aspect, and Trait?装饰器、属性、方面和特征之间有什么区别?
【发布时间】:2017-03-03 01:28:36
【问题描述】:

从纯计算机科学(或者可能是计算语言学)的角度来看,我想知道这两个词之间的区别:

  • 装饰器
  • 属性
  • 方面
  • 特性

各种语言以不同的方式利用这些词和功能。例如,在 Python 中,装饰器 [根据 Python Wiki](强调我的):

装饰器动态改变函数、方法或类的功能,而无需直接使用子类或更改被装饰函数的源代码。

这让我觉得它与 PostSharp 或 DynamicProxy 等面向方面的编程工具非常相似。即:

   [Profile]
   private static void SleepSync()
   {
       Thread.Sleep(200);
   }

来源:PostSharp Examples

在 C# 和 Java(以及无数其他语言)中,属性可以表示装饰器模式 (C#) 或字段 (Java)。

在 C++ 中通过 boost 或 PhP 通过内置 trait 词,我们可以使用 trait 来扩展类,如下所示:https://en.wikipedia.org/wiki/Trait_(computer_programming)

那么,从“纯粹”的角度来看,所有这些实际上是什么的规范定义是什么?有没有更好的方法来定义它们?

【问题讨论】:

  • 语言之间没有标准。另外,我认为并非所有语言都支持每种语言的严格定义。以 Java 为例。 Decorator = Annotation,Attribute = Field,Trait = Default method(可能是接口)...另一方面,Aspect 更像是一个软件架构概念,而不是任何一种语言的特性。
  • 按理说,这些关联的“事物”中的每一个都有一个范式级别的“术语”。我很难从理论的角度相信你无法准确地量化它们。
  • 您真正需要在编程语言之间切换多久才能需要讨论概念而不是语言的实际术语? Trait 和 Aspect 的维基百科页面似乎足以让我从高层次上理解。 (参考:en.m.wikipedia.org/wiki/Aspect_(computer_programming))但是,如果你说的是 Python,我说的是 C#,我们都说装饰器,那么我们的理解可能不同。
  • 我不确定这有什么关系。问题是关于理论和计算机科学的。但答案很常见。我在不同的技术上处理了很多不同的问题。
  • @MichaelScheper 注释不会直接修改行为或添加新行为,而装饰器则可以。注释可能会间接改变行为,但注释基本上只是元数据,是关于类的额外信息。

标签: computer-science decorator theory traits aspect


【解决方案1】:

装饰器

我从设计模式的角度来考虑装饰器。许多语言都认可设计模式,尤其是面向对象的语言。因此,作为模式的装饰器是一个包装器,它添加了被装饰的函数或类中不存在的功能。

我能想到的最简单的例子是装饰器函数。功能

int foo(int x)

可以被另一个接受第二个参数的函数修饰,用它做其他事情,然后依次调用 foo(),传递原始参数 x。

int bar(int x, int y) {
    return y*y + foo(x);
}

虽然设计模式通常应用于类级别,但这里的原理是相同的,我认为它很好地说明了装饰器的含义。每种特定语言是否遵守这一点是另一回事。一种语言可能有其他东西,它称之为“装饰器”——但对我来说,这个概念最符合用附加功能装饰简单的东西的想法,而不改变原始代码甚至使用继承。

另一个常见的例子是 Java 中的 I/O 类。有基本的

OutputStream s

然后您可以根据正在处理的数据类型或您希望读取数据的格式使用更专业的类来装饰它:

OutputStream s1 = new FileOutputStream("somefile.txt");

OutputStream s2 = new ByteOutputStream("rawdata.hex");

属性

我倾向于正确理解属性的 C# 理念,因为它与装饰器不同。属性分配的语义值可能因一种用途而异,甚至在使用相同属性的 API 之间也可能不同。例如,我可能有两个函数:

[Logging] private void a() { ... }
[Security] private void b() { ... }

我可以分配一个日志属性和一个安全属性,这些属性的含义可能与检查这些属性的客户端 API 不同。一个可能使用 log4j 来实现日志记录,另一个可能使用另一个 API。这里的定义更加流畅,并且可以由我的代码的不同方或用户进行解释。当然可以使用属性作为装饰器,但属性的用途远不止于此。

为了消除歧义,属性一词也用于表示类的成员变量。在这里,我们讨论的是更大、更抽象的概念,即为现有对象或类分配预定义的语义值。 Java 调用这些注解。

在我看来,它是属性的一个限定词,在我们正在谈论的意义上,它不会直接修改行为,只会间接修改。例如,将 [Logging] 属性分配给某物不会以任何方式更改其代码。这就像附加一个其他人正在寻找的名称标签。当另一个程序或应用程序看到名称标签时,它会推断出某些事情并可能相应地改变其行为。但是(至少在 Java 中)注解或属性不会直接修改任何东西——同样,只是一个名称标签。这在 C# 或其他支持属性的语言中可能略有不同,在这种情况下,我会认为它们是更高级的属性或完全不同的东西。

方面

在面向方面编程 (AOP) 意义上的方面是一种自我修改或自我更新的代码结构。它将一段代码定义为更具延展性(切入点),并允许在同一段代码的一个或多个可能的更新、补丁或不同版本之间交换该特定部分。

你能用方面做一些与装饰器和属性相同的事情吗?当然。但是你为什么要让自己那么头疼呢? AOP 就像 OOP 的下一步,只是在必要时才应该使用它。什么时候需要?当一个特定的应用程序有很多“横切关注点”,比如安全性或日志记录时——例如,银行应用程序。这些问题是交叉的;它们超越了传统的界限,这些界限使得定义良好的类和包。当您记录时,除非您记录所有内容,否则它不会有什么好处;因此,这种担忧是跨领域的。所以当你去更新一个类的日志机制时,很难同时修改所有其他类和 API,但它也是必要的。否则,您的日志记录现在不一致且令人困惑,并且更难以用于故障排除或监控。

为了让这些更新不那么令人头疼,我们引入了像 AspectJ 这样的面向方面的语言。我没有遇到过除此之外的任何“方面”,但可能有一些如前所述的关于装饰器的内容。一种特定的语言可能将某事物称为一个方面,但它可能看起来更像我们已经讨论过的其他事物之一。

特质

Trait 是界面的同义词,或者至少在我研究过的语言中是这样的。

接口是一个 OOP 概念,它声明行为而不实现它们。创建这些预期行为允许这些行为变得通用,并且可以在不关心细节的情况下被通用调用。由子类来实现它们。

经典的 OOP 示例是 Animal,它有两个子类 Cat 和 Dog。

public interface/trait Animal {
    public void speak();
}
public class Cat implements Animal {
    public void speak() {
        output("Meow.");
    }
}
public class Dog implements Animal {
    public void speak() {
         output("Bark!");
    }
}

这也是多态性的一个很好的例子——这些词往往会让非计算机人员畏缩。只是表示猫和狗有个体行为,如果我声明一个Animal对象,我不在乎你给我什么样的。你可以给我一只猫或一只狗。因为两者都是动物,在任何一种情况下,我所要做的就是调用我的 Animal 对象的 speak() 函数,我可以放心,在任何一种情况下都会出现正确的结果。每个单独的子类都知道它需要做什么。在 C++ 中,这里的水有点浑浊,但一般概念是相同的(如果你想从那个兔子洞开始,请阅读关键字“虚拟”)。

总结

我希望这能消除一些困惑。正如您所说,似乎许多不同的语言对每种语言都有许多不同的含义,这无疑会造成混乱。我相信,如果您进一步研究它,您会发现,总的来说,这些都是标准含义。我使用多种语言(VB、C++、C#、Java、Paschal、PHP、Perl)编程已有 18 年多的时间,这些定义是我最愿意相信的一种标准。

我当然欢迎进一步讨论我所说的话。

【讨论】:

  • 恕我直言,特征远远超出了界面。接口只声明可以找到哪些方法但不提供实现。特征还包括定义。所以我认为一个特征更像是某种 partial class,它可以是 partial to multiple classes
  • @derM 正如我在回答中所说,不同的语言有不同的含义。我同意特征可以远远​​超出仅仅是一个接口。我从来没有说过他们不能比这更重要。当你提炼出特征总是意味着什么而不是它们有时意味着什么时,你最终会发现界面是共同点。我同意那里存在歧义,但这就是讨论的本质。随意发布您自己的答案,以更好地解释特征。
  • 请注意,.NET 属性不与 对象(实例)相关联,而是与类型系统实体(例如类型、字段、属性、方法和参数)相关联。我倾向于将属性视为无能为力的标签或标签,它们可以为编译器、运行时或使用反射的自定义代码提供强大的功能。
  • @RobertSchmidt 感谢您的澄清。我将进行编辑以更准确地指出这一点。
猜你喜欢
  • 2017-04-21
  • 2012-01-09
  • 2020-02-21
  • 2023-03-21
  • 2016-09-16
  • 2011-06-03
  • 1970-01-01
  • 2016-01-16
  • 2011-02-04
相关资源
最近更新 更多