每天一个(设计模式)-- Decorator模式

GOF的设计模式,讲的很细,我这里都很粗略。什么意图,别名,参与者,结构,协作,效果等等。。。
我这等小民,也没有那么高深的理论,拾人牙慧就满足了。
模式是死的,运用是活的。

今天讲讲装饰器模式。其实 每个模式的名字都很重要,因为这个名字基本就说明了这个模式用来干什么。当然,装饰器,不是用来装饰的,但是和装饰相关。

比如,我们设计一个窗子,完了,觉得太单调,于是重新加上边框,还觉得单调,那就贴点窗纸吧,如果还是不满意,那么可以糊个纱窗……

如果用程序来实现,我们可能想到这些对象,窗子,加边框的窗子,贴窗纸的窗子(好拗口),加边框贴窗纸的窗子…… 其实这种错误只会在初学者身上犯。

首先,我们的对象的粒度,应该合适,这样通过面向对象的重复利用,组合,就可以实现很多奇怪的东西。这里,我们有必要把窗,窗纸,边框,窗纱等看单独的对象,再细分,发现,其他都是作为装饰品,为窗服务的。于是,一个大概的参与者思路就清晰了。

然后,如何组织这些类呢?由于他们都与窗有关,那么,肯定又一个共同点,我们可以抽象成一个接口。然后,窗,与其他的东西又不一样,那么又可以分别对待。

最后,我们大概可以得出一个结构图了。这里使用标准的装饰器模式的UML:


[img]http://dl.iteye.com/upload/attachment/0065/6782/b8b42707-c9c2-3801-8f85-c8c66d4c4f12.gif[/img]

图中的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里的那么多类,会有一个新的认识。


[img]http://dl.iteye.com/upload/attachment/0065/6807/024b1666-1c30-3d01-b8bf-c9ce2aabb8e4.jpg[/img]
这个图好像是《Head First 设计模式》的吧。。。嘿嘿,我也没看这本书,但是风格很像。

到这里,我们对装饰器模式应该有个大概的认识了。这里,也可以看到,装饰器模式,其实很适合用于链式过滤,每一层都灵活控制。
这点很像OSI的七层协议,每一层,都添加一个头(做个装饰)。
我们也看看自己有什么设计上需要用到装饰器模式的吧,赶快动手试试效果。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值