一、什么是装饰者模式
装饰者模式的定义是 动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更加有弹性的替代方案。
在设计的时候,往往要给一个对象的功能进行一些修饰,对功能进行拓展和增强,以满足我们的需求。
二、装饰者模式的作用
比如我们现在有一个歌手大赛,所有的歌手都需要去唱歌。但是每一个歌手需要不一样的舞台特效,我们可提供的舞台特效有:伴舞,伴乐,背景大屏幕等。比如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!!!!");
}
}
看上去没有什么太大的不同?事实上,装饰者模式灵活的使用了继承和多态,来使我们的系统很好的解耦。我们内部保有一个父类的对象以后,子类的构造方法需要提供一个父类。而我们构造子类的时候可以提供一个其他的子类给这个子类,这个子类在调用本身保有的那个父类的sing()时,其实是调用了入参的子类的sing();因此我们可以根据不同的业务,不同的条件,让系统去组成不一样的定制化的歌手,这样就尽量少的对系统进行改动,也很大的减少了代码的冗余。
测试方法如下
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二级缓存中装饰者模式的使用
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);
}
}
是不是很清晰明了呢?