GOF的设计模式,讲的很细,我这里都很粗略。什么意图,别名,参与者,结构,协作,效果等等。。。
我这等小民,也没有那么高深的理论,拾人牙慧就满足了。
模式是死的,运用是活的。
今天讲讲装饰器模式。其实 每个模式的名字都很重要,因为这个名字基本就说明了这个模式用来干什么。当然,装饰器,不是用来装饰的,但是和装饰相关。
比如,我们设计一个窗子,完了,觉得太单调,于是重新加上边框,还觉得单调,那就贴点窗纸吧,如果还是不满意,那么可以糊个纱窗……
如果用程序来实现,我们可能想到这些对象,窗子,加边框的窗子,贴窗纸的窗子(好拗口),加边框贴窗纸的窗子…… 其实这种错误只会在初学者身上犯。
首先,我们的对象的粒度,应该合适,这样通过面向对象的重复利用,组合,就可以实现很多奇怪的东西。这里,我们有必要把窗,窗纸,边框,窗纱等看单独的对象,再细分,发现,其他都是作为装饰品,为窗服务的。于是,一个大概的参与者思路就清晰了。
然后,如何组织这些类呢?由于他们都与窗有关,那么,肯定又一个共同点,我们可以抽象成一个接口。然后,窗,与其他的东西又不一样,那么又可以分别对待。
最后,我们大概可以得出一个结构图了。这里使用标准的装饰器模式的UML:
图中的Component,应该就是要装饰的对象的抽象,注意,饰品也是可以被装饰的,但是必须有一实际的被装饰对象(下面会说明为什么)。
Decorator接口把装饰器隔离出来,表明这些是装饰器。
每个装饰器需要一个可装饰的 被装饰对象,这就是ConcreteComponent了。为了强制要求装饰器必须装饰一个可装饰对象,我们在构造函数里做了要求。
下面就来看一个小例子。本例子完全只是为了说明模式的结构运用,实际的情况只能如有雷同,实属巧合。
一个接口,这是可被装饰对象的统一接口,表明他们能做什么。
public interface VisualComponent {
public void draw();
}
这是一个可视组件。他的方法就是画出自己。也可以使用抽象类。
某些组件可能有共同的方法。这里为了简化,直接说关键。
public class Pane implements VisualComponent{
@Override
public void draw() {
// TODO Auto-generated method stub
System.out.println("drawing pane...");
}
}
这是一块面板,我们省略了绘制过程,并打印出一串字符,表示正在绘制此组件。
接下来,我们就要给这个可视化组件做装饰了。当然,不仅仅是Pane可以被装饰,只要是实现了VisualComponent接口的类都可以被装饰。
首先要有一个装饰器的接口。
public abstract class ComponentDecorator implements VisualComponent{
protected VisualComponent visualComponent ;
public ComponentDecorator(VisualComponent vc){
this.visualComponent = vc;
}
}
这里使用了抽象类,如果使用接口也可以,直接继承 VisualComponent就行。
之所以使用抽象类,是为了确保子类使用父类的构造函数来完成装饰器应有的职责,那就是我们只是装饰者,应该提供一个被装饰者给我。
接下来,是具体的装饰器了,我们定义两个,这样可以看到使用情况。
首先是一个边框装饰器。
public class BorderDecorator extends ComponentDecorator{
public BorderDecorator(VisualComponent vc){
super(vc);
}
@Override
public void draw() {
// TODO Auto-generated method stub
visualComponent.draw();
System.out.println("draw bording...");
}
}
可以看到,他所装饰的类,是通过继承父类构造函数来实现的。
在绘制时,我们先绘制父类,然后绘制自己(代码中省略)。
然后再来一个头装饰器,也就是绘制一个顶栏的装饰器。
public class HeaderDecorator extends ComponentDecorator{
public HeaderDecorator(VisualComponent vc){
super(vc);
}
@Override
public void draw() {
// TODO Auto-generated method stub
visualComponent.draw();
System.out.println("drawing header...");
}
}
好了,我们的装饰器好了,现在来看看如何使用。
装饰器模式,通过不同的修饰顺序,我们可以得到不同的结果。这里如何组装,
是使用者决定的。
public class TestMain {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
VisualComponent vc = new Pane();
VisualComponent newVC = new HeaderDecorator(new BorderDecorator(vc));
newVC.draw();
}
}
看着是不是眼熟,这个其实和java中的I/O很眼熟;
InputStream ins = new BufferedInputStream(new FileInputStream("xx.txt"));
没错,java.io 下的类就是使用了装饰器模式(当然还有其他的比如Adapter)
从网上找到一张图,很能说明问题,相信大家看完以后,对 java.io里的那么多类,会有一个新的认识。
这个图好像是《Head First 设计模式》的吧。。。嘿嘿,我也没看这本书,但是风格很像。
到这里,我们对装饰器模式应该有个大概的认识了。这里,也可以看到,装饰器模式,其实很适合用于链式过滤,每一层都灵活控制。
这点很像OSI的七层协议,每一层,都添加一个头(做个装饰)。
我们也看看自己有什么设计上需要用到装饰器模式的吧,赶快动手试试效果。