装饰者模式的定义是 动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更加有弹性的替代方案。
在设计的时候,往往要给一个对象的功能进行一些修饰,对功能进行拓展和增强,以满足我们的需求。
比如我们现在有一个歌手大赛,所有的歌手都需要去唱歌。但是每一个歌手需要不一样的舞台特效,我们可提供的舞台特效有:伴舞,伴乐,背景大屏幕等。比如A歌手需要伴舞,B歌手需要伴奏,C歌手需要伴奏和伴舞,D歌手需要伴奏和背景大屏等等。有很多个歌手,有很多个需求。 我们一般性地算一下,如果有不同的装饰功能 N 个,那么,如果我们将这N个随机组合,将有:
种可能,如果不使用装饰者模式,我们需要对每一个歌手进行个性化定制。并且代码的重复度很高(这一点会在第三部分代码部分说明)。因此我们使用装饰者模式,这种模式灵活使用了继承与多态。具体内容将会在下面的部分以代码的形式展现。
下面我们用代码来演示上面的例子
首先所有的歌手都需要唱歌,因此我们需要一个歌手
public class Competitor {
public void sing() {
System.out.println("我唱唱唱......");
}
}
然后有一名歌手需要伴舞
class DanceCompetitor extends Competitor{
//dance方法
public void dance(){
System.out.println("打开微信跳一跳 ---- 我蹦,我蹦!");
}
// sing方法,对sing方法进行改造,使其既可以sing也可以dance
@Override
public void sing(){
super.sing();
dance();
}
}
又有一名歌手需要伴乐
class MusicCompetitor extends Competitor{
//dance方法
public void music(){
System.out.println("我拨片呢! 大哥要开始疯狂扫弦了 !! ---- ");
}
// sing方法,对sing方法进行改造,使其既可以sing也可以dance
@Override
public void sing(){
super.sing();
music();
}
}
现在又有一个歌手需要观众鼓掌
class ApplauseCompetitor extends Competitor{
//dance方法
public void applause(){
System.out.println("啪啪啪 !!啪啪啪!!啪!啊!!!啊 ---- 不要误会,鼓掌之后需要尖叫的");
}
// sing方法,对sing方法进行改造,使其既可以sing也可以dance
@Override
public void sing(){
super.sing();
applause();
}
}
是不是看起来很清晰?但是现在又有一个问题,如果一个歌手又需要伴舞又需要伴乐又需要观众鼓掌怎么办呢??
class AllCompetitor extends Competitor{
ApplauseCompetitor applauseCompetitor = new ApplauseCompetitor();
MusicCompetitor musicCompetitor = new MusicCompetitor();
DanceCompetitor danceCompetitor = new DanceCompetitor();
public void all(){
applauseCompetitor.applause();
musicCompetitor.music();
danceCompetitor.dance();
}
@Override
public void sing(){
super.sing();
all();
}
}
代码会有会多重复的调用,而且需求增加,我们就需要多创建一个类,冗余很大,侵入很强。那怎么办呢?使用装饰者模式就 可以解决这样的问题
鼓掌类:我们需要继承与基础的那个唱歌的方法,同时使用组合,在内部保有一个super class。同时提供一个有参的构造方法。
public class CompetitorApplause extends Competitor{
private Competitor competitor;
public CompetitorApplause(Competitor competitor){
this.competitor = competitor;
}
public void sing(){
applause();
competitor.sing();
}
public void applause(){
System.out.println("啪啪啪!!啪啪啪!!!");
}
}
同样的我们定义跳舞和伴乐
public class CompetitorDance extends Competitor{
private Competitor competitor;
public CompetitorDance(Competitor competitor){
this.competitor = competitor;
}
public void sing(){
dance();
competitor.sing();
}
public void dance(){
System.out.println("dancing");
}
}
public class CompetitorMusic extends Competitor{
private Competitor competitor;
public CompetitorMusic(Competitor competitor){
this.competitor = competitor;
}
public void sing(){
music();
competitor.sing();
}
public void music(){
System.out.println("music!!!!");
}
}
测试方法如下
public class decorationTest {
@Test
public void testA(){
Competitor competitor = new Competitor();
CompetitorMusic competitorMusic = new CompetitorMusic(competitor);
CompetitorDance competitorDance = new CompetitorDance(competitorMusic);
CompetitorApplause competitorApplause = new CompetitorApplause(competitorDance);
competitorApplause.sing();
}
@Test
public void testWithBusiness(){
Competitor competitor = new Competitor();
int a = 10;
//做业务判断
if( a == 10 ){ competitor = new CompetitorMusic(competitor); }
if( a > 35) { competitor = new CompetitorApplause(competitor); }
if( a < 50 ){ competitor = new CompetitorDance(competitor); }
competitor.sing();
}
}
Mybatis中,其二级缓存机制使用了装饰者模式。他根据配置文件中读取的不同信息,而动态的生成不一样类型的Cache。具体类图如下
一个Cache的父类,拥有很多子类,我们根据不同的业务,去装饰Cache的父类,得到不同的Cache方式。
具体对装饰者模式的使用的代码如下:
private Cache setStandardDecorators(Cache cache) {
try {
MetaObject metaCache = SystemMetaObject.forObject(cache);
if (size != null && metaCache.hasSetter("size")) {
metaCache.setValue("size", size);
}
if (clearInterval != null) {
cache = new ScheduledCache(cache);
((ScheduledCache) cache).setClearInterval(clearInterval);
}
if (readWrite) {
cache = new SerializedCache(cache);
}
cache = new LoggingCache(cache);
cache = new SynchronizedCache(cache);
if (blocking) {
cache = new BlockingCache(cache);
}
return cache;
} catch (Exception e) {
throw new CacheException("Error building standard cache decorators. Cause: " + e, e);
}
}
是不是很清晰明了呢?