装饰器
在IT技术中,我认为装饰器是指设计模式中的一种。这种模式被广泛地应用于许多语言中,特别是面向对象的语言。因此,装饰器作为一种模式,是一个包装器,可以为被装饰的函数或类添加新的功能。
我能想到的最简单的例子是一个装饰器函数。该函数会接收另一个函数作为参数,并返回一个新的函数,这个新的函数可以对原来的函数进行一些额外的操作。
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#中的“attribute”是正确的理解,因为它与装饰器不同。属性赋予一个语义值,这个值可以在不同的用途之间甚至在使用相同属性的API之间发生变化。例如,我可能有两个函数:
[Logging] private void a() { ... }
[Security] private void b() { ... }
我可以为一个分配一个日志属性,为另一个分配安全属性,而这些属性的含义对检查这些属性的客户端 API 可能有所不同。可能会使用 log4j 来实现日志记录,也可能会使用其他 API。这里的定义更加流动和开放,可以由我的代码的不同参与方或用户进行解释。某个属性肯定可以用作装饰器,但是属性可用途远不止于此。
仅作澄清,属性一词也用于表示类的成员变量。在这里,我们谈论的是将预定义语义值分配给现有对象或类的更大、更抽象的概念。Java 将其称为注释。
我心目中对它成为属性(就我们所谈论的意义而言)的一个限定因素是它不直接修改行为,只是间接地影响。例如,将 [Logging] 属性分配给某个东西并不会以任何方式改变它的代码。这就像附上一个名牌,其他人正在寻找它。当另一个程序或应用程序看到名牌时,它会推断出某些事情,并可能相应地更改其行为。但是(至少在 Java 中),注释或属性不会直接修改任何内容——只是一个名牌。在支持属性的 C# 或其他语言中,情况可能略有不同,在这种情况下,我会认为它们是更高级的属性或完全不同的东西。
在面向方面编程(AOP)的意义上,Aspect 是一种自修改或自更新的代码构造。它将某个代码部分定义为更具可塑性(切入点),并允许该特定部分在一个或多个可能的更新、补丁或相同代码部分的不同版本之间进行交换。
使用 Aspect 可以像装饰器和属性一样执行一些操作吗?当然可以。但是为什么要给自己制造麻烦呢?AOP 就像从 OOP 迈向的下一步,只有在必要时才应使用它。何时才是必要的呢?当一个特定的应用程序有很多“跨领域问题”(cross-cutting concerns)时,例如安全性或日志记录——比如银行应用程序。这些关注点贯穿始终;它们超越了使良好定义的类和包变得美观的传统边界。当您在记录日志时,除非记录所有内容,否则不会有太大的好处;因此,这个关注点是跨越领域的。因此,当您要更新一个类的日志记录机制时,同时修改所有其他类和 API 会很困难,但也是必要的。否则,您的记录日志现在就不一致和混乱,更难以用于故障排除或监视。
为了使这些更新变得不那么麻烦,引入了面向方面的语言,如 AspectJ。我还没有遇到过除装饰器之外任何其他含义的“方面”(aspect),但是可能会有一些,正如前面关于装饰器所述的那样。某种语言可能将某些内容称为“Aspect”,但它可能看起来更像我们已经讨论过的其他东西之一。
Trait 与接口同义,至少在我学习的语言中是这样。接口是一个 OOP 概念,它声明行为而不实现它们。创建这些期望的行为允许这些行为变得通用,并
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年,这些定义是我最舒适的信仰之一。
我当然欢迎进一步讨论我所说的。