在编写 OOP 代码时,我应用了一些优雅对象的实践。
其中之一是类应该是最终的。这意味着它们不能通过继承来扩展,只能通过组合来扩展。
优点是简单。我的意思是,通过这种方式,每个对象都被视为一个有凝聚力的块。其客户感兴趣的是其暴露的行为。而已。相反,通过扩展,客户可以打破它。
例如,一个对象可以将它的两个方法相互关联。因此,如果我们可以通过扩展替换其中一个,我们就可以破坏另一个。出于这个原因,可以肯定的是,我们应该检查它的实现。通过这种方式,我们增加了扩展和扩展之间的耦合。
换句话说,最终类强制我们应该只关心暴露的行为的想法。而不是实施。尽管如此,它需要改变我们对它们的推理方式。 Alias 模式简化了这种变化的一个方面。
别名模式允许扩展类可以构建其对象的方式,而无需子类化或修改它。
假设一个最终类使用一些强制参数创建其对象。我们如何添加另一种方法来创建它的对象?例如,我们如何添加一个构造函数,为它的一个或多个缺失参数使用默认值?
一种方法是向类添加另一个构造函数。但这可能会失控。此外,这不可能。例如,上述最终类可以在外部库中。
这种方法的另一个缺点是我们可以污染最终类。例如,我们可以有一个最终类,它在给定 JSON 的情况下构建其对象。但一段时间后,我们还需要添加 XML。正如您可以想象的那样,添加将 XML 映射到 JSON 的代码将不可避免地污染该类。
但是,别名模式不限于最终类。例如,我们不能有两个具有相同参数但语义不同的构造函数。
为了解决这个问题,我们可以在类代码中添加静态工厂方法。但是同样的上述缺点影响了这种方法。那就是:这失控了;这不可能总是可行的;这会污染课堂。
解决这两个问题的更好方法是创建另一个具有所需构造函数行为的类。这个类封装了自己的构造逻辑。它会将所有内容委托给其他类,包括实际的创建。这是别名模式。
在以下情况下使用别名模式:
您需要添加或修改最终类的构造函数;
您想添加或修改类的构造函数而不修改或子类化它;
您需要两个或多个具有相同参数的类的构造函数,而无需对其进行修改或子类化。
结构简单。我们至少需要两个实现相同接口的类: Alias
和Aliased.
AnInterface
声明一个接口。
Aliased
实现AnInterface
;
公开一个或多个构造函数。
Alias
AnInterface
;Aliased
对象的引用;Aliased
对象。Alias
根据自己的规则构建Aliased
对象并维护它的引用。然后它将所有内容委托给别名。
别名模式具有以下后果:
要实现别名模式,您需要:
定义接口;
用一个类来实现之前定义的接口。这将是别名;
要使用别名类实现先前定义的接口,您需要:
下面的 Java-ish 代码表达了别名模式。在此代码中,别名为其他强制性参数注入了默认值:
interface AnInterface { void aMethod(); Something anotherMethod(); } final class Aliased implements AnInterface { private final A a; private final B b; Aliased(final A a, final B b) { this.a = a; this.b = b; } void aMethod() { // implementation } Something anotherMethod() { // implementation } } final class Alias implements AnInterface { private final Aliased aliased; Alias(final A a) { this( new Aliased( a, new InstanceOfB(...) ) ); } private Alias(final Aliased aliased) { this.aliased = aliased; } void aMethod() { this.aliased.aMethod(); } Something anotherMethod() { return this.aliased.anotherMethod(); } }
在一定程度上,别名模式也可以看作是装饰对象构造的一种方式。如果我们将类视为负责创建对象的对象,则这种愿景尤其正确。