装饰器
装饰指在某物件上装点额外饰品的行为,以使其原本朴素的外表变得更加饱满、华丽,而装饰器(装饰者)就是能够化“腐朽”为神奇的利器。装饰器模式(Decorator)能够在运行时动态地为原始对象增加一些额外的功能,使其变得更加强大。从某种程度上讲,装饰器非常类似于“继承”,它们都是为了增强原始对象的功能,区别在于方式的不同,后者是在编译时(compile-time)静态地通过对原始类的继承完成,而前者则是在程序运行时(run-time)通过对原始对象动态地“包装”完成,是对类实例(对象)“装饰”的结果
从素面朝天到花容月貌
室内装修对房屋视觉效果的改善立竿见影,人们化妆也是如此,“人靠衣装马靠鞍”,人们总是惊叹女生们魔法师一般的化妆技巧,可以从素面朝天变成花容月貌),化妆前后简直判若两人,这正是装饰器的粉饰效果在发挥作用
当然,化妆的过程也许对软件研发人员来说比较陌生,但我们可以从设计模式的角度出发,对这项充满神秘色彩的工作进行拆解和分析。下面开始我们的代码实战,首先对于任何妆容展示者必然对应一个标准的展示行为show(),我们将它抽象出来定义为接口Showable
package decorator;
public interface Showable {
void show(); //标准展示行为
}
Showable这个标准行为需要人去实现,女生们绝对当仁不让,下面来定义女生类
package decorator;
public class Girl implements Showable {
@Override
public void show() {
System.out.println("女生的素颜");
}
}
化妆品对于女生的妆容效果起着至关重要的作用,我们就称之为“装饰器”吧
package decorator;
public class Decorator implements Showable {
Showable showable;
public Decorator(Showable showable) {
this.showable = showable;
}
@Override
public void show() {
System.out.println("粉饰开始【");
showable.show();
System.out.println("]");
}
}
化妆品装饰器类与女生类一样也实现了标准行为展示接口Showable,这说明它同样能够进行展示,只是方式可能比较独特。构造方法中,化妆品装饰器类在构造自己的时候可以把其他可展示者注入进来并赋给在第3行定义的引用。如此一来,化妆品装饰器类中包含的这个可展示者就成为一个“被装饰者”的角色了。注意展示方法show(),化妆品装饰器类不但调用了“被装饰者”的展示方法,而且在其前后加入了自己的“粉饰效果”,这就像加了一层“壳”一样,包裹了被装饰对象。最后,我们来看客户端类的运行结果
package decorator;
public class Client {
public static void main(String[] args) {
new Decorator(new Girl()).show();
//运行结果:粉饰【女生的素颜】
}
}
化妆品的多样化
至此,我们已经完成了基本的装饰工作,可是装饰器中只有一个简单的“粉饰”效果,这未免过于单调,我们是否忘记了“口红”的效果?除此之外,可能还会有“眼线”“睫毛膏”“腮红”等各种各样的化妆品
如何让我们的装饰器具备以上所有装饰功效呢?有些读者可能会想到,把这些装饰操作统统加入化妆品装饰器类中,一次搞定所有化妆操作。这样的做法必然是错误的,试想,难道每位女生都习惯于如此浓妆艳抹吗?化妆品的多样性决定了装饰器应该是多态化的,单个装饰器应该只负责自己的化妆功效,例如口红只用于涂口红,眼线笔只用于画眼线,把化妆品按功能分类才能让用户更加灵活地自由搭配,用哪个或不用哪个由用户自己决定,而不是把所有功能都固化在同一个装饰器里。
诚然,Showable接口是能够满足多态化需求的,但它只是对行为接口的一种规范,极度的抽象并不具备对代码继承的功能,所以化妆品的多态化还需要接口与抽象类的搭配使用才能两全其美。装饰器类的抽象化势在必行,我们来看如何重构它
package decorator;
public abstract class Decorator implements Showable {
Showable showable;
public Decorator(Showable showable) {
this.showable = showable;
}
@Override
public void show() {
showable.show();
}
}
们将化妆品装饰器类修改为装饰器抽象类,这主要是为了不允许用户直接实例化此类。接着我们重构了展示方法show(),其中只是调用了被装饰者的show()方法,而不再做任何装饰操作,至于具体如何装饰则属于其子类的某个化妆品类的操作范畴了
package decorator;
public class FoundationMakeup extends Decorator {
public FoundationMakeup(Showable showable) {
super(showable);
}
@Override
public void show() {
System.out.println("打粉底【");
super.show();
System.out.println("】");
}
}
粉底类不用去实现Showable接口了,而是继承了装饰器抽象类,如此父类中对被装饰者的定义得以继承,可以看到我们在第4行的构造方法中调用了父类的构造方法并注入被装饰者,这便是继承的优势所在。当然,这个粉底类的show()方法一定要加上自己特有的操作,我们在调用被装饰者的show()方法前后都进行了打粉底操作。化妆尚未结束,打完粉底再涂个口红吧
package decorator;
public class Lipstick extends Decorator {
public Lipstick(Showable showable) {
super(showable);
}
@Override
public void show() {
System.out.println("涂口红【");
super.show();
System.out.println("】");
}
}
与粉底类同出一辙,口红类只是进行了自己特有的“涂口红”操作。最后,客户端可以依次把被装饰者“女生”、装饰器“粉底”、装饰器“口红”用构造方法层层包裹起来,再进行展示即可完成整体化妆工作
package decorator;
public class Client {
public static void main(String[] args) {
Showable madeupGirl = new Lipstick(new FoundationMakeup(new Girl()));
madeupGirl.show();
}
}
至此,装饰器模式重构完毕,化妆品多态化得以顺利实现。如果用户对这些淡妆效果不够满意,我们还可以接着添加其他化妆品类,以便用户自由搭配出自己的理想效果,使“清新淡妆”或“浓妆艳抹”均成为可能。
自由嵌套
Java类库中对装饰器模式的应用当然要比我们的例程复杂得多,但基本思想其实是一致的。装饰器模式最终的目的就在于“装饰”对象,其中装饰器抽象类扮演着至关重要的角色,它实现了组件的通用接口,并且使自身抽象化以迫使子类继承,使装饰器固定特性的延续与多态化成为可能。我们来看装饰器模式的类结构,如图所示。装饰器模式的各角色定义如下
Component(组件接口):所有被装饰组件及装饰器对应的接口标准,指定进行装饰的行为方法。对应本章例程中的展示接口Showable。
ConcreteComponent(组件实现):需要被装饰的组件,实现组件接口标准,只具备自身未被装饰的原始特性。对应本章例程中的女生类Girl。
Decorator(装饰器):装饰器的高层抽象类,同样实现组件接口标准,且包含一个被装饰的组件。
ConcreteDecorator(装饰器实现):继承自装饰器抽象类的具体子类装饰器,可以有多种实现,在被装饰组件对象的基础上为其添加新的特性。对应本章例程中的粉底类FoundationMakeup、口红类Lipstick。
Go版本代码
package decorator
import "fmt"
type Showable interface {
Show()
}
//Decorator
type Decorator struct {
Showable
}
func (d *Decorator) Show() {
d.Showable.Show()
}
func (d *Decorator) SetShowable(s Showable) {
d.Showable = s
}
//FoundationMakeup
type FoundationMakeup struct {
Decorator
}
func NewFoundationMakeup(s Showable) Showable {
f := &FoundationMakeup{}
f.SetShowable(s)
return f
}
func (f *FoundationMakeup) Show() {
fmt.Print("打粉底【")
f.Decorator.Show()
fmt.Print("】")
}
//Lipstick
type Lipstick struct {
Decorator
}
func NewLipstick(s Showable) Showable {
l := &Lipstick{}
l.SetShowable(s)
return l
}
func (l *Lipstick) Show() {
fmt.Print("涂口红【")
l.Decorator.Show()
fmt.Print("]")
}
//Girl
type Girl struct {
}
func (g *Girl) Show() {
fmt.Print("女生的素颜")
}
package main
import "desginPatterns/decorator"
func main() {
madeupGirl := decorator.NewLipstick(decorator.NewFoundationMakeup(&decorator.Girl{}))
madeupGirl.Show()
}
补充:动态代理
go语言目前来说是不支持动态代理的