装饰模式
-
装饰模式 Decorator Pattern
- 装饰模式是属于GoF23中设计模式中的一种,又叫装饰者模式,属于结构型模式的一种。
- 装饰模式的描述为:动态的给一个对象添加一些额外的功能,前提是不改变原文件和使用继承来进行对象功能的扩展。
- 简单的来说,就是将对象进行加一层包裹的意思,比如我们所熟悉的Java IO流是一种通过继承抽象类扩展功能的装饰着模式的使用。
使用到装饰模式的情况,关键之处就是在于装饰两字,翻译过来就是需要为一个类或者对象添加额外的功能,而且这个功能可能是出于特定的情况之下才会需要的,所以通过装饰模式把每个要装饰的功能放在单独的类中,并让这个类装饰需要这个功能的被装饰对象,于是在处于特定情况之下要执行这个特殊的功能的时候,客户端只需要有选择地,按照需要的顺序使用装饰功能包装对象即可。
装饰模式的组成
- 抽象构件(Component)角色:给出一个抽象接口,规范接受附加责任的对象;
- 具体构建(Concrete Component)角色:定义一个将要接受附加责任的类;
- 装饰(Decorator)角色:持有并维护一个构件对象的实例并实现一个与抽象构件一致的接口;
- 具体装饰(Concrete Decorator)角色:负责给构件对象添加上附加的责任。
装饰模式的特点
- 具体装饰对象和具体构件对象有相同的接口;
- 装饰类对象包含并维护一个具体构件对象的引用;
- 装饰类对象接受客户端的附加请求并增加一些实现的附加功能。
通过一个案例理解
可以使用到装饰模式的案例在日常生活中会有很多,比如我们自己每天的日常搭配,穿什么类型的上衣,下装和鞋子,上班期间有着自己的小西装加上小皮鞋,休假期间一套休闲装加休闲鞋,晚会或者酒会更是需要盛装出席不仅仅需要穿衣还需要搭配不少小饰品比如手表或者项链等等,衣服和饰品即是对"人"这个具体对象的装饰,这也是日常生活中可以使用到装饰模式的小例子,这一次的案例以另一个生活常见的例子为例。
情景:不管什么时候我们都是需要喝水的,类似于星巴克这种高级咖啡店或者去街边的小奶茶店,点上那么一杯奶茶,但是仅仅是一杯奶茶最后得到只能是原味奶茶,于是我们需要给奶茶前加各种修饰例如珍珠奶茶,椰果奶茶,蓝莓奶茶等等,这不就是对"奶茶"进行了装饰吗?
于是就可以开始编写这个小例子啦!
- 首先明确各自的关系:
抽象构件:奶茶店
具体构件:奶茶
装饰:装饰类
具体装饰:以冰,椰果,糖为例子 - 绘制UML类图明确各自的关系:
- 准备抽象构件
/**
* 装饰模式案例 抽象构件角色 奶茶店接口定义
* @author WQ
*/
public interface MilkTeaShop {
void show();
}
- 准备具体构件
/**
* 装饰模式 具体构件角色 奶茶类定义
* @author WQ
*/
public class MilkyTea implements MilkTeaShop{
@Override
public void show() {
System.out.println("奶茶一杯!");
}
}
- 准备装饰类
/**
* 装饰模式 装饰角色 装饰类定义 注意该类需要维护和持有一个抽象构件的引用
* @author WQ
*/
public class Decorator implements MilkTeaShop{
protected MilkTeaShop shop = null;
//开始构造 也就是装饰
public void Decorate(MilkTeaShop shop){
this.shop = shop;
}
@Override
public void show() {
if (shop != null){
shop.show();
}
}
}
- 准备各个具体的装饰类
/**
* 装饰模式 具体装饰角色 冰块类定义
* @author WQ
*/
public class Ice extends Decorator{
@Override
public void show() {
System.out.print("加冰的,");
shop.show();
}
}
-------------------------------------------------------------------------------
/**
* 装饰模式 具体装饰角色 椰果类定义
* @author WQ
*/
public class Coconuts extends Decorator{
@Override
public void show() {
System.out.print("加椰果的,");
shop.show();
}
}
-------------------------------------------------------------------------------
/**
* 装饰模式 具体装饰角色 椰果类定义
* @author WQ
*/
public class Suger extends Decorator{
@Override
public void show() {
System.out.print("加糖的,");
shop.show();
}
}
- 准备测试类
/**
* 装饰模式案例测试类
* @author WQ
*/
public class Test {
public static void main(String[] args){
MilkTeaShop shop = new MilkyTea();
Ice ice = new Ice();
Coconuts coconuts = new Coconuts();
Suger suger = new Suger();
System.out.println("没有配置:");
shop.show();
System.out.println("第一种配置:");
ice.Decorate(shop);
coconuts.Decorate(ice);
coconuts.show();
System.out.println("第二种配置:");
ice.Decorate(shop);
suger.Decorate(ice);
suger.show();
System.out.println("第三种配置:");
suger.Decorate(shop);
coconuts.Decorate(suger);
ice.Decorate(coconuts);
ice.show();
}
}
- 测试结果展示
装饰模式的案例完成!!装饰模式的介绍也结束啦!
但是这个案例中的奶茶店的抽象接口其实可以直接变成奶茶类,而使装饰类继承奶茶类即可!
同时根据实际情况是可以进行对装饰模式进行简化的:
- 如果只有一个具体构件(Concrete Component)类而没有抽象的抽象构件(Component)接口时,可以让装饰(Decorator)类继承具体构件(Concrete Component)类。
- 如果只有一个具体装饰(Concrete Decorator)类时,可以将装饰(Decorator)类和具体装饰(ConcreteDecorator)类合并。