装饰者模式
装饰器模式是一种用于代替继承的技术,无需通过继承增加子类就能扩展对象的新功能。使用对象的关联关系代替继承关系,更加灵活,同时避免类型体系的快速膨胀。
怎么说?老样子,先设想个场景
假如我要去楼下买手抓饼吃,一般来说,手抓饼都会提供不同的配料,比如加鸡蛋,加鸡柳,加肉松或者加火腿,不同的配料价钱也不同
是不是感觉饿啦哈哈哈哈,我们继续...
如果我们要用编程语言来描述的话,我们首先想到的就是先定义一个基类,然后每增加一种搭配就写个子类
定义一个手抓饼基类,啥都没加的那种
public class HandPanCake {
String name;
public HandPanCake(String name) {
this.name = name;
}
public void eatSense(){
System.out.println("啥都没加,难吃的一匹");
}
如果我们要增加配料,比如要加个蛋的话,我们继承手抓饼类进行拓展
class HandPanCakeWithEgg extends HandPanCake{
public HandPanCakeWithEgg(String name) {
super(name);
}
//增加配料
public void addEgg(){
System.out.println("加了鸡蛋,美滋滋");
}
public void eatSense(){
super.eatSense();
addEgg();
}
}
同样,我们也可以类似分别写出增加 鸡柳、肉松、火腿等的搭配。到目前为止的话,都没什么问题
但是,如果某天我捡到钱了,于是买手抓饼的时候同时打算增加鸡蛋和火腿这两样的组合(就是又加鸡蛋又加火腿啦),那么现在要怎么操作呢?是不是同样用继承的方式呢? 如果我又打算加鸡蛋和肉松,是不是又要写个子类呢?再换一种包含多个配料的组合的搭配,则又要再写个子类,这样的话,如果我们的搭配非常多,则需要建立非常多的子类,显然这不是我们想要看到的结果,所以我们就提出了新的思路,而这种思路,就是我们要讲的装饰者模式
再小小的总结下,装饰者模式解决的是组合搭配搭配问题,当独立搭配(单独加鸡柳、单独加鸡蛋,单独加..)数量增加时,其组合搭配数量是爆炸式增长的...
主要角色
- Component抽象构件角色。真实对象和装饰对象有相同的接口。这样,客户端对象就能够以与真实对象相同的方式同装饰对象交互。
- ConcreteComponent 具体构件角色(真实对象):
- Decorator装饰角色:持有一个抽象构件的引用。装饰对象接受所有客户端的请求,并把这些请求转发给真实的对象。这样,就能在真实对象调用前后增加新的功能。
- ConcreteDecorator具体装饰角色:负责给构件对象增加新的责任。
实例讲解
首先定义一个抽象的手抓饼接口,里面定义了吃的感觉这一方法
public interface IhandPanCake {
void eatSense();
}
接着是实现手抓饼接口,构建真实对象(是一个啥都没加的裸饼)
//真实角色,实现抽象接口
public class HandPanCake implements IhandPanCake{
public void eatSense(){
System.out.println("啥都没加,难吃的一匹");
}
}
再也就是装饰者角色,同样也是实现手抓饼接口,该角色持有一个抽象构建的引用,负责把请求转发给真实角色
public class HandPanCakeDecorator implements IhandPanCake{
protected IhandPanCake handPanCake;
public HandPanCakeDecorator(IhandPanCake handPanCake) {
super();
this.handPanCake = handPanCake;
}
@Override
public void eatSense() {
handPanCake.eatSense();
}
}
接下来就是定义具体的装饰角色了,分别负责把鸡蛋、火腿这些增加到具体的手抓饼上
加蛋
public class HandPanCakeWithEgg extends HandPanCakeDecorator{
public HandPanCakeWithEgg(IhandPanCake handPanCake) {
super(handPanCake);
}
public void addEgg(){
System.out.println("加了鸡蛋,美滋滋");
}
public void eatSense() {
super.eatSense();
addEgg();
}
}
加鸡柳
public class HandPanCakeWithChicken extends HandPanCakeDecorator{
public HandPanCakeWithChicken(IhandPanCake handPanCake) {
super(handPanCake);
}
public void addChicken(){
System.out.println("加了鸡柳,吃了流口水");
}
public void eatSense() {
super.eatSense();
addChicken();
}
}
加火腿
public class HandPanCakeWithHam extends HandPanCakeDecorator{
public HandPanCakeWithHam(IhandPanCake handPanCake) {
super(handPanCake);
}
public void addHam(){
System.out.println("加了火腿,有点爽");
}
public void eatSense() {
super.eatSense();
addHam();
}
}
客户端测试
采用装饰者模式的话,当我们要增加多种组合的搭配时,就不需要增加新的子类了,而是
HandPanCake handPanCake = new HandPanCake();
handPanCake.eatSense();
System.out.println("老板,加个鸡柳和蛋..................");
HandPanCakeWithChicken handPanCakeWithChickenAndEgg = new HandPanCakeWithChicken(handPanCakeWithEgg);
handPanCakeWithChickenAndEgg.eatSense();
当然也可以用set方法来组装装饰类
优点
- 扩展对象功能,比继承灵活,不会导致类个数急剧增加
- 可以对一个对象进行多次装饰,创造出不同行为的组合,得到功能更加强大的对象具体构建类和具体装饰类可以独立变化
- 用户可以根据需要自己增加新的具体构件子类和具体装饰子。
缺点
- 产生很多小对象。大量小对象占据内存,一定程度上影响性能。
- 装饰模式易于出错,调试排查比较麻烦。
实际开发中的应用
- IO中输入流和输出流的设计(最典型)
- Servlet API 中提供了一个request对象的Decorator设计模式的默认实现类HttpServletRequestWrapper,HttpServletRequestWrapper 类,增强了request对象的功能。
桥接模式与装饰模式
这两个模式似乎有点像?两个模式都是为了解决子类过多问题, 但他们的诱因不同:
1.桥接模式对象自身有 沿着多个维度变化的趋势 , 本身不稳定;
2.装饰者模式对象自身非常稳定, 只是为了增加新功能/增强原功能。