博主声明:
转载请在开头附加本文链接及作者信息,并标记为转载。本文由博主 威威喵 原创,请多支持与指教。
本文首发于此 博主:威威喵 | 博客主页:https://blog.csdn.net/smile_running
装饰模式是设计模式中属于常用的一种之一,装饰模式也称作包装模式,顾名思义,我们可以想象为衣着装扮、包装盒之类的具体事物,那么它包装、装扮的具体内容就是变相拥有了这个东西,比如我们穿上服饰、还有给蛋糕添加包装盒、给礼物定制一个精致的小盒子,这些行为都是属于对某一个东西进行装饰,通过装饰之后,使得被装饰的东西拥有了曾经没有的属性、能力等。这就是装饰模式的一大功能体现。
装饰模式这被这样定义的:装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。
动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。因为除了继承的方式,让类的功能变得强大以外,装饰模式也可以使这个类变得强大,同时它并不是通过继承的方式,这就体现了它的灵活和可扩展性较高。
下面我们通过一个简单的小例子,来学习一下如何使用装饰模式来提高代码的灵活度。就以我们每天都穿衣打扮为例吧,运用装饰模式来写这个例子,通过代码的对比,才能真正的体现装饰模式的益处。
首先,我们的主角 coder 每天都去上班,996的工作充实了它的日常生活时间,与至于他没有太多的时间去关心衣着打扮,而且每天几乎对着代码,又不是对着小姐姐,在意什么呢。所以呢,每天起来看看头发,穿上衣服、裤子和鞋子就出门了,代码如下:
package com.xww.dp.decorator;
public class Coder {
public Coder(String coder) {
System.out.println(coder);
}
public void hair(String appearance) {
System.out.println("头发:"+appearance);
}
public void clothes(String appearance) {
System.out.println("衣服:"+appearance);
}
public void trousers(String appearance) {
System.out.println("裤子:"+appearance);
}
public void shoes(String appearance) {
System.out.println("鞋子:"+appearance);
}
}
客户端代码:
package com.xww.dp.decorator;
/**
* 装饰模式(包装模式)
*
* @author xww
* @博客:https://blog.csdn.net/smile_Running/column/info/27169
*/
public class DecoratorPatternClient {
public static void main(String[] args) {
Coder coder = new Coder("程序员_007");
coder.hair("乌黑稀疏");
coder.clothes("格子衫");
coder.trousers("裤衩");
coder.shoes("人字拖");
}
}
那么,他每天上班都是这样的:
很快的,996的生活到了星期日这一天。星期日呢,突然部门说搞娱乐活动,要打扮帅气一点来吸引小女生,那么当天早上,coder 就早早起床了,洗了个头发,穿上了 帅气的西装和皮鞋。
摇身一变,coder 也非常靓:
后来呢,他发现自己新买的手表还没有戴上,刚刚好,这次可以展现帅气的一面,怎么可以少了手表呢。对了,西装还少了领带,显得比较单调,于是又找出了一条领带。那代码怎么写呢,哦对了,写一个继承 coder 的帅气 boy 类吧,如下:
package com.xww.dp.decorator;
public class Boy extends Coder{
public Boy(String coder) {
super(coder);
}
public void watch(String appearance) {
System.out.println("手表:"+appearance);
}
public void tie(String appearance) {
System.out.println("领带:"+appearance);
}
}
客户端:
package com.xww.dp.decorator;
/**
* 装饰模式(包装模式)
*
* @author xww
* @博客:https://blog.csdn.net/smile_Running/column/info/27169
*/
public class DecoratorPatternClient {
public static void main(String[] args) {
Boy coder = new Boy("帅气的_程序员_007");
coder.hair("帅气发型");
coder.clothes("西装");
coder.trousers("西裤");
coder.shoes("皮鞋");
coder.watch("劳力士");
coder.tie("白色领结");
}
}
然后呢,他摇身一变的去了部门参加晚会了。
在晚会上,帅气的 coder 变得与往常不太一样,简直判若两人,获得小姐姐芳心,通过装饰可以彻底的改变一个人。同样的,通过装饰模式,也可以改变一个类。我们上面使用的就是一般的继承写法,虽然没有什么大的问题,但是使用装饰模式可以更加的灵活,不信我们试试看。
在此要新建一个 component 接口,表示 coder 的一系列行为,如整理发型,穿衣等。就是把 Coder 类中方法提到接口中,然后Coder 类实现这个接口,接口代码如下:
package com.xww.dp.decorator;
/**
* 装饰模式接口,定义基本的操作
*
* @author xww
*
* @博客:https://blog.csdn.net/smile_running?t=1
*
*/
public interface CoderComponent {
public void hair(String appearance);
public void clothes(String appearance);
public void trousers(String appearance);
public void shoes(String appearance);
}
然后,Coder 类必须实现此接口,里面的代码不需要更改,代码还是上面的那样,就不贴出来了。要修改的是 Boy 类,因为 Boy 类装饰了 Coder 类,所以它更牛皮、更帅气。Boy 类要拥有同样的操作,就必须实现此接口,代码如下:
package com.xww.dp.decorator;
public class Boy implements CoderComponent {
public CoderComponent coder;
public Boy() {
}
//进行装饰
public void decorator(CoderComponent coder) {
this.coder = coder;
}
public void watch(String appearance) {
System.out.println("手表:" + appearance);
}
public void tie(String appearance) {
System.out.println("领带:" + appearance);
}
@Override
public void hair(String appearance) {
if (coder != null)
coder.hair(appearance);
}
@Override
public void clothes(String appearance) {
if (coder != null)
coder.clothes(appearance);
}
@Override
public void trousers(String appearance) {
if (coder != null)
coder.clothes(appearance);
}
@Override
public void shoes(String appearance) {
if (coder != null)
coder.shoes(appearance);
}
}
实现接口,必须实现里面的抽象方法。这里有所不同,因为要对 Coder 类进行包装,所以要新建一个方法,把 CoderComponent 接口作为参数传进来,这样拿到接口的实现类,就可以调用接口里面的方法了。代码没什么难度,接下看看客户端的调用方式:
package com.xww.dp.decorator;
/**
* 装饰模式(包装模式)
*
* @author xww
* @博客:https://blog.csdn.net/smile_Running/column/info/27169
*/
public class DecoratorPatternClient {
public static void main(String[] args) {
Coder coder = new Coder("帅气的_程序员_007");
Boy boy = new Boy();
boy.decorator(coder);
boy.hair("帅气发型");
boy.clothes("西装");
boy.trousers("西裤");
boy.shoes("皮鞋");
boy.watch("劳力士");
boy.tie("白色领结");
}
}
看上面代码,coder 本来没有 watch 和 tie 方法的,通过 Boy 类的包装就变得更加强大,直接多了两个方法。这也就是装饰模式的运用,装饰模式对比继承的特点就是扩展性比较好,你可以想象一下这样的场景,如果我们的 boy 类功能越来越多的话,是不是就要继承 boy 类写一个 boy1、boy2,因为要根据一个原则,最好不要修改原有类代码的基础上进行添加新功能,而装饰模式就可以很方便的解决这个问题,只要实现一下通用的操作方法,然后进行扩展即可。
如何才能说明这个装饰模式的强大之处呢,莫不过看源代码更能说明其魅力了。在 Java 中,流的操作是我们最熟悉不过的了,整个流家族的设计,其实都用了这种装饰模式,我们看看调用流的代码就一目了然了。
try {
Reader reader = new FileReader("xx.txt");
BufferedReader bufferedReader = new BufferedReader(reader);
((BufferedReader) bufferedReader).readLine();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
BufferedReader 包装了 Reader 类,是其多了一个 readLine() 方法,而且可以调用 Reader 类的方法,这就是典型的装饰模式的使用,通过阅读源码,我们更能体会这种设计模式的好处。