装饰者模式
定义
在不改变原有对象的基础上,将功能附加到对象上,提供了比继承更有弹性的替代方案。
使用场景
扩展一个类的功能或给一个类添加附加职责,动态地给一个对象添加功能,这些功能可以再动态撤销。
优点
装饰类和被装饰类可以独立发展,不会互相耦合。装饰者模式是继承的一个替代模式
通过使用不同的装饰类以及装饰类的排列组合,可以实现不同的效果。
符合开闭原则(OCP):对扩展开放,对修改关闭。 符合多用组合,少用继承原则
缺点
多层装饰代码较复杂
组成
组件(Component):每个组件可以单独使用,也可以被装饰者包起来使用。
具体组件(ConcreteComponent):动态地加上新行为的对象,扩展自Component。
装饰者(Decorator):每个装饰者都会包装一个组件,即每个装饰者有一个实例变量保存某个Component的引用。
tips
装饰者也要实现被装饰对象实现的接口,这样才能使用多态的方法面向接口编程。在被装饰之后依旧返回一个接口引用,使其他类感知不到我们装饰者的存在。
关键代码
1.Component类充当抽象角色,不应该具体实现。
2.修饰类引用和继承Component类,具体扩展类重写父类方法。
类图
使用装饰者模式的源码
具体实现
星巴克咖啡订单项目( 咖啡馆):
1) 咖啡种类/单品咖啡:Espresso(意大利浓咖啡 )、ShortBlack 、LongBlack( 美式咖啡 )、Decaf( 无因咖啡 )
2) 调料: Milk、Soy(豆浆)、Chocolate
3) 要求在扩展新的咖啡种类时,具有良好的扩展性、改动方便,维护方便
4)使用 OO 来计算不同种类咖啡的费用 : 客户可以点单品咖啡,也可以单品咖啡+调料组合 。
1 package Decorator; 2 3 /** 4 * @title: Drink 5 * @Author yzhengy 6 * @Date: 2020/7/28 23:36 7 * @Question: 饮料抽象类 8 */ 9 public abstract class Drink { 10 11 public String des; 12 private float price = 0.0f; 13 14 public String getDes() { 15 return des; 16 } 17 18 public void setDes(String des) { 19 this.des = des; 20 } 21 22 public float getPrice() { 23 return price; 24 } 25 26 public void setPrice(float price) { 27 this.price = price; 28 } 29 30 //计算费用的抽象方法,需要子类实现 31 public abstract float cost(); 32 }
1 package Decorator; 2 3 /** 4 * @title: Coffee 5 * @Author yzhengy 6 * @Date: 2020/7/28 23:40 7 * @Question: 缓冲层,由于ConcreteComponent类很多,将共有部分提取出来 8 */ 9 public class Coffee extends Drink { 10 11 @Override 12 public float cost() { 13 return super.getPrice(); 14 } 15 }
1 //具体的饮料 2 public class Espresso extends Coffee { 3 4 public Espresso(){ 5 setDes("意大利咖啡"); 6 setPrice(6.0f); 7 } 8 9 } 10 11 public class LongBlack extends Coffee { 12 13 public LongBlack() { 14 setDes("美式咖啡"); 15 setPrice(5.0f); 16 } 17 } 18 19 public class ShortBlack extends Coffee { 20 21 public ShortBlack() { 22 setDes("单品咖啡"); 23 setPrice(4.0f); 24 } 25 }
1 package Decorator; 2 3 /** 4 * @title: Decorator 5 * @Author yzhengy 6 * @Date: 2020/7/28 23:45 7 * @Question: 装饰者对象 8 */ 9 public class Decorator extends Drink { 10 11 private Drink obj; 12 13 public Decorator(Drink obj) {//组合关系 14 this.obj = obj; 15 } 16 17 @Override 18 public float cost() { 19 return super.getPrice() + obj.cost();//小料自己的价格+单杯咖啡的价格 20 } 21 22 //obj.getDes():输出被装饰者的信息 23 @Override 24 public String getDes() { 25 return super.des + " "+super.getPrice() + "&&" + obj.getDes(); 26 } 27 }
1 //调料类 2 package Decorator; 3 4 /** 5 * @title: Chocolate 6 * @Author yzhengy 7 * @Date: 2020/7/29 10:15 8 * @Question: 具体的decorator ,此处指每一种小料 9 */ 10 public class Chocolate extends Decorator { 11 12 public Chocolate(Drink obj) { 13 super(obj);//通过子类的构造方法去调用父类的构造方法. 14 setDes("巧克力"); 15 setPrice(3.0f); 16 } 17 } 18 19 public class Milk extends Decorator { 20 21 public Milk(Drink obj) { 22 super(obj); 23 setDes("牛奶"); 24 setPrice(2.0f); 25 } 26 } 27 28 public class Soy extends Decorator { 29 30 public Soy(Drink obj) { 31 super(obj); 32 setDes("豆浆"); 33 setPrice(1.5f); 34 } 35 }
1 package Decorator; 2 3 /** 4 * @title: CoffeeBar 5 * @Author yzhengy 6 * @Date: 2020/7/29 10:22 7 * @Question: 装饰者模式下的订单:两份巧克力+一分牛奶+LongBlack 8 */ 9 public class CoffeeBar { 10 11 public static void main(String[] args) { 12 //1.点一份LongBlack 13 Drink order = new LongBlack(); 14 System.out.println("费用=" + order.cost()); 15 System.out.println("描述=" + order.getDes()); 16 17 //2.order加入一份牛奶 18 order = new Milk(order); 19 System.out.println("加入一份牛奶 费用=" + order.cost()); 20 System.out.println("加入一份牛奶 描述=" + order.getDes()); 21 22 //3.order加入一份巧克力 23 order = new Chocolate(order); 24 System.out.println("加入一份牛奶和一份巧克力 费用=" + order.cost()); 25 System.out.println("加入一份牛奶和一份巧克力 描述=" + order.getDes()); 26 27 //4.order再加入一份巧克力 28 order = new Chocolate(order); 29 System.out.println("加入一份牛奶和一份巧克力 费用=" + order.cost()); 30 System.out.println("加入一份牛奶和一份巧克力 描述=" + order.getDes()); 31 32 //新的订单 33 Drink order2 = new Newcoffee(); 34 System.out.println("无因咖啡 费用=" + order2.cost()); 35 System.out.println("无因咖啡 描述=" + order2.getDes()); 36 37 } 38 39 }
如果需要新增加一种咖啡,则只需要增加该咖啡品种即可。
1 package Decorator; 2 3 /** 4 * @title: Newcoffee 5 * @Author yzhengy 6 * @Date: 2020/7/29 11:19 7 * @Question: 如果新增了一个咖啡品种 8 */ 9 public class Newcoffee extends Coffee { 10 11 public Newcoffee() { 12 setDes("无因咖啡"); 13 setPrice(3.5f); 14 } 15 }