概述
快餐店的例子:快餐店有炒面,炒饭这些快餐,还可以附加鸡蛋,火腿,培根这些配菜,配菜需要额外加钱,每种配菜的价格不太一样,计算总价就比较麻烦
使用继承方式存在的问题:
- 扩展性不好:如果需要再加一个配料 (火腿肠),我们就会发现需要给FriedRice和FriedNoodles 类分别定义一个子类,如果要新增一个快餐类 (炒河粉) 的话,就需要定义更多的子类
- 产生过多的子类
装饰者模式的定义:
在不改变现有对象结构的情况下,动态的给该对象添加一些职责 (即增加其额外功能) 的模式,即增强对象
结构
装饰 (Decorate) 模式中的角色:
- 抽象构建角色:定义一个抽象接口以规范准备接受附加责任的对象
- 具体构建角色:实现抽象构建,通过装饰角色为其添加一些职责
- 抽象装饰角色:继承或实现抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能
- 具体装饰角色:实现抽象装饰的相关方法,并给具体构件对象添加附加的责任
举例
我们使用装饰者模式对快餐店案例进行改进,体会装饰者模式的精髓
类图如下:
FastFood 接口:定义 getPrice(),getDesc()
package com.atguigu.decorate;
public interface FastFood {
//接口中定义的是全局常量 ~~
public abstract float getPrice();
public abstract String getDesc();
}
FriedNoodles 类:FastFood的一种
package com.atguigu.decorate;
public class FriedNoodles implements FastFood{
String desc = "炒面条";
float price = 12;
public FriedNoodles() {
}
@Override
public float getPrice() {
return this.price;
}
@Override
public String getDesc() {
return this.desc;
}
}
FriedRice 类:FastFood的一种
package com.atguigu.decorate;
public class FriedRice implements FastFood{
String desc = "炒米饭";
float price = 10;
public FriedRice() {
}
@Override
public float getPrice() {
return this.price;
}
@Override
public String getDesc() {
return this.desc;
}
}
Garnish 类:实现和聚合 FastFood接口,相当于FastFood的一种
package com.atguigu.decorate;
public abstract class Garnish implements FastFood{
protected FastFood fastFood;
}
Egg 类:继承 Garnish
package com.atguigu.decorate;
public class Egg extends Garnish{
String GarnishDesc = "加鸡蛋";
float GarnishPrice = 2;
public Egg(FastFood fastFood){
this.fastFood = fastFood;
}
@Override
public float getPrice() {
return fastFood.getPrice() + GarnishPrice;
}
@Override
public String getDesc() {
return fastFood.getDesc() + GarnishDesc;
}
}
Bacon 类:继承 Garnish
package com.atguigu.decorate;
public class Bacon extends Garnish{
String GarnishDesc = "加培根";
float GarnishPrice = 3;
public Bacon(FastFood fastFood){
this.fastFood = fastFood;
}
@Override
public float getPrice() {
return fastFood.getPrice() + GarnishPrice;
}
@Override
public String getDesc() {
return fastFood.getDesc() + GarnishDesc;
}
}
Client 类:测试类
package com.atguigu.decorate;
public class Client {
public static void main(String[] args) {
//点一份炒米饭
FastFood food = new FriedRice();
System.out.println(food.getDesc() + " " + food.getPrice() + "元");
//加一个鸡蛋
//多态 : Egg 对象的实例 赋给 FastFood 接口的实例
food = new Egg(food);
System.out.println(food.getDesc() + " " + food.getPrice() + "元");
//加一个培根
//多态 : Bacon对象的实例 赋给 FastFood 接口的实例
food = new Bacon(food);
System.out.println(food.getDesc() + " " + food.getPrice() + "元");
}
}
好处:
- 装饰着模式可以带来比继承更加灵活型的扩展功能,使用更加方便,可以通过组合不同的装饰者对象来获取具有不同行为状态的多样化的结果。装饰者模式比继承更具良好的扩展性,完美遵循开闭原则,继承是静态的附加责任,装饰者是动态的附加责任
- 装饰类和被装饰类可以独立发展,不会相互融合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能
使用场景
当不能采用继承的方式对系统进行扩充或者采用继承不利于系统扩展维护时
不能采用继承的情况主要有两类:
- 第一种是系统中存在大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈现爆炸性增长
- 第二类是因为类定义不能继承 (如 final 类)
再不影响其他对象的情况下,以动态,透明的方式给单个对象添加职责
当对象的功能要求可以动态的添加,可以动态撤销